diff --git a/cli/main.c b/cli/main.c index 12ada5b..894f7c9 100644 --- a/cli/main.c +++ b/cli/main.c @@ -1,6 +1,7 @@ #include "discovery.h" #include "ratelimit.h" #include +#include #include #include #include @@ -67,7 +68,7 @@ static pid_t parse_pid(const char *arg) { char *endptr; errno = 0; long pid_long = strtol(arg, &endptr, 10); - if (errno != 0 || *endptr != '\0' || pid_long <= 0) { + if (errno != 0 || *endptr != '\0' || pid_long <= 0 || pid_long > INT_MAX) { fprintf(stderr, "%s: invalid PID: %s\n", PROGRAM_NAME, arg); exit(EXIT_FAILURE); } diff --git a/gui/main.c b/gui/main.c index a786a8e..0a7dd03 100644 --- a/gui/main.c +++ b/gui/main.c @@ -1,5 +1,7 @@ #include +#include #include +#include #include #include #include @@ -42,17 +44,32 @@ static void handle_list_cmd(void) { destroy_process_list(list); } -static void handle_limit_cmd(pid_t pid, uint32_t upload, uint32_t download) { +static void handle_limit_cmd(const char *line) { + long pid, upload, download; + errno = 0; + if (sscanf(line, "%*ld %ld %ld %ld", &pid, &upload, &download) != 3 || errno != 0) { + printf("%d\n", BACKEND_RESPONSE_ERROR); + fflush(stdout); + return; + } + if (pid <= 0 || pid > INT_MAX || upload < 0 || upload > UINT32_MAX || download < 0 || + download > UINT32_MAX) { + printf("%d\n", BACKEND_RESPONSE_ERROR); + fflush(stdout); + return; + } + ratelimit_code rc; if (upload == RATELIMIT_UNLIMITED && download == RATELIMIT_UNLIMITED) { - // TODO: Check if it makes sense to clean using the monitor itself, instead of doing this. - rc = unregister_rate_limiter_by_pid(pid); + rc = unregister_rate_limiter_by_pid((pid_t)pid); if (rc == RATELIMIT_CGROUP_NOT_FOUND) { rc = RATELIMIT_OK; } } else { - rate_limit_config cfg = {.upload_kbps = upload, .download_kbps = download}; - rc = limit_process_bandwidth(pid, cfg); + rate_limit_config cfg = { + .upload_kbps = (uint32_t)upload, .download_kbps = (uint32_t)download + }; + rc = limit_process_bandwidth((pid_t)pid, cfg); } printf("%d\n", rc == RATELIMIT_OK ? BACKEND_RESPONSE_OK : BACKEND_RESPONSE_ERROR); @@ -79,35 +96,24 @@ static int run_privileged(void) { char line[64]; while (fgets(line, sizeof(line), stdin)) { - int cmd; - if (sscanf(line, "%d", &cmd) != 1) { + long cmd; + errno = 0; + if (sscanf(line, "%ld", &cmd) != 1 || errno != 0) { continue; } - if (cmd == BACKEND_CMD_LIST) { + switch (cmd) { + case BACKEND_CMD_LIST: handle_list_cmd(); - continue; - } - - if (cmd == BACKEND_CMD_LIMIT) { - gint pid; - uint32_t upload, download; - if (sscanf(line, "%d %d %u %u", &cmd, &pid, &upload, &download) == 4) { - handle_limit_cmd(pid, upload, download); - } else { - printf("%d\n", BACKEND_RESPONSE_ERROR); - } - fflush(stdout); - continue; - } - - if (cmd == BACKEND_CMD_CLEAN) { + break; + case BACKEND_CMD_LIMIT: + handle_limit_cmd(line); + break; + case BACKEND_CMD_CLEAN: handle_clean_cmd(); - continue; - } - - if (cmd == BACKEND_CMD_QUIT) { break; + case BACKEND_CMD_QUIT: + return EXIT_SUCCESS; } } diff --git a/lib/src/discovery.c b/lib/src/discovery.c index 5df2a59..c76aa99 100644 --- a/lib/src/discovery.c +++ b/lib/src/discovery.c @@ -93,6 +93,13 @@ static int add_process_list(process_list *list, pid_t pid, int is_tcp) { if (len > 0 && proc->process_name[len - 1] == '\n') { proc->process_name[len - 1] = '\0'; } + // The process itself controls its comm (prctl/argv[0]), so strip + // control characters to prevent terminal escape injection. + for (char *p = proc->process_name; *p; p++) { + if ((unsigned char)*p < 0x20 || (unsigned char)*p == 0x7f) { + *p = '?'; + } + } } fclose(f); } @@ -101,6 +108,12 @@ static int add_process_list(process_list *list, pid_t pid, int is_tcp) { ssize_t len = readlink(path, proc->exe_path, sizeof(proc->exe_path) - 1); if (len != -1) { proc->exe_path[len] = '\0'; + // Same here + for (char *p = proc->exe_path; *p; p++) { + if ((unsigned char)*p < 0x20 || (unsigned char)*p == 0x7f) { + *p = '?'; + } + } } list->count++; diff --git a/lib/src/monitor.c b/lib/src/monitor.c index e0a2b94..08fdb3a 100644 --- a/lib/src/monitor.c +++ b/lib/src/monitor.c @@ -133,11 +133,17 @@ static void monitor_run(void) { struct sockaddr_un addr; struct stat st; - if (stat(MONITOR_SOCKET_DIR, &st) < 0) { - if (mkdir(MONITOR_SOCKET_DIR, 0700) < 0 && errno != EEXIST) { + // The directory may already exist; only trust it if it is a real + // root-owned directory and not a symlink. + if (lstat(MONITOR_SOCKET_DIR, &st) < 0) { + if (errno != ENOENT || mkdir(MONITOR_SOCKET_DIR, 0700) < 0 || + lstat(MONITOR_SOCKET_DIR, &st) < 0) { exit(1); } } + if (!S_ISDIR(st.st_mode) || st.st_uid != 0) { + exit(1); + } unlink(MONITOR_SOCKET_PATH); monitor_socket = socket(AF_UNIX, SOCK_STREAM, 0); diff --git a/lib/src/ratelimit.c b/lib/src/ratelimit.c index 602b098..a485183 100644 --- a/lib/src/ratelimit.c +++ b/lib/src/ratelimit.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -153,7 +154,7 @@ static ratelimit_code attach_bpf_programs(rate_limiter *limiter, rate_limit_conf } static void build_cgroup_name(pid_t pid, char *buf, size_t size) { - snprintf(buf, size, "%s/%d", CGROUP_NAME, pid); + snprintf(buf, size, "%s/%ld", CGROUP_NAME, (long)pid); } ratelimit_code ratelimit_init(void) { @@ -309,7 +310,7 @@ ratelimit_code ratelimit_cleanup_all(void) { char *endptr; errno = 0; long pid_long = strtol(info.path, &endptr, 10); - if (errno != 0 || *endptr != '\0' || pid_long <= 0) { + if (errno != 0 || *endptr != '\0' || pid_long <= 0 || pid_long > INT_MAX) { ret = cgroup_walk_tree_next(0, &handle, &info, base_level); continue; }