From ccd667be067a5148545d7ccc958e29e65a2ac458 Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Wed, 29 Jun 2022 20:12:20 +0200 Subject: validate revisions, set threads to 4, do not allow concurent updates --- src/CMakeLists.txt | 3 +++ src/cli/updater.c | 27 +++++++++++++++++------ src/fs.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/fs.h | 5 +++++ src/qt/workers.cpp | 22 ++++++++++++++++-- src/qt/workers.hpp | 1 + src/toast.c | 7 ++++-- 7 files changed, 119 insertions(+), 11 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 26279e3..ffc77c7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,6 +36,9 @@ target_link_libraries(tvn LINK_PUBLIC ${LIBCURL_LIBRARIES}) target_link_libraries(tvn LINK_PUBLIC ${JSONC_LIBRARIES}) target_link_libraries(tvn LINK_PUBLIC md5) target_link_libraries(tvn LINK_PUBLIC vdf) +if (WIN32) + target_link_libraries(tvn LINK_PUBLIC shlwapi) +endif() # frontends are included last so they will be # put in the root of the build directory diff --git a/src/cli/updater.c b/src/cli/updater.c index ae86aa1..68757c5 100644 --- a/src/cli/updater.c +++ b/src/cli/updater.c @@ -10,7 +10,7 @@ #include -#define THREAD_COUNT 8 +#define THREAD_COUNT 4 struct thread_object_info { int working; @@ -57,6 +57,19 @@ void update_setup(char* of_dir, char* remote, int local_rev, int remote_rev) if (rev) { + fprintf(stderr, "Validating revisions"); + for (size_t i = 0; i < rev->file_count; ++i) + { + struct file_info* file = &rev->files[i]; + + if (leavesRelativePath(file->path)) + { + printf("Revision contains invalid path '%s'\n", file->path); + freeRevision(rev); + return; + } + } + pthread_t download_threads[THREAD_COUNT] = {0}; struct thread_object_info thread_info[THREAD_COUNT] = {0}; size_t tindex = 0; @@ -100,20 +113,20 @@ void update_setup(char* of_dir, char* remote, int local_rev, int remote_rev) free(buf); } - fprintf(stderr, "\rInstalling..."); + puts(""); for (size_t i = 0; i < rev->file_count; ++i) { struct file_info* file = &rev->files[i]; + fprintf(stderr, "\rInstalling %zu/%zu (%s)", i+1, rev->file_count, file->object); switch (file->type) { case TYPE_WRITE: case TYPE_MKDIR: { - int k = applyObject(of_dir, file); - if (k) + if (applyObject(of_dir, file)) { - printf("Failed to write %s\n", file->path); + printf("\rFailed to write %s\n", file->path); } } break; @@ -125,7 +138,7 @@ void update_setup(char* of_dir, char* remote, int local_rev, int remote_rev) snprintf(buf, len, "%s%s%s", of_dir, OS_PATH_SEP, file->path); if (isFile(buf) && remove(buf)) { - printf("Failed to delete %s\n", file->path); + printf("\rFailed to delete %s\n", file->path); } free(buf); } @@ -137,7 +150,7 @@ void update_setup(char* of_dir, char* remote, int local_rev, int remote_rev) setLocalRemote(of_dir, remote); setLocalRevision(of_dir, remote_rev); - fprintf(stderr, "\rUpdated OpenFortress\n"); + fprintf(stderr, "\nUpdated OpenFortress\n"); freeRevision(rev); } } diff --git a/src/fs.c b/src/fs.c index ee7a39d..ee11a1b 100644 --- a/src/fs.c +++ b/src/fs.c @@ -7,6 +7,11 @@ #include #include +#ifdef _WIN32 +#include +#include +#endif + #include "fs.h" #ifdef _WIN32 @@ -57,6 +62,66 @@ int isDir(const char* path) return S_ISDIR(sb.st_mode); } +int isRelativePath(const char* path) +{ + if (!path) + return 0; + else if (*path == *OS_PATH_SEP) + return 0; +#ifdef _WIN32 + else if (!PathIsRelativeA(path)) + return 0; +#endif + return 1; +} + +int leavesRelativePath(const char* path) +{ + if (!path || !isRelativePath(path)) + return 0; + + int depth = 0; + const char* head = path; + const char* tail = head; + + while (*tail) + { + ++tail; + if (*tail == *OS_PATH_SEP || *tail == '\0') + { + size_t size = (size_t)(tail-head); + if (!size) + continue; + else if (!strncmp(head, "..", size)) + depth -= 1; + else if (strncmp(head, ".", size)) + depth += 1; + + if (depth < 0) + return 1; + + head = tail + 1; + } + } + return 0; +} + +char* normalizeUnixPath(char* path) +{ + char* head = path; + if (head) + { + while (*head) + { + if (*head == '/') + *head = *OS_PATH_SEP; + + ++head; + } + } + + return path; +} int makeDir(const char* path) { char pathcpy[PATH_MAX]; diff --git a/src/fs.h b/src/fs.h index 26cdd8b..109f5ed 100644 --- a/src/fs.h +++ b/src/fs.h @@ -14,6 +14,11 @@ extern "C" { int isFile(const char*); int isDir(const char*); +int isRelativePath(const char*); +int leavesRelativePath(const char*); + +char* normalizeUnixPath(char* path); + int makeDir(const char*); int removeDir(const char*); diff --git a/src/qt/workers.cpp b/src/qt/workers.cpp index 41e8994..dc761e9 100644 --- a/src/qt/workers.cpp +++ b/src/qt/workers.cpp @@ -109,14 +109,30 @@ void Worker::stop_work() int Worker::update_setup(int local_rev, int remote_rev) { if (!of_dir) return 1; + else if (update_in_progress) return 0; - int retval = 0; - + update_in_progress = true; + int retval = 0; struct revision_t* rev = fastFowardRevisions(remote, local_rev, remote_rev); if (rev) { + for (size_t i = 0; i < rev->file_count; ++i) + { + struct file_info* file = &rev->files[i]; + + if (leavesRelativePath(file->path)) + { + infoText = QString("Revision contains invalid path '%1'").arg(file->path); + emit resultReady(RESULT_UPDATE_TEXT); + + update_in_progress = false; + freeRevision(rev); + return 1; + } + } + pthread_t download_threads[THREAD_COUNT] = {0}; struct thread_object_info thread_info[THREAD_COUNT] = {0, NULL, NULL, NULL, NULL, 0}; size_t tindex = 0; @@ -222,6 +238,8 @@ int Worker::update_setup(int local_rev, int remote_rev) freeRevision(rev); } + update_in_progress = false; + return retval; } diff --git a/src/qt/workers.hpp b/src/qt/workers.hpp index a31d9eb..82325cb 100644 --- a/src/qt/workers.hpp +++ b/src/qt/workers.hpp @@ -20,6 +20,7 @@ private: size_t remote_len; bool do_work = true; + bool update_in_progress = false; public: int progress = -1; diff --git a/src/toast.c b/src/toast.c index 475847c..4c26142 100644 --- a/src/toast.c +++ b/src/toast.c @@ -348,7 +348,7 @@ struct revision_t* getRevisionData(char* url, int rev) json_object_object_get_ex(file, "path", &temp); assert(temp); - revision->files[i].path = strdup(json_object_get_string(temp)); + revision->files[i].path = normalizeUnixPath(strdup(json_object_get_string(temp))); json_object_object_get_ex(file, "hash", &temp); if (temp) @@ -419,7 +419,7 @@ struct revision_t* fastFowardRevisions(char* url, int from, int to) rev->files[rev->file_count].type = cur_rev->files[j].type; if (cur_rev->files[j].path) - rev->files[rev->file_count].path = strdup(cur_rev->files[j].path); + rev->files[rev->file_count].path = normalizeUnixPath(strdup(cur_rev->files[j].path)); else rev->files[rev->file_count].path = NULL; @@ -515,6 +515,7 @@ size_t downloadObject(char* dir, char* url, struct file_info* info) * 0 on success * 1 on missing object * 2 on general failure + * 3 on invalid path */ int applyObject(char* path, struct file_info* info) { @@ -526,6 +527,8 @@ int applyObject(char* path, struct file_info* info) if (!path || !isDir(path)) return 2; + else if (!isRelativePath(file) || leavesRelativePath(file)) + return 3; else if (!object) return 0; -- cgit v1.2.3