diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 4c121af3..89e3798d 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -268,6 +268,7 @@ enum lws_token_indexes {
WSI_TOKEN_HTTP_DATE,
WSI_TOKEN_HTTP_RANGE,
WSI_TOKEN_HTTP_REFERER,
+ WSI_TOKEN_HTTP_URI_ARGS,
WSI_TOKEN_MUXURL,
diff --git a/lib/parsers.c b/lib/parsers.c
index c5142519..fb43fca9 100644
--- a/lib/parsers.c
+++ b/lib/parsers.c
@@ -334,6 +334,9 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c)
else /* last we issued was / so SEEN_SLASH */
wsi->u.hdr.ups = URIPS_SEEN_SLASH;
break;
+ case URIPS_ARGUMENTS:
+ /* leave them alone */
+ break;
}
check_eol:
@@ -346,6 +349,28 @@ check_eol:
lwsl_parser("*\n");
}
+ if (c == '?') { /* start of URI arguments */
+ /* seal off uri header */
+ wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = '\0';
+
+ /* move to using WSI_TOKEN_HTTP_URI_ARGS */
+ wsi->u.hdr.ah->next_frag_index++;
+ wsi->u.hdr.ah->frags[
+ wsi->u.hdr.ah->next_frag_index].offset =
+ wsi->u.hdr.ah->pos;
+ wsi->u.hdr.ah->frags[
+ wsi->u.hdr.ah->next_frag_index].len = 0;
+ wsi->u.hdr.ah->frags[
+ wsi->u.hdr.ah->next_frag_index].next_frag_index = 0;
+
+ wsi->u.hdr.ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] =
+ wsi->u.hdr.ah->next_frag_index;
+
+ /* defeat normal uri path processing */
+ wsi->u.hdr.ups = URIPS_ARGUMENTS;
+ goto swallow;
+ }
+
spill:
if (issue_char(wsi, c) < 0)
return -1;
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 5d2b4704..740066df 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -304,6 +304,7 @@ enum uri_path_states {
URIPS_SEEN_SLASH,
URIPS_SEEN_SLASH_DOT,
URIPS_SEEN_SLASH_DOT_DOT,
+ URIPS_ARGUMENTS,
};
enum uri_esc_states {
diff --git a/test-server/attack.sh b/test-server/attack.sh
index e6f10be1..0186f486 100755
--- a/test-server/attack.sh
+++ b/test-server/attack.sh
@@ -16,6 +16,29 @@ function check {
exit 1
fi
dd if=$LOG bs=1 skip=$LEN 2>/dev/null
+
+ if [ "$1" = "default" ] ; then
+ diff /tmp/lwscap /usr/share/libwebsockets-test-server/test.html > /dev/null
+ if [ $? -ne 0 ] ; then
+ echo "FAIL: got something other than test.html back"
+ exit 1
+ fi
+ fi
+
+ if [ "$1" = "forbidden" ] ; then
+ if [ -z "`grep '
403 Forbidden
' /tmp/lwscap`" ] ; then
+ echo "FAIL: should have told forbidden (test server has no dirs)"
+ exit 1
+ fi
+ fi
+
+ if [ "$1" == "args" ] ; then
+ a="`dd if=$LOG bs=1 skip=$LEN 2>/dev/null |grep Uri.Args\: | tr -s ' ' | cut -d' ' -f4-`"
+ if [ "$a" != "$2" ] ; then
+ echo "Args '$a' not $2"
+ exit 1
+ fi
+ fi
LEN=`stat $LOG -c %s`
}
@@ -30,6 +53,19 @@ while [ -z "`grep Listening $LOG`" ] ; do
done
check
+echo
+echo "---- ? processing (%2f%2e%2e%2f%2e./test.html?arg=1)"
+rm -f /tmp/lwscap
+echo -e "GET %2f%2e%2e%2f%2e./test.html?arg=1 HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+check args "arg=1"
+
+echo
+echo "---- ? processing (%2f%2e%2e%2f%2e./test.html?arg=/../.)"
+rm -f /tmp/lwscap
+echo -e "GET %2f%2e%2e%2f%2e./test.html?arg=/../. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
+check args "arg=/../."
+
+
echo
echo "---- spam enough crap to not be GET"
echo "not GET" | nc $SERVER $PORT
@@ -124,99 +160,58 @@ echo -e "GET /test.html HTTP/1.1\x0d\x0a\x0d\x0aILLEGAL-PAYLOAD.................
"......................................................................................................................." \
"......................................................................................................................." \
| nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
-check
-diff /tmp/lwscap /usr/share/libwebsockets-test-server/test.html > /dev/null
-if [ $? -ne 0 ] ; then
- echo "FAIL: got something other than test.html back"
- exit 1
-fi
+check default
echo
echo "---- directory attack 1 (/../../../../etc/passwd should be /etc/passswd)"
rm -f /tmp/lwscap
echo -e "GET /../../../../etc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
-check
-if [ -z "`grep '403 Forbidden
' /tmp/lwscap`" ] ; then
- echo "FAIL: should have told forbidden (test server has no dirs)"
- exit 1
-fi
+check forbidden
echo
echo "---- directory attack 2 (/../ should be /)"
rm -f /tmp/lwscap
echo -e "GET /../ HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
-check
-diff /tmp/lwscap /usr/share/libwebsockets-test-server/test.html > /dev/null
-if [ $? -ne 0 ] ; then
- echo "FAIL: got something other than test.html back"
- exit 1
-fi
+check default
echo
echo "---- directory attack 3 (/./ should be /)"
rm -f /tmp/lwscap
echo -e "GET /./ HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
-check
-diff /tmp/lwscap /usr/share/libwebsockets-test-server/test.html > /dev/null
-if [ $? -ne 0 ] ; then
- echo "FAIL: got something other than test.html back"
- exit 1
-fi
+check default
echo
echo "---- directory attack 4 (/blah/.. should be /)"
rm -f /tmp/lwscap
echo -e "GET /blah/.. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
-check
-diff /tmp/lwscap /usr/share/libwebsockets-test-server/test.html > /dev/null
-if [ $? -ne 0 ] ; then
- echo "FAIL: got something other than test.html back"
- exit 1
-fi
+check default
echo
echo "---- directory attack 5 (/blah/../ should be /)"
rm -f /tmp/lwscap
echo -e "GET /blah/../ HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
-check
-diff /tmp/lwscap /usr/share/libwebsockets-test-server/test.html > /dev/null
-if [ $? -ne 0 ] ; then
- echo "FAIL: got something other than test.html back"
- exit 1
-fi
+check default
echo
echo "---- directory attack 6 (/blah/../. should be /)"
rm -f /tmp/lwscap
echo -e "GET /blah/../. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
-check
-diff /tmp/lwscap /usr/share/libwebsockets-test-server/test.html > /dev/null
-if [ $? -ne 0 ] ; then
- echo "FAIL: got something other than test.html back"
- exit 1
-fi
+check default
echo
echo "---- directory attack 7 (/%2e%2e%2f../../../etc/passwd should be /etc/passswd)"
rm -f /tmp/lwscap
echo -e "GET /%2e%2e%2f../../../etc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
-check
-if [ -z "`grep '403 Forbidden
' /tmp/lwscap`" ] ; then
- echo "FAIL: should have told forbidden (test server has no dirs)"
- exit 1
-fi
+check forbidden
echo
echo "---- directory attack 7 (%2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd should be /etc/passswd)"
rm -f /tmp/lwscap
echo -e "GET %2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
-check
-if [ -z "`grep '403 Forbidden
' /tmp/lwscap`" ] ; then
- echo "FAIL: should have told forbidden (test server has no dirs)"
- exit 1
-fi
+check forbidden
+
echo
-echo "--- survived"
+echo "--- survived OK ---"
kill -2 $CPID
diff --git a/test-server/test-server.c b/test-server/test-server.c
index 899314c8..d9382cc1 100644
--- a/test-server/test-server.c
+++ b/test-server/test-server.c
@@ -103,6 +103,69 @@ struct per_session_data__http {
int fd;
};
+/*
+ * this is just an example of parsing handshake headers, you don't need this
+ * in your code unless you will filter allowing connections by the header
+ * content
+ */
+
+static void
+dump_handshake_info(struct libwebsocket *wsi)
+{
+ int n;
+ static const char *token_names[] = {
+ /*[WSI_TOKEN_GET_URI] =*/ "GET URI",
+ /*[WSI_TOKEN_HOST] =*/ "Host",
+ /*[WSI_TOKEN_CONNECTION] =*/ "Connection",
+ /*[WSI_TOKEN_KEY1] =*/ "key 1",
+ /*[WSI_TOKEN_KEY2] =*/ "key 2",
+ /*[WSI_TOKEN_PROTOCOL] =*/ "Protocol",
+ /*[WSI_TOKEN_UPGRADE] =*/ "Upgrade",
+ /*[WSI_TOKEN_ORIGIN] =*/ "Origin",
+ /*[WSI_TOKEN_DRAFT] =*/ "Draft",
+ /*[WSI_TOKEN_CHALLENGE] =*/ "Challenge",
+
+ /* new for 04 */
+ /*[WSI_TOKEN_KEY] =*/ "Key",
+ /*[WSI_TOKEN_VERSION] =*/ "Version",
+ /*[WSI_TOKEN_SWORIGIN] =*/ "Sworigin",
+
+ /* new for 05 */
+ /*[WSI_TOKEN_EXTENSIONS] =*/ "Extensions",
+
+ /* client receives these */
+ /*[WSI_TOKEN_ACCEPT] =*/ "Accept",
+ /*[WSI_TOKEN_NONCE] =*/ "Nonce",
+ /*[WSI_TOKEN_HTTP] =*/ "Http",
+
+ "Accept:",
+ "If-Modified-Since:",
+ "Accept-Encoding:",
+ "Accept-Language:",
+ "Pragma:",
+ "Cache-Control:",
+ "Authorization:",
+ "Cookie:",
+ "Content-Type:",
+ "Date:",
+ "Range:",
+ "Referer:",
+ "Uri-Args:",
+
+ /*[WSI_TOKEN_MUXURL] =*/ "MuxURL",
+ };
+ char buf[256];
+
+ for (n = 0; n < sizeof(token_names) / sizeof(token_names[0]); n++) {
+ if (!lws_hdr_total_length(wsi, n))
+ continue;
+
+ lws_hdr_copy(wsi, buf, sizeof buf, n);
+
+ fprintf(stderr, " %s = %s\n", token_names[n], buf);
+ }
+}
+
const char * get_mimetype(const char *file)
{
int n = strlen(file);
@@ -152,6 +215,8 @@ static int callback_http(struct libwebsocket_context *context,
switch (reason) {
case LWS_CALLBACK_HTTP:
+ dump_handshake_info(wsi);
+
if (len < 1) {
libwebsockets_return_http_status(context, wsi,
HTTP_STATUS_BAD_REQUEST, NULL);
@@ -371,67 +436,6 @@ bail:
return 0;
}
-/*
- * this is just an example of parsing handshake headers, you don't need this
- * in your code unless you will filter allowing connections by the header
- * content
- */
-
-static void
-dump_handshake_info(struct libwebsocket *wsi)
-{
- int n;
- static const char *token_names[] = {
- /*[WSI_TOKEN_GET_URI] =*/ "GET URI",
- /*[WSI_TOKEN_HOST] =*/ "Host",
- /*[WSI_TOKEN_CONNECTION] =*/ "Connection",
- /*[WSI_TOKEN_KEY1] =*/ "key 1",
- /*[WSI_TOKEN_KEY2] =*/ "key 2",
- /*[WSI_TOKEN_PROTOCOL] =*/ "Protocol",
- /*[WSI_TOKEN_UPGRADE] =*/ "Upgrade",
- /*[WSI_TOKEN_ORIGIN] =*/ "Origin",
- /*[WSI_TOKEN_DRAFT] =*/ "Draft",
- /*[WSI_TOKEN_CHALLENGE] =*/ "Challenge",
-
- /* new for 04 */
- /*[WSI_TOKEN_KEY] =*/ "Key",
- /*[WSI_TOKEN_VERSION] =*/ "Version",
- /*[WSI_TOKEN_SWORIGIN] =*/ "Sworigin",
-
- /* new for 05 */
- /*[WSI_TOKEN_EXTENSIONS] =*/ "Extensions",
-
- /* client receives these */
- /*[WSI_TOKEN_ACCEPT] =*/ "Accept",
- /*[WSI_TOKEN_NONCE] =*/ "Nonce",
- /*[WSI_TOKEN_HTTP] =*/ "Http",
-
- "Accept:",
- "If-Modified-Since:",
- "Accept-Encoding:",
- "Accept-Language:",
- "Pragma:",
- "Cache-Control:",
- "Authorization:",
- "Cookie:",
- "Content-Type:",
- "Date:",
- "Range:",
- "Referer:"
-
- /*[WSI_TOKEN_MUXURL] =*/ "MuxURL",
- };
- char buf[256];
-
- for (n = 0; n < sizeof(token_names) / sizeof(token_names[0]); n++) {
- if (!lws_hdr_total_length(wsi, n))
- continue;
-
- lws_hdr_copy(wsi, buf, sizeof buf, n);
-
- fprintf(stderr, " %s = %s\n", token_names[n], buf);
- }
-}
/* dumb_increment protocol */