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