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; }