diff options
author | Andy Green <andy.green@linaro.org> | 2013-11-10 15:15:21 +0800 |
---|---|---|
committer | Andy Green <andy.green@linaro.org> | 2013-11-10 15:15:21 +0800 |
commit | b1a9e508cd1626f1b1fa411711d9ddb64a86d8e3 (patch) | |
tree | ccb0f98868b8d4b048c07daa717bc2853d02c23d /lib | |
parent | 19895bcfd4e89f96100ab2dca041e965973f8e2b (diff) | |
download | libwebsockets-b1a9e508cd1626f1b1fa411711d9ddb64a86d8e3.tar.gz |
translate and protect uri test sever use uri path
This translates %xx in the GET uri and removes /.. and /... type sequences along with
translating // or /// etc to /.
Since the result is hopefully secure, it also changes the test server to actually use
the uri path pasted on a resource directory without whitelisting.
Signed-off-by: Andy Green <andy.green@linaro.org>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/parsers.c | 135 | ||||
-rw-r--r-- | lib/private-libwebsockets.h | 15 |
2 files changed, 140 insertions, 10 deletions
diff --git a/lib/parsers.c b/lib/parsers.c index ba10899a..f6d71206 100644 --- a/lib/parsers.c +++ b/lib/parsers.c @@ -144,6 +144,33 @@ int lws_hdr_simple_create(struct libwebsocket *wsi, return 0; } +static char char_to_hex(const char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + + return -1; +} + +static int issue_char(struct libwebsocket *wsi, unsigned char c) +{ + if (wsi->u.hdr.ah->pos == sizeof(wsi->u.hdr.ah->data)) { + lwsl_warn("excessive header content\n"); + return -1; + } + wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = c; + if (c) + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len++; + + return 0; +} + int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) { int n; @@ -188,12 +215,105 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) wsi->u.hdr.parser_state]].len && c == ' ') break; - /* special case space terminator for get-uri */ - if (wsi->u.hdr.parser_state == WSI_TOKEN_GET_URI && c == ' ') { + if (wsi->u.hdr.parser_state != WSI_TOKEN_GET_URI) + goto check_eol; + + /* special URI processing... end at space */ + + if (c == ' ') { + /* enforce starting with / */ + if (!wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len) + if (issue_char(wsi, '/') < 0) + return -1; c = '\0'; wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING; + goto spill; } + /* special URI processing... convert %xx */ + + switch (wsi->u.hdr.ues) { + case URIES_IDLE: + if (c == '%') { + wsi->u.hdr.ues = URIES_SEEN_PERCENT; + goto swallow; + } + break; + case URIES_SEEN_PERCENT: + if (char_to_hex(c) < 0) { + /* regurgitate */ + if (issue_char(wsi, '%') < 0) + return -1; + wsi->u.hdr.ues = URIES_IDLE; + /* continue on to assess c */ + break; + } + wsi->u.hdr.esc_stash = c; + wsi->u.hdr.ues = URIES_SEEN_PERCENT_H1; + break; + + case URIES_SEEN_PERCENT_H1: + if (char_to_hex(c) < 0) { + /* regurgitate */ + issue_char(wsi, '%'); + wsi->u.hdr.ues = URIES_IDLE; + /* regurgitate + assess */ + if (libwebsocket_parse(wsi, wsi->u.hdr.esc_stash) < 0) + return -1; + /* continue on to assess c */ + break; + } + c = (char_to_hex(wsi->u.hdr.esc_stash) << 4) | + char_to_hex(c); + break; + } + + /* + * special URI processing... + * convert /.. or /... etc to / + * convert // or /// etc to / + * leave /.dir or whatever alone + */ + + switch (wsi->u.hdr.ups) { + case URIPS_IDLE: + /* issue the first / always */ + if (c == '/') + wsi->u.hdr.ups = URIPS_SEEN_SLASH; + break; + case URIPS_SEEN_SLASH: + /* swallow subsequent slashes */ + if (c == '/') + goto swallow; + /* track and swallow the first . after / */ + if (c == '.') { + wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT; + goto swallow; + } else + wsi->u.hdr.ups = URIPS_IDLE; + break; + case URIPS_SEEN_SLASH_DOT: + /* swallow second . */ + if (c == '.') { + wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT_DOT; + goto swallow; + } + /* it was like /.dir ... regurgitate the . */ + wsi->u.hdr.ups = URIPS_IDLE; + issue_char(wsi, '.'); + break; + + case URIPS_SEEN_SLASH_DOT_DOT: + /* swallow prior .. chars and any subsequent . */ + if (c == '.') + goto swallow; + else /* last we issued was / so SEEN_SLASH */ + wsi->u.hdr.ups = URIPS_SEEN_SLASH; + break; + } + +check_eol: + /* bail at EOL */ if (wsi->u.hdr.parser_state != WSI_TOKEN_CHALLENGE && c == '\x0d') { @@ -202,15 +322,10 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) lwsl_parser("*\n"); } - if (wsi->u.hdr.ah->pos == sizeof(wsi->u.hdr.ah->data)) { - lwsl_warn("excessive header content\n"); +spill: + if (issue_char(wsi, c) < 0) return -1; - } - wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = c; - if (c) - wsi->u.hdr.ah->frags[ - wsi->u.hdr.ah->next_frag_index].len++; - +swallow: /* per-protocol end of headers management */ if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE) diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 7cf88995..5d2b4704 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -299,6 +299,18 @@ struct libwebsocket_context { void *user_space; }; +enum uri_path_states { + URIPS_IDLE, + URIPS_SEEN_SLASH, + URIPS_SEEN_SLASH_DOT, + URIPS_SEEN_SLASH_DOT_DOT, +}; + +enum uri_esc_states { + URIES_IDLE, + URIES_SEEN_PERCENT, + URIES_SEEN_PERCENT_H1, +}; /* * This is totally opaque to code using the library. It's exported as a @@ -335,6 +347,9 @@ struct _lws_header_related { struct allocated_headers *ah; short lextable_pos; unsigned char parser_state; /* enum lws_token_indexes */ + enum uri_path_states ups; + enum uri_esc_states ues; + char esc_stash; }; struct _lws_websocket_related { |