1
0
Fork 0
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:
Andy Green 2013-11-10 15:15:21 +08:00
parent 19895bcfd4
commit b1a9e508cd
3 changed files with 172 additions and 25 deletions

View file

@ -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)

View file

@ -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 {

View file

@ -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 */
/*