From a66608159965718452f994655718b2b83f3a0f95 Mon Sep 17 00:00:00 2001 From: Alessandro Mauri Date: Sat, 3 Apr 2021 17:17:23 +0200 Subject: [PATCH] touches to shell proxy reworked the privilege escalation works by changing the way target user info is passed around, now we pass struct passwd* and struct group* instead of strings wich allows much greater flexibility. Also now us mimics the behavior of sudo and doas regarding the shell proxies: - commands are started under a subshell, that shell is the user's - if no command is specified just the shell is executed --- TODO | 2 + us.c | 225 ++++++++++++++++++++++++++++++++--------------------------- 2 files changed, 125 insertions(+), 102 deletions(-) diff --git a/TODO b/TODO index 207d22a..01fe41b 100644 --- a/TODO +++ b/TODO @@ -3,3 +3,5 @@ * LOGNAME -> to target user * SHELL -> to the target user's SHELL * HOME -> to the target user's HOME + +- Reconsider executing programs in a subshell diff --git a/us.c b/us.c index 53d5af8..40ab1c6 100644 --- a/us.c +++ b/us.c @@ -32,8 +32,10 @@ #include static void usage (void); -static int perm_set (const char *, const char *); +static int perm_set (struct passwd *, struct group *); static int authenticate (const char *); +static struct passwd* user_to_passwd (const char *); +static struct group* group_to_grp (const char *); // FIXME: misc_conv is a separate library, should stick to plain PAM or make // our own pam module @@ -43,9 +45,12 @@ int main (int argc, char *argv[]) { // TODO: Add arguments // FIXME: change the default program to execute SHELL - char *t_usr = NULL, *t_grp = NULL; + char *t_usr = "root", *t_grp = NULL; + struct passwd *t_pw; + struct group *t_gr; int opt; - while ((opt = getopt(argc, argv, "A:u:g:C:")) != -1) { + int shellflag = 0; + while ((opt = getopt(argc, argv, "A:u:g:C:s")) != -1) { switch (opt) { case 'A': printf("-A is not yet implemented\n"); @@ -61,6 +66,9 @@ int main (int argc, char *argv[]) printf("-C is not yet implemented\n"); exit(EXIT_FAILURE); break; + case 's': + shellflag = 1; + break; case '?': usage(); exit(EINVAL); @@ -68,64 +76,51 @@ int main (int argc, char *argv[]) } } -/* - int c_argc = argc - optind + 2; - char **c_argv = malloc((c_argc + 1) * sizeof(char *)); - if (!c_argv) { - fprintf(stderr, "malloc: %s\n", strerror(errno)); - goto fail_end; + /* Get user info */ + const char *uname; + char *shell; + uid_t ruid = getuid(); + struct passwd *my_pw = getpwuid(ruid); + if (!my_pw) { + fprintf(stderr, "getpwid: %s\n", strerror(errno)); + return errno; } - c_argv[0] = shell; - c_argv[1] = "-c"; - for (int i = 2; optind < argc; i++, optind++) { - c_argv[i] = strdup(argv[optind]); - if (!c_argv[i]) { - fprintf(stderr, "getpwid: %s\n", strerror(errno)); - exit(errno); - } + uname = my_pw->pw_name; + + t_pw = user_to_passwd(t_usr); + if (!t_pw) { + fprintf(stderr, "user_to_passwd: %s\n", strerror(errno)); + return errno; } - c_argc[c_argv] = NULL; -*/ + t_gr = group_to_grp(t_grp); + + /* Get target user's shell */ + if (!shellflag) + shell = t_pw->pw_shell; + else + shell = getenv("SHELL"); + if (!shell) + shell = "/bin/sh"; /* Set argc and argv */ - // TODO: get the right shell for the user (it is in the passwd struct) - char *shell = "/bin/sh"; - char *command, **c_argv; + char *command = NULL; int size, popind = optind; - int c_argc = 3; // /bin/sh -c command - - c_argv = malloc((c_argc + 1) * sizeof(char *)); - if (!c_argv) { - fprintf(stderr, "malloc: %s\n", strerror(errno)); - exit(errno); - } - - for (size = 0; optind < argc; optind++) - size += strlen(argv[optind]) + 1; - command = malloc(sizeof(char) * size); - if (!command) { - fprintf(stderr, "malloc: %s\n", strerror(errno)); - exit(errno); - } - for (optind = popind; optind < argc; optind++) { - strcat(command, argv[optind]); - strcat(command, " "); + if (argc - optind) { + for (size = 0; optind < argc; optind++) + size += strlen(argv[optind]) + 1; + command = malloc(sizeof(char) * size + 1); + if (!command) { + fprintf(stderr, "malloc: %s\n", strerror(errno)); + exit(errno); + } + memset(command, 0, size + 1); + for (optind = popind; optind < argc; optind++) { + strcat(command, argv[optind]); + strcat(command, " "); + } } - c_argv[0] = shell; - // FIXME: this should be optional if size is zero: we want just a shell - c_argv[1] = "-c"; - c_argv[2] = command; - c_argv[c_argc] = NULL; - /* Authenticate */ - uid_t ruid = getuid(); - struct passwd *pw = getpwuid(ruid); - if (!pw) { - fprintf(stderr, "getpwid: %s\n", strerror(errno)); - return errno; - } - const char *uname = pw->pw_name; if (authenticate(uname) != PAM_SUCCESS) exit(EXIT_FAILURE); @@ -133,21 +128,24 @@ int main (int argc, char *argv[]) errno = 0; /* Set permissions */ - if (perm_set(t_usr, t_grp) == -1) { // 0 = root + if (perm_set(t_pw, t_gr) == -1) { fprintf(stderr, "perm_set: %s\n", strerror(errno)); goto fail_end; } /* Execute the command */ - if (execv(*c_argv, c_argv) == -1) // execvp searches in path - fprintf(stderr, "execv: %s\n", strerror(errno)); + int err; + if (command) + err = execl(shell, shell, "-c", command, (char *)NULL); + else + err = execl(shell, shell, (char *)NULL); + if (err == -1) + fprintf(stderr, "execl: %s\n", strerror(errno)); /* Cleanup and return */ fail_end: /* Free up the copied argv */ - for (int i = 0; i < c_argc; i++) - free(c_argv[i]); - free(c_argv); + free(command); return errno; } @@ -161,60 +159,24 @@ static inline void usage (void) // -c [file]: manually select config file // something about environment // something about non interactiveness - printf("usage: us [-u user] [-g group] command [args]\n"); + printf("usage: us [-s] [-u user] [-g group] command [args]\n"); } -static int perm_set (const char *user, const char *group) +static int perm_set (struct passwd *pw, struct group *gr) { - if (!user) - user = "root"; - - struct passwd *pw; - struct group *gr; - uid_t uid; - gid_t gid; - long uid_l, gid_l; - - errno = 0; - if (user[0] != '#') { - pw = getpwnam(user); - } else { - uid_l = strtol(&user[1], NULL, 10); - if (uid_l < 0 || errno) { - errno = errno ? errno : EINVAL; - return -1; - } - pw = getpwuid((uid_t)uid_l); - } - if (!pw) { - if (!errno) - errno = EINVAL; + errno = EINVAL; return -1; } + uid_t uid; + gid_t gid; + uid = pw->pw_uid; gid = pw->pw_gid; - if (group) { - if (group[0] != '#') { - gr = getgrnam(group); - } else { - gid_l = strtol(&group[1], NULL, 10); - if (gid_l < 0 || errno) { - errno = errno ? errno : EINVAL; - return -1; - } - gr = getgrgid((gid_t)gid_l); - } - - if (!gr) { - if (!errno) - errno = EINVAL; - return -1; - } + if (gr) gid = gr->gr_gid; - } /* Set permissions, setting group perms first because in the case of * dropping from higher permissions setting the uid first results in @@ -272,3 +234,62 @@ static int authenticate (const char *uname) return pam_end(pamh, pam_err); } + +static struct passwd* user_to_passwd (const char *user) +{ + if (!user) { + errno = EINVAL; + return NULL; + } + + struct passwd* pw; + long uid_l; + errno = 0; + if (user[0] != '#') { + pw = getpwnam(user); + } else { + uid_l = strtol(&user[1], NULL, 10); + if (uid_l < 0 || errno) { + errno = errno ? errno : EINVAL; + return NULL; + } + pw = getpwuid((uid_t)uid_l); + } + + if (!pw) { + if (!errno) + errno = EINVAL; + return NULL; + } + + return pw; +} + +static struct group* group_to_grp (const char *group) +{ + if (!group) { + errno = EINVAL; + return NULL; + } + + struct group* gr; + long gid_l; + errno = 0; + if (group[0] != '#') { + gr = getgrnam(group); + } else { + gid_l = strtol(&group[1], NULL, 10); + if (gid_l < 0 || errno) { + errno = errno ? errno : EINVAL; + return NULL; + } + gr = getgrgid((gid_t)gid_l); + } + + if (!gr) { + if (!errno) + errno = EINVAL; + return NULL; + } + return gr; +}