From 8c62a848ed728485c3287da4c7b4d056c595bad6 Mon Sep 17 00:00:00 2001 From: "mingang.he" Date: Thu, 30 May 2024 22:18:43 +0800 Subject: [PATCH] Add support for running traced commands as a specified user This change adds a new command-line option `-u/--user=`. The primary motivation for this feature is to address the limitations when the traced command invokes a `setuid` system call (such as those executed by the `yay` command, which internally calls `sudo pacman ...`). Ordinary users cannot `ptrace` a `setuid` call, which leads to errors like "sudo: effective uid is not 0..." and the subsequent termination of the process. To circumvent this issue, the tracer process can start with root privileges, and the tracee process executes the commands to be traced as the specified user. For example: sudo mgraftcp -u username command_to_trace This allows tracing commands that invoke setuid system calls, which would otherwise fail due to insufficient privileges when run as a non-root user. --- conf.c | 7 +++++++ conf.h | 1 + graftcp.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/conf.c b/conf.c index a181dcb..fb76d9d 100644 --- a/conf.c +++ b/conf.c @@ -171,6 +171,7 @@ int conf_init(struct graftcp_conf *conf) conf->blackip_file_path = NULL; conf->whiteip_file_path = NULL; conf->ignore_local = NULL; + conf->username = NULL; return 0; } @@ -200,6 +201,10 @@ void conf_free(struct graftcp_conf *conf) free(conf->ignore_local); conf->ignore_local = NULL; } + if (conf->username) { + free(conf->username); + conf->username = NULL; + } } static char *xdg_config_path_dup(void) @@ -283,4 +288,6 @@ void conf_override(struct graftcp_conf *w, const struct graftcp_conf *r) w->whiteip_file_path = r->whiteip_file_path; if (r->ignore_local) w->ignore_local = r->ignore_local; + if (r->username) + w->username = r->username; } diff --git a/conf.h b/conf.h index 27d614e..52800b3 100644 --- a/conf.h +++ b/conf.h @@ -26,6 +26,7 @@ struct graftcp_conf { char *blackip_file_path; char *whiteip_file_path; bool *ignore_local; + char *username; }; typedef int (*config_cb)(const char *, const char *, struct graftcp_conf *); diff --git a/graftcp.c b/graftcp.c index 05c2423..46821d3 100644 --- a/graftcp.c +++ b/graftcp.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) #define ENABLE_SECCOMP_BPF @@ -47,6 +49,10 @@ int LOCAL_PIPE_FD; cidr_trie_t *BLACKLIST_IP = NULL; cidr_trie_t *WHITELACKLIST_IP = NULL; + +static uid_t run_uid; +static gid_t run_gid; + static int exit_code = 0; static void load_ip_file(char *path, cidr_trie_t **trie) @@ -308,7 +314,7 @@ void do_child(int argc, char **argv) } } -void init(int argc, char **argv) +void init(struct graftcp_conf *conf, int argc, char **argv) { pid_t child; struct proc_info *pi; @@ -318,6 +324,21 @@ void init(int argc, char **argv) perror("fork"); exit(errno); } else if (child == 0) { + if (conf->username) { + if (initgroups(conf->username, run_gid) < 0) { + perror("initgroups"); + exit(errno); + } + + if (setregid(run_gid, run_gid) < 0) { + perror("setregid"); + exit(errno); + } + if (setreuid(run_uid, run_uid) < 0) { + perror("setreuid"); + exit(errno); + } + } #ifdef ENABLE_SECCOMP_BPF install_seccomp(); #endif @@ -495,6 +516,8 @@ static void usage(char **argv) " -n --not-ignore-local\n" " Connecting to local is not changed by default, this\n" " option will redirect it to SOCKS5\n" + " -u --user=\n" + " Run command as USERNAME handling setuid and/or setgid\n" " -V --version\n" " Show version\n" " -h --help\n" @@ -514,6 +537,7 @@ int client_main(int argc, char **argv) {"local-fifo", required_argument, 0, 'f'}, {"blackip-file", required_argument, 0, 'b'}, {"whiteip-file", required_argument, 0, 'w'}, + {"user", required_argument, 0, 'u'}, {"not-ignore-local", no_argument, 0, 'n'}, {0, 0, 0, 0} }; @@ -525,6 +549,7 @@ int client_main(int argc, char **argv) .blackip_file_path = NULL, .whiteip_file_path = NULL, .ignore_local = &DEFAULT_IGNORE_LOCAL, + .username = NULL, }; __defer_free char *conf_file_path = NULL; @@ -533,7 +558,7 @@ int client_main(int argc, char **argv) conf_init(&file_conf); conf_init(&cmd_conf); - while ((opt = getopt_long(argc, argv, "+Vha:p:f:b:w:c:n", long_opts, + while ((opt = getopt_long(argc, argv, "+Vha:p:f:b:w:c:u:n", long_opts, &index)) != -1) { switch (opt) { case 'a': @@ -559,6 +584,9 @@ int client_main(int argc, char **argv) case 'c': conf_file_path = strdup(optarg); break; + case 'u': + cmd_conf.username = strdup(optarg); + break; case 'V': fprintf(stderr, "graftcp %s\n", VERSION); exit(0); @@ -609,7 +637,23 @@ int client_main(int argc, char **argv) exit(errno); } - init(argc - optind, argv + optind); + if (conf.username) { + struct passwd *pent; + + if (getuid() != 0 || geteuid() != 0) { + fprintf(stderr, "You must be root to use the -u option\n"); + exit(1); + } + pent = getpwnam(conf.username); + if (pent == NULL) { + fprintf(stderr, "Cannot find user '%s'\n", conf.username); + exit(1); + } + run_gid = pent->pw_gid; + run_uid = pent->pw_uid; + } + + init(&conf, argc - optind, argv + optind); if (do_trace() < 0) return -1; return exit_code;