From 7659765116c1edb91effc8389e55734f33add92f Mon Sep 17 00:00:00 2001 From: Lauri Kasanen Date: Tue, 24 Sep 2024 15:03:54 +0300 Subject: [PATCH 1/4] Implement /api/downloads --- common/network/websocket.c | 98 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/common/network/websocket.c b/common/network/websocket.c index bd231d2..48946fb 100644 --- a/common/network/websocket.c +++ b/common/network/websocket.c @@ -27,6 +27,9 @@ #include #include #include // daemonizing +#include +#include +#include #include #include #include /* base64 encode/decode */ @@ -1636,6 +1639,101 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in, const char * const use ws_send(ws_ctx, buf, strlen(buf)); weblog(200, wsthread_handler_id, 0, origip, ip, user, 1, origpath, strlen(buf)); + ret = 1; + } else entry("/api/downloads") { + char subpath[PATH_MAX] = "", startpath[PATH_MAX] = "~/Downloads", allpath[PATH_MAX]; + param = parse_get(args, "path", &len); + if (len) { + memcpy(buf, param, len); + buf[len] = '\0'; + percent_decode(buf, subpath, 0); + + if (strstr(subpath, "../")) { + handler_msg("Attempted directory traversal in /api/downloads\n"); + goto nope; + } + } + + wordexp_t wexp; + if (!wordexp(startpath, &wexp, WRDE_NOCMD)) + strcpy(startpath, wexp.we_wordv[0]); + else + goto nope; + wordfree(&wexp); + + snprintf(allpath, PATH_MAX, "%s/%s", startpath, subpath); + allpath[PATH_MAX - 1] = '\0'; + + DIR *dir = opendir(allpath); + if (!dir) { + handler_msg("Requested dir does not exist\n"); + goto nope; + } + + sprintf(buf, "HTTP/1.1 200 OK\r\n" + "Server: KasmVNC/4.0\r\n" + "Connection: close\r\n" + "Content-type: text/json\r\n" + "%s" + "\r\n", extra_headers ? extra_headers : ""); + ws_send(ws_ctx, buf, strlen(buf)); + len = 15; + + ws_send(ws_ctx, "{ \"files\": [\n", 13); + + struct dirent *ent; + unsigned char sent = 0; + while ((ent = readdir(dir))) { + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + continue; + + sprintf(path, "%s/%s", allpath, ent->d_name); + struct stat st; + if (lstat(path, &st)) + continue; + + char own[LOGIN_NAME_MAX], grp[LOGIN_NAME_MAX], perms[32]; + sprintf(perms, "%o", st.st_mode & 0777); + + struct passwd pwdt, *pwdptr; + if (getpwuid_r(st.st_uid, &pwdt, buf, sizeof(buf), &pwdptr)) { + sprintf(own, "(unknown uid %u)", st.st_uid); + } else { + strcpy(own, pwdt.pw_name); + } + + struct group grpt, *grpptr; + if (getgrgid_r(st.st_gid, &grpt, buf, sizeof(buf), &grpptr)) { + sprintf(grp, "(unknown gid %u)", st.st_gid); + } else { + strcpy(grp, grpt.gr_name); + } + + sprintf(buf, "%s{ \"filename\": \"%s\", " + "\"date_modified\": %lu, " + "\"date_created\": %lu, " + "\"is_dir\": %s, " + "\"owner\": \"%s\", " + "\"group\": \"%s\", " + "\"perms\": \"%s\" }", + sent ? ",\n" : "", + ent->d_name, + st.st_mtime, + st.st_ctime, + S_ISDIR(st.st_mode) ? "true" : "false", + own, + grp, + perms); + sent = 1; + ws_send(ws_ctx, buf, strlen(buf)); + len += strlen(buf); + } + + ws_send(ws_ctx, "]}", 2); + + closedir(dir); + weblog(200, wsthread_handler_id, 0, origip, ip, user, 1, origpath, len); + ret = 1; } From 0cc4a4f1286a66ee955e398e17f078c0261eb8c7 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen Date: Tue, 24 Sep 2024 15:04:24 +0300 Subject: [PATCH 2/4] Warning fix --- common/network/websocket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/network/websocket.c b/common/network/websocket.c index 48946fb..7952479 100644 --- a/common/network/websocket.c +++ b/common/network/websocket.c @@ -927,7 +927,7 @@ static void servefile(ws_ctx_t *ws_ctx, const char *in, const char * const user, // in case they percent-encoded dots if (strstr(buf, "../")) { - handler_msg("Attempted dir traversal attack, rejecting\n", len); + handler_msg("Attempted dir traversal attack, rejecting\n"); goto nope; } From 17ec8c2e35348524b242992df449d80930ba0c9c Mon Sep 17 00:00:00 2001 From: Lauri Kasanen Date: Tue, 24 Sep 2024 17:45:30 +0300 Subject: [PATCH 3/4] Tune perm printing format --- common/network/websocket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/network/websocket.c b/common/network/websocket.c index 7952479..2aa2ce0 100644 --- a/common/network/websocket.c +++ b/common/network/websocket.c @@ -1693,7 +1693,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in, const char * const use continue; char own[LOGIN_NAME_MAX], grp[LOGIN_NAME_MAX], perms[32]; - sprintf(perms, "%o", st.st_mode & 0777); + sprintf(perms, "%03o", st.st_mode & 0777); struct passwd pwdt, *pwdptr; if (getpwuid_r(st.st_uid, &pwdt, buf, sizeof(buf), &pwdptr)) { From 73e28f7d62a60c2602c69323a16c095e2b79d22f Mon Sep 17 00:00:00 2001 From: Lauri Kasanen Date: Tue, 24 Sep 2024 17:48:19 +0300 Subject: [PATCH 4/4] Add size to downloads json --- common/network/websocket.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/network/websocket.c b/common/network/websocket.c index 2aa2ce0..f128ffe 100644 --- a/common/network/websocket.c +++ b/common/network/websocket.c @@ -1713,6 +1713,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in, const char * const use "\"date_modified\": %lu, " "\"date_created\": %lu, " "\"is_dir\": %s, " + "\"size\": %lu, " "\"owner\": \"%s\", " "\"group\": \"%s\", " "\"perms\": \"%s\" }", @@ -1721,6 +1722,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in, const char * const use st.st_mtime, st.st_ctime, S_ISDIR(st.st_mode) ? "true" : "false", + S_ISDIR(st.st_mode) ? 0 : st.st_size, own, grp, perms);