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@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
usage(void)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@guardian.no>]\n");
35
36 exit(1);
37 }
38
39
40 static void
wait_on_fd(int fd)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
main(int argc,char ** argv)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