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