diff --git a/common/network/websocket.c b/common/network/websocket.c index e9ad04a..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,32 @@ 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++; + } + } + + *dst = '\0'; +} + +static void percent_encode(const char *src, char *dst) { + while (1) { + if (!*src) + break; + if (isalnum(*src) || *src == '.' || *src == ',') { + *dst++ = *src++; + } else { + *dst++ = '%'; + sprintf(dst, "%02X", *src); + dst += 2; + src++; } } @@ -746,6 +771,7 @@ def: static void dirlisting(ws_ctx_t *ws_ctx, const char fullpath[], const char path[]) { char buf[4096]; + char enc[PATH_MAX * 3 + 1]; unsigned i; // Redirect? @@ -779,11 +805,13 @@ static void dirlisting(ws_ctx_t *ws_ctx, const char fullpath[], const char path[ if (!strcmp(names[i]->d_name, ".") || !strcmp(names[i]->d_name, "..")) continue; + percent_encode(names[i]->d_name, enc); + if (names[i]->d_type == DT_DIR) - sprintf(buf, "
  • %s/
  • ", names[i]->d_name, + sprintf(buf, "
  • %s/
  • ", enc, names[i]->d_name); else - sprintf(buf, "
  • %s
  • ", names[i]->d_name, + sprintf(buf, "
  • %s
  • ", enc, names[i]->d_name); ws_send(ws_ctx, buf, strlen(buf)); @@ -822,13 +850,15 @@ static void servefile(ws_ctx_t *ws_ctx, const char *in) { len = strlen(path); } - wserr("Requested file '%s'\n", path); - sprintf(fullpath, "%s/%s", settings.httpdir, path); + percent_decode(path, buf, 1); + + wserr("Requested file '%s'\n", buf); + sprintf(fullpath, "%s/%s", settings.httpdir, buf); DIR *dir = opendir(fullpath); if (dir) { closedir(dir); - dirlisting(ws_ctx, fullpath, path); + dirlisting(ws_ctx, fullpath, buf); return; } @@ -976,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; @@ -1023,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]) @@ -1052,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]) @@ -1097,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;