** Warning: Cannot open xref database.
1 /* Copyright (C) 2002 Fernando Lopez-Lezcano
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16
17 Jackstart is based on code and concepts found in sucap.c, written by
18 Finn Arne Gangstad <finnag@guardian.no> and givertcap.c, written by
19 Tommi Ilmonen, Tommi.Ilmonen@hut.fi
20
21 */
22
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <pwd.h>
29 #include <grp.h>
30 #include <unistd.h>
31 #include <sys/wait.h>
32 #include <errno.h>
33 #include <string.h>
34
35 #include <config.h>
36
37 #undef _POSIX_SOURCE
38 #include <sys/capability.h>
39
40 #include "jack/start.h"
41 #include "md5.h"
42 #include "jack_md5.h"
43
44 #define READ_BLOCKSIZE 4096
45
46 /* JACKD_LOCATION must be passed on the gcc command line */
47 static char *jackd_bin_path = JACKD_LOCATION;
48
49 static char *jackd_md5_sum = JACKD_MD5_SUM;
50
51
52 static int check_capabilities (void)
53 {
54 cap_t caps = cap_init();
55 cap_flag_value_t cap;
56 pid_t pid;
57 int have_all_caps = 1;
58
59 if (caps == NULL) {
60 fprintf (stderr, "jackstart: could not allocate capability working storage\n");
61 return 0;
62 }
63 pid = getpid ();
64 cap_clear (caps);
65 if (capgetp (pid, caps)) {
66 fprintf (stderr, "jackstart: could not get capabilities for process %d\n", pid);
67 return 0;
68 }
69 /* check that we are able to give capabilites to other processes */
70 cap_get_flag(caps, CAP_SETPCAP, CAP_EFFECTIVE, &cap);
71 if (cap == CAP_CLEAR) {
72 have_all_caps = 0;
73 goto done;
74 }
75 /* check that we have the capabilities we want to transfer */
76 cap_get_flag(caps, CAP_SYS_NICE, CAP_EFFECTIVE, &cap);
77 if (cap == CAP_CLEAR) {
78 have_all_caps = 0;
79 goto done;
80 }
81 cap_get_flag(caps, CAP_SYS_RESOURCE, CAP_EFFECTIVE, &cap);
82 if (cap == CAP_CLEAR) {
83 have_all_caps = 0;
84 goto done;
85 }
86 cap_get_flag(caps, CAP_IPC_LOCK, CAP_EFFECTIVE, &cap);
87 if (cap == CAP_CLEAR) {
88 have_all_caps = 0;
89 goto done;
90 }
91 done:
92 cap_free (caps);
93 return have_all_caps;
94 }
95
96
97 static int give_capabilities (pid_t pid)
98 {
99 cap_t caps = cap_init();
100 const unsigned caps_size = 4;
101 cap_value_t cap_list[] =
102 { CAP_SETPCAP, CAP_SYS_NICE, CAP_SYS_RESOURCE, CAP_IPC_LOCK} ;
103
104 if (caps == NULL) {
105 fprintf (stderr, "jackstart: could not allocate capability working storage\n");
106 return -1;
107 }
108 cap_clear(caps);
109 if (capgetp (pid, caps)) {
110 fprintf (stderr, "jackstart: could not get capabilities for process %d\n", pid);
111 cap_clear(caps);
112 }
113 cap_set_flag(caps, CAP_EFFECTIVE, caps_size, cap_list , CAP_SET);
114 cap_set_flag(caps, CAP_INHERITABLE, caps_size, cap_list , CAP_SET);
115 cap_set_flag(caps, CAP_PERMITTED, caps_size, cap_list , CAP_SET);
116 if (capsetp (pid, caps)) {
117 fprintf (stderr, "jackstart: could not give capabilities: %s\n", strerror (errno));
118 cap_free (caps);
119 return -1;
120 }
121 cap_free (caps);
122 return 0;
123 }
124
125 static int check_binary (const char *binpath)
126 {
127 struct stat status;
128 FILE *binstream;
129
130 if (lstat(jackd_bin_path, &status)) {
131 fprintf (stderr, "jackstart: could not stat %s: %s\n",
132 binpath, strerror(errno));
133 return -1;
134 }
135 if (!(S_ISREG(status.st_mode))) {
136 fprintf (stderr, "jackstart: %s is not a regular file\n",
137 binpath);
138 return -1;
139 }
140 if (status.st_uid != 0) {
141 fprintf (stderr, "jackstart: %s is not owned by root\n",
142 binpath);
143 return -1;
144 }
145 if ((status.st_mode & 022) != 0) {
146 fprintf (stderr,
147 "jackstart: %s mode %o writeable by non-root users\n",
148 binpath, status.st_mode & 07777);
149 return -1;
150 }
151 if ((binstream = fopen (binpath, "r")) == NULL) {
152 fprintf (stderr, "jackstart: can't open %s for reading: %s\n",
153 binpath, strerror(errno));
154 return -1;
155 } else {
156 /* md5sum the executable file, check man evp for more details */
157 size_t sum;
158 md5_t ctx;
159 char buffer[READ_BLOCKSIZE + 72];
160 unsigned char md_value[MD5_SIZE];
161 char md_string[3];
162 int i, j;
163
164 md5_init(&ctx);
165 while (1) {
166 size_t n;
167 sum = 0;
168 do {
169 n = fread (buffer + sum, 1, READ_BLOCKSIZE - sum, binstream);
170 sum += n;
171 } while (sum < READ_BLOCKSIZE && n != 0);
172 if (n == 0 && ferror (binstream)) {
173 fprintf (stderr, "jackstart: error while reading %s: %s\n", binpath, strerror(errno));
174 return -1;
175 }
176 if (n == 0) {
177 break;
178 }
179 md5_process(&ctx, buffer, READ_BLOCKSIZE);
180 }
181 if (sum > 0)
182 md5_process(&ctx, buffer, sum);
183 if (fclose (binstream)) {
184 fprintf (stderr, "jackstart: could not close %s after reading: %s\n", binpath, strerror(errno));
185 }
186 md5_finish(&ctx, md_value);
187 for(i = 0, j = 0; i < sizeof(md_value); i++, j+=2) {
188 sprintf(md_string, "%02x", md_value[i]);
189 if (md_string[0] != jackd_md5_sum[j] ||
190 md_string[1] != jackd_md5_sum[j+1]) {
191 fprintf (stderr, "jackstart: md5 checksum for %s does not match\n", binpath);
192 return -1;
193 }
194 }
195 }
196 return 0;
197 }
198
199 int main(int argc, char **argv)
200 {
201 uid_t uid, euid;
202 pid_t pid, parent_pid;
203 gid_t gid;
204 int pipe_fds[2];
205 int err;
206
207 parent_pid = getpid ();
208
209 /* get real user and group ids, effective user id */
210 uid = getuid ();
211 gid = getgid ();
212 euid = geteuid ();
213
214 /* are we running suid root? */
215 if (uid != 0) {
216 if (euid != 0) {
217 fprintf (stderr, "jackstart: not running suid root, can't use capabilities\n");
218 fprintf (stderr, " (currently running with uid=%d and euid=%d),\n", uid, euid);
219 fprintf (stderr, " make jackstart suid root or start jackd directly\n\n");
220 exit (1);
221 }
222 }
223 /* see if we can get the required capabilities */
224 if (check_capabilities () == 0) {
225 size_t size;
226 cap_t cap = cap_init();
227 capgetp(0, cap);
228 fprintf (stderr, "jackstart: cannot get realtime capabilities, current capabilities are:\n");
229 fprintf (stderr, " %s\n", cap_to_text(cap, &size));
230 fprintf (stderr, " probably running under a kernel with capabilities disabled,\n");
231 fprintf (stderr, " a suitable kernel would have printed something like \"=eip\"\n\n");
232 exit (1);
233 }
234
235 /* check the executable, owner, permissions, md5 checksum */
236 if (check_binary(jackd_bin_path)) {
237 exit(1);
238 }
239
240 /* set process group to current pid */
241 if (setpgid (0, getpid())) {
242 fprintf (stderr, "jackstart: failed to set process group: %s\n",
243 strerror(errno));
244 exit (1);
245 }
246
247 /* create pipe to synchronize with jackd */
248 if (pipe (pipe_fds)) {
249 fprintf (stderr, "jackstart: could not create pipe: %s\n",
250 strerror(errno));
251 exit (1);
252 }
253
254 /* make sure the file descriptors are the right ones, otherwise dup them,
255 this is to make sure that both jackstart and jackd use the same fds
256 */
257 if (pipe_fds[0] != PIPE_READ_FD) {
258 if (dup2 (pipe_fds[0], PIPE_READ_FD) != PIPE_READ_FD) {
259 fprintf (stderr, "jackstart: could not dup pipe read file descriptor: %s\n",
260 strerror(errno));
261 exit (1);
262 }
263 }
264 if (pipe_fds[1] != PIPE_WRITE_FD) {
265 if (dup2(pipe_fds[1], PIPE_WRITE_FD)!=PIPE_WRITE_FD) {
266 fprintf (stderr, "jackstart: could not dup pipe write file descriptor: %s\n",
267 strerror(errno));
268 exit (1);
269 }
270 }
271 /* fork off a child to wait for jackd to start */
272 fflush(NULL);
273 pid = fork();
274 if (pid == -1) {
275 fprintf (stderr, "jackstart: fork failed\n");
276 exit (1);
277 }
278 if (pid) {
279 /* mother process: drops privileges, execs jackd */
280 close(PIPE_READ_FD);
281
282 /* get rid of any supplemental groups */
283 if (!getuid () && setgroups (0, 0)) {
284 fprintf (stderr, "jackstart: setgroups failed: %s\n", strerror(errno));
285 exit (1);
286 }
287
288 /* set gid and uid */
289 setregid(gid, gid);
290 setreuid(uid, uid);
291 execvp(jackd_bin_path, argv);
292
293 /* we could not start jackd, clean up and exit */
294 fprintf(stderr, "jackstart: unable to execute %s: %s\n", jackd_bin_path, strerror(errno));
295 close (PIPE_WRITE_FD);
296 wait (&err);
297 exit (1);
298 } else {
299 /* child process: grants privileges to jackd */
300 close(PIPE_WRITE_FD);
301
302 /* wait for jackd to start */
303 while (1) {
304 int ret;
305 char c;
306
307 /* picking up pipe closure is a tricky business.
308 this seems to work as well as anything else.
309 */
310
311 ret = read(PIPE_READ_FD, &c, 1);
312 fprintf (stderr, "back from read, ret = %d errno == %s\n", ret, strerror (errno));
313 if (ret == 1) {
314 break;
315 } else if (errno != EINTR) {
316 break;
317 }
318 }
319
320 /* set privileges on jackd process */
321 if (give_capabilities (parent_pid) != 0) {
322 exit (2);
323 }
324 }
325 exit (0);
326 }
327
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.