diff options
Diffstat (limited to 'progs/old/sucap.c')
-rw-r--r-- | progs/old/sucap.c | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/progs/old/sucap.c b/progs/old/sucap.c new file mode 100644 index 0000000..366a093 --- /dev/null +++ b/progs/old/sucap.c @@ -0,0 +1,199 @@ +/* + * $Id: sucap.c,v 1.1.1.1 1999/04/17 22:16:31 morgan Exp $ + * + * This was written by Finn Arne Gangstad <finnag@guardian.no> + * + * This is a program that is intended to exec a subsequent program. + * The purpose of this 'sucap' wrapper is to change uid but keep all + * privileges. All environment variables are inherited. + */ + +#include <sys/types.h> +#include <errno.h> +#include <stdio.h> +#undef _POSIX_SOURCE +#include <sys/capability.h> +#include <pwd.h> +#define __USE_BSD +#include <grp.h> +#include <unistd.h> +#include <sys/wait.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> + +static void usage(void) +{ + fprintf(stderr, +"usage: sucap <user> <group> <command-path> [command-args...]\n\n" +" This program is a wrapper that change UID but not privileges of a\n" +" program to be executed.\n" +" Note, this wrapper is intended to assist in overcoming a lack of support\n" +" for filesystem capability attributes and should be used to launch other\n" +" files. This program should _NOT_ be made setuid-0.\n\n" +"[Copyright (c) 1998 Finn Arne Gangstad <finnag@guardian.no>]\n"); + + exit(1); +} + + +static void +wait_on_fd(int fd) +{ + /* Wait until some data is available on a file descriptor, or until + * end of file or an error is detected */ + char buf[1]; + while (read(fd, buf, sizeof(buf)) == -1 && errno == EINTR) { + /* empty loop */ + } +} + + +int main(int argc, char **argv) +{ + cap_t old_caps; + uid_t uid; + pid_t pid, parent_pid; + gid_t gid; + int pipe_fds[2]; + + /* this program should not be made setuid-0 */ + if (getuid() && !geteuid()) { + usage(); + } + + /* check that we have at least 3 arguments */ + if (argc < 4) { + usage(); + } + + /* Convert username to uid */ + { + struct passwd *pw = getpwnam(argv[1]); + if (!pw) { + fprintf(stderr, "sucap: No such user: %s\n", argv[1]); + exit(1); + } + uid = pw->pw_uid; + } + + /* Convert groupname to gid */ + { + struct group *gr = getgrnam(argv[2]); + if (!gr) { + fprintf(stderr, "sucap: No such group: %s\n", argv[2]); + exit(1); + } + gid = gr->gr_gid; + } + + /* set process group to current pid */ + if (setpgid(0, getpid())) { + perror("sucap: Failed to set process group"); + exit(1); + } + + if (pipe(pipe_fds)) { + perror("sucap: pipe() failed"); + exit(1); + } + + parent_pid = getpid(); + + old_caps = cap_init(); + if (capgetp(0, old_caps)) { + perror("sucap: capgetp"); + exit(1); + } + + { + ssize_t x; + printf("Caps: %s\n", cap_to_text(old_caps, &x)); + } + + + /* fork off a child to do the hard work */ + fflush(NULL); + pid = fork(); + if (pid == -1) { + perror("sucap: fork failed"); + exit(1); + } + + /* 1. mother process sets gid and uid + * 2. child process sets capabilities of mother process + * 3. mother process execs whatever is to be executed + */ + + if (pid) { + /* Mother process. */ + close(pipe_fds[0]); + + /* Get rid of any supplemental groups */ + if (!getuid() && setgroups(0, 0)) { + perror("sucap: setgroups failed"); + exit(1); + } + + /* Set gid and uid (this probably clears capabilities) */ + setregid(gid, gid); + setreuid(uid, uid); + + { + ssize_t x; + cap_t cap = cap_init(); + capgetp(0, cap); + printf("Caps: %s\n", cap_to_text(cap, &x)); + } + + printf("[debug] uid:%d, real uid:%d\n", geteuid(), getuid()); + + /* Signal child that we want our privileges updated */ + close(pipe_fds[1]); /* Child hangs in blocking read */ + + /* Wait for child process to set our privileges */ + { + int status = 0; + if (wait(&status) == -1) { + perror("sucap: wait failed"); + } + if (!WIFEXITED(status) || WEXITSTATUS(status)) { + fprintf(stderr, "sucap: child did not exit cleanly.\n"); + exit(1); + } + } + + { + ssize_t x; + cap_t cap = cap_init(); + capgetp(0, cap); + printf("Caps: %s\n", cap_to_text(cap, &x)); + } + +/* printf("[debug] uid:%d, real uid:%d\n", geteuid(), getuid()); */ + /* exec the program indicated by args 2 ... */ + execvp(argv[3], argv+3); + + /* if we fall through to here, our exec failed -- announce the fact */ + fprintf(stderr, "Unable to execute command: %s\n", strerror(errno)); + + usage(); + } else { + /* Child process */ + close(pipe_fds[1]); + + /* Wait for mother process to setuid */ + wait_on_fd(pipe_fds[0]); + + /* Set privileges on mother process */ + if (capsetp(parent_pid, old_caps)) { + perror("sucaps: capsetp"); + _exit(1); + } + + /* exit to signal mother process that we are ready */ + _exit(0); + } + + return 0; +} |