mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
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>
This commit is contained in:
parent
19895bcfd4
commit
b1a9e508cd
3 changed files with 172 additions and 25 deletions
135
lib/parsers.c
135
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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -99,18 +99,29 @@ struct serveable {
|
|||
const char *mimetype;
|
||||
};
|
||||
|
||||
static const struct serveable whitelist[] = {
|
||||
{ "/favicon.ico", "image/x-icon" },
|
||||
{ "/libwebsockets.org-logo.png", "image/png" },
|
||||
|
||||
/* last one is the default served if no match */
|
||||
{ "/test.html", "text/html" },
|
||||
};
|
||||
|
||||
struct per_session_data__http {
|
||||
int fd;
|
||||
};
|
||||
|
||||
const char * get_mimetype(const char *file)
|
||||
{
|
||||
int n = strlen(file);
|
||||
|
||||
if (n < 5)
|
||||
return NULL;
|
||||
|
||||
if (!strcmp(&file[n - 4], ".ico"))
|
||||
return "image/x-icon";
|
||||
|
||||
if (!strcmp(&file[n - 4], ".png"))
|
||||
return "image/png";
|
||||
|
||||
if (!strcmp(&file[n - 5], ".html"))
|
||||
return "text/html";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* this protocol server (always the first one) just knows how to do HTTP */
|
||||
|
||||
static int callback_http(struct libwebsocket_context *context,
|
||||
|
@ -133,6 +144,7 @@ static int callback_http(struct libwebsocket_context *context,
|
|||
struct stat stat_buf;
|
||||
struct per_session_data__http *pss =
|
||||
(struct per_session_data__http *)user;
|
||||
const char *mimetype;
|
||||
#ifdef EXTERNAL_POLL
|
||||
int fd = (int)(long)in;
|
||||
#endif
|
||||
|
@ -197,12 +209,17 @@ static int callback_http(struct libwebsocket_context *context,
|
|||
}
|
||||
|
||||
/* if not, send a file the easy way */
|
||||
|
||||
for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++)
|
||||
if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0)
|
||||
break;
|
||||
|
||||
sprintf(buf, "%s%s", resource_path, whitelist[n].urlpath);
|
||||
strcpy(buf, resource_path);
|
||||
if (strcmp(in, "/"))
|
||||
strncat(buf, in, sizeof(buf) - strlen(resource_path));
|
||||
else /* default file to serve */
|
||||
strcat(buf, "/test.html");
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
mimetype = get_mimetype(buf);
|
||||
if (!mimetype) {
|
||||
lwsl_err("Unknown mimetype for %s\n", buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* demostrates how to set a cookie on / */
|
||||
|
||||
|
@ -223,7 +240,7 @@ static int callback_http(struct libwebsocket_context *context,
|
|||
}
|
||||
|
||||
if (libwebsockets_serve_http_file(context, wsi, buf,
|
||||
whitelist[n].mimetype, other_headers))
|
||||
mimetype, other_headers))
|
||||
return -1; /* through completion or error, close the socket */
|
||||
|
||||
/*
|
||||
|
|
Loading…
Add table
Reference in a new issue