Home | History | Annotate | Download | only in old
      1 /*
      2  * $Id: sucap.c,v 1.1.1.1 1999/04/17 22:16:31 morgan Exp $
      3  *
      4  * This was written by Finn Arne Gangstad <finnag (at) guardian.no>
      5  *
      6  * This is a program that is intended to exec a subsequent program.
      7  * The purpose of this 'sucap' wrapper is to change uid but keep all
      8  * privileges. All environment variables are inherited.
      9  */
     10 
     11 #include <sys/types.h>
     12 #include <errno.h>
     13 #include <stdio.h>
     14 #undef _POSIX_SOURCE
     15 #include <sys/capability.h>
     16 #include <pwd.h>
     17 #define __USE_BSD
     18 #include <grp.h>
     19 #include <unistd.h>
     20 #include <sys/wait.h>
     21 #include <errno.h>
     22 #include <string.h>
     23 #include <stdlib.h>
     24 
     25 static void usage(void)
     26 {
     27     fprintf(stderr,
     28 "usage: sucap <user> <group> <command-path> [command-args...]\n\n"
     29 "  This program is a wrapper that change UID but not privileges of a\n"
     30 "  program to be executed.\n"
     31 "  Note, this wrapper is intended to assist in overcoming a lack of support\n"
     32 "  for filesystem capability attributes and should be used to launch other\n"
     33 "  files. This program should _NOT_ be made setuid-0.\n\n"
     34 "[Copyright (c) 1998 Finn Arne Gangstad <finnag (at) guardian.no>]\n");
     35 
     36     exit(1);
     37 }
     38 
     39 
     40 static void
     41 wait_on_fd(int fd)
     42 {
     43     /* Wait until some data is available on a file descriptor, or until
     44      * end of file or an error is detected */
     45     char buf[1];
     46     while (read(fd, buf, sizeof(buf)) == -1 && errno == EINTR) {
     47 	/* empty loop */
     48     }
     49 }
     50 
     51 
     52 int main(int argc, char **argv)
     53 {
     54     cap_t old_caps;
     55     uid_t uid;
     56     pid_t pid, parent_pid;
     57     gid_t gid;
     58     int pipe_fds[2];
     59 
     60     /* this program should not be made setuid-0 */
     61     if (getuid() && !geteuid()) {
     62         usage();
     63     }
     64 
     65     /* check that we have at least 3 arguments */
     66     if (argc < 4) {
     67         usage();
     68     }
     69 
     70     /* Convert username to uid */
     71     {
     72 	struct passwd *pw = getpwnam(argv[1]);
     73 	if (!pw) {
     74 	    fprintf(stderr, "sucap: No such user: %s\n", argv[1]);
     75 	    exit(1);
     76 	}
     77 	uid = pw->pw_uid;
     78     }
     79 
     80     /* Convert groupname to gid */
     81     {
     82 	struct group *gr = getgrnam(argv[2]);
     83 	if (!gr) {
     84 	    fprintf(stderr, "sucap: No such group: %s\n", argv[2]);
     85 	    exit(1);
     86 	}
     87 	gid = gr->gr_gid;
     88     }
     89 
     90     /* set process group to current pid */
     91     if (setpgid(0, getpid())) {
     92 	perror("sucap: Failed to set process group");
     93 	exit(1);
     94     }
     95 
     96     if (pipe(pipe_fds)) {
     97 	perror("sucap: pipe() failed");
     98 	exit(1);
     99     }
    100 
    101     parent_pid = getpid();
    102 
    103     old_caps = cap_init();
    104     if (capgetp(0, old_caps)) {
    105 	perror("sucap: capgetp");
    106 	exit(1);
    107     }
    108 
    109     {
    110 	ssize_t x;
    111 	printf("Caps: %s\n", cap_to_text(old_caps, &x));
    112     }
    113 
    114 
    115     /* fork off a child to do the hard work */
    116     fflush(NULL);
    117     pid = fork();
    118     if (pid == -1) {
    119 	perror("sucap: fork failed");
    120 	exit(1);
    121     }
    122 
    123     /* 1. mother process sets gid and uid
    124      * 2. child process sets capabilities of mother process
    125      * 3. mother process execs whatever is to be executed
    126      */
    127 
    128     if (pid) {
    129 	/* Mother process. */
    130 	close(pipe_fds[0]);
    131 
    132 	/* Get rid of any supplemental groups */
    133 	if (!getuid() && setgroups(0, 0)) {
    134 	    perror("sucap: setgroups failed");
    135 	    exit(1);
    136 	}
    137 
    138 	/* Set gid and uid (this probably clears capabilities) */
    139 	setregid(gid, gid);
    140 	setreuid(uid, uid);
    141 
    142 	{
    143 	    ssize_t x;
    144 	    cap_t cap = cap_init();
    145 	    capgetp(0, cap);
    146 	    printf("Caps: %s\n", cap_to_text(cap, &x));
    147 	}
    148 
    149 	printf("[debug] uid:%d, real uid:%d\n", geteuid(), getuid());
    150 
    151 	/* Signal child that we want our privileges updated */
    152 	close(pipe_fds[1]); /* Child hangs in blocking read */
    153 
    154 	/* Wait for child process to set our privileges */
    155 	{
    156 	    int status = 0;
    157 	    if (wait(&status) == -1) {
    158 		perror("sucap: wait failed");
    159 	    }
    160 	    if (!WIFEXITED(status) || WEXITSTATUS(status)) {
    161 		fprintf(stderr, "sucap: child did not exit cleanly.\n");
    162 		exit(1);
    163 	    }
    164 	}
    165 
    166 	{
    167 	    ssize_t x;
    168 	    cap_t cap = cap_init();
    169 	    capgetp(0, cap);
    170 	    printf("Caps: %s\n", cap_to_text(cap, &x));
    171 	}
    172 
    173 /*	printf("[debug] uid:%d, real uid:%d\n", geteuid(), getuid()); */
    174 	/* exec the program indicated by args 2 ... */
    175 	execvp(argv[3], argv+3);
    176 
    177 	/* if we fall through to here, our exec failed -- announce the fact */
    178 	fprintf(stderr, "Unable to execute command: %s\n", strerror(errno));
    179 
    180 	usage();
    181     } else {
    182 	/* Child process */
    183 	close(pipe_fds[1]);
    184 
    185 	/* Wait for mother process to setuid */
    186 	wait_on_fd(pipe_fds[0]);
    187 
    188 	/* Set privileges on mother process */
    189 	if (capsetp(parent_pid, old_caps)) {
    190 	    perror("sucaps: capsetp");
    191 	    _exit(1);
    192 	}
    193 
    194 	/* exit to signal mother process that we are ready */
    195 	_exit(0);
    196     }
    197 
    198     return 0;
    199 }
    200