diff --git a/common/network/websocket.c b/common/network/websocket.c index 1053613..6270b49 100644 --- a/common/network/websocket.c +++ b/common/network/websocket.c @@ -111,7 +111,7 @@ static const char *parse_get(const char * const in, const char * const opt, unsi return ""; } -static void percent_decode(const char *src, char *dst) { +static void percent_decode(const char *src, char *dst, const uint8_t filter) { while (1) { if (!*src) break; @@ -127,7 +127,15 @@ static void percent_decode(const char *src, char *dst) { hex[2] = '\0'; src += 2; - *dst++ = strtol(hex, NULL, 16); + *dst = strtol(hex, NULL, 16); + + if (filter) { + // Avoid directory traversal + if (*dst == '/') + *dst = '_'; + } + + dst++; } } @@ -842,7 +850,7 @@ static void servefile(ws_ctx_t *ws_ctx, const char *in) { len = strlen(path); } - percent_decode(path, buf); + percent_decode(path, buf, 1); wserr("Requested file '%s'\n", buf); sprintf(fullpath, "%s/%s", settings.httpdir, buf); @@ -998,14 +1006,14 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) { if (len) { memcpy(buf, param, len); buf[len] = '\0'; - percent_decode(buf, decname); + percent_decode(buf, decname, 0); } param = parse_get(args, "password", &len); if (len) { memcpy(buf, param, len); buf[len] = '\0'; - percent_decode(buf, decpw); + percent_decode(buf, decpw, 0); struct crypt_data cdata; cdata.initialized = 0; @@ -1045,7 +1053,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) { if (len) { memcpy(buf, param, len); buf[len] = '\0'; - percent_decode(buf, decname); + percent_decode(buf, decname, 0); } if (!decname[0]) @@ -1074,7 +1082,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) { if (len) { memcpy(buf, param, len); buf[len] = '\0'; - percent_decode(buf, decname); + percent_decode(buf, decname, 0); } if (!decname[0]) @@ -1119,7 +1127,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) { if (len) { memcpy(buf, param, len); buf[len] = '\0'; - percent_decode(buf, decname); + percent_decode(buf, decname, 0); } else { wserr("client param required\n"); goto nope;