From daffea4660560f7420e57ecc40b29f6ef3c46f98 Mon Sep 17 00:00:00 2001 From: Xavier Del Campo Romero Date: Thu, 23 Nov 2023 00:01:41 +0100 Subject: main.c: Treat non-existing upload dir as non-fatal When a user attempts to upload a file into a non-existing directory, slcl would not check whether the directory exists. Then, rename(3) would fail and slcl would treat this as a fatal error, effectively closing itself. Since this is an example of ill-formed user input, it must be treated as a non-fatal error, and instead slcl should return a bad request page. --- main.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/main.c b/main.c index 5abf848..0c7be51 100644 --- a/main.c +++ b/main.c @@ -1131,12 +1131,43 @@ static int rename_or_move(const char *const old, const char *const new) return res; } +static int check_upload_dir(const char *const dir) +{ + struct stat sb; + + if (stat(dir, &sb)) + { + switch (errno) + { + case ENOENT: + /* Fall through. */ + case ENOTDIR: + fprintf(stderr, "%s: cannot upload to non-existing dir %s\n", + __func__, dir); + return 1; + + default: + fprintf(stderr, "%s: stat(2) %s: %s\n", + __func__, dir, strerror(errno)); + return -1; + } + } + else if (!S_ISDIR(sb.st_mode)) + { + fprintf(stderr, "%s: %s not a dir\n", __func__, dir); + return 1; + } + + return 0; +} + static int upload_file(const struct http_post_file *const f, const char *const user, const char *const root, const char *const dir) { int ret = -1; - struct dynstr d; + struct dynstr dird, d; + dynstr_init(&dird); dynstr_init(&d); if (!root) @@ -1144,9 +1175,21 @@ static int upload_file(const struct http_post_file *const f, fprintf(stderr, "%s: auth_dir failed\n", __func__); goto end; } - else if (dynstr_append(&d, "%s/user/%s/%s%s", root, user, dir, f->filename)) + else if (dynstr_append(&dird, "%s/user/%s/%s", root, user, dir)) { - fprintf(stderr, "%s: dynstr_append failed\n", __func__); + fprintf(stderr, "%s: dynstr_append dird failed\n", __func__); + goto end; + } + else if ((ret = check_upload_dir(dird.str))) + { + if (ret < 0) + fprintf(stderr, "%s: check_upload_dir failed\n", __func__); + + goto end; + } + else if (dynstr_append(&d, "%s%s", dird.str, f->filename)) + { + fprintf(stderr, "%s: dynstr_append d failed\n", __func__); goto end; } else if (rename_or_move(f->tmpname, d.str)) @@ -1158,6 +1201,7 @@ static int upload_file(const struct http_post_file *const f, ret = 0; end: + dynstr_free(&dird); dynstr_free(&d); return ret; } @@ -1257,11 +1301,15 @@ static int upload_files(const struct http_payload *const p, for (size_t i = 0; i < po->nfiles; i++) { - if (upload_file(&po->files[i], user, root, dir)) + const int ret = upload_file(&po->files[i], user, root, dir); + + if (ret < 0) { fprintf(stderr, "%s: upload_file failed\n", __func__); return -1; } + else if (ret) + return page_bad_request(r); } return redirect_to_dir(dir, r); -- cgit v1.2.3