• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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