aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAndy Green <andy.green@linaro.org>2013-11-10 15:15:21 +0800
committerAndy Green <andy.green@linaro.org>2013-11-10 15:15:21 +0800
commitb1a9e508cd1626f1b1fa411711d9ddb64a86d8e3 (patch)
treeccb0f98868b8d4b048c07daa717bc2853d02c23d /lib
parent19895bcfd4e89f96100ab2dca041e965973f8e2b (diff)
downloadlibwebsockets-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.c135
-rw-r--r--lib/private-libwebsockets.h15
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 {