1 /* Copyright (C) 2002 Fernando Lopez-Lezcano
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.
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.
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.
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
23 #include <sys/types.h>
38 #include <sys/capability.h>
40 #include "jack/start.h"
44 #define READ_BLOCKSIZE 4096
46 /* JACK_LOCATION must be passed on the gcc command line */
47 static char *jackd_bin_path
= JACK_LOCATION
"/jackd";
49 static char *jackd_md5_sum
= JACKD_MD5_SUM
;
52 static int check_capabilities (void)
54 cap_t caps
= cap_init();
57 int have_all_caps
= 1;
60 fprintf (stderr
, "jackstart: could not allocate capability working storage\n");
65 if (capgetp (pid
, caps
)) {
66 fprintf (stderr
, "jackstart: could not get capabilities for process %d\n", pid
);
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
) {
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
) {
81 cap_get_flag(caps
, CAP_SYS_RESOURCE
, CAP_EFFECTIVE
, &cap
);
82 if (cap
== CAP_CLEAR
) {
86 cap_get_flag(caps
, CAP_IPC_LOCK
, CAP_EFFECTIVE
, &cap
);
87 if (cap
== CAP_CLEAR
) {
97 static int give_capabilities (pid_t pid
)
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
} ;
105 fprintf (stderr
, "jackstart: could not allocate capability working storage\n");
109 if (capgetp (pid
, caps
)) {
110 fprintf (stderr
, "jackstart: could not get capabilities for process %d\n", pid
);
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
));
125 static int check_binary (const char *binpath
)
130 if (lstat(jackd_bin_path
, &status
)) {
131 fprintf (stderr
, "jackstart: could not stat %s: %s\n",
132 binpath
, strerror(errno
));
135 if (!(S_ISREG(status
.st_mode
))) {
136 fprintf (stderr
, "jackstart: %s is not a regular file\n",
140 if (status
.st_uid
!= 0) {
141 fprintf (stderr
, "jackstart: %s is not owned by root\n",
145 if ((status
.st_mode
& 022) != 0) {
147 "jackstart: %s mode %o writeable by non-root users\n",
148 binpath
, status
.st_mode
& 07777);
151 if ((binstream
= fopen (binpath
, "r")) == NULL
) {
152 fprintf (stderr
, "jackstart: can't open %s for reading: %s\n",
153 binpath
, strerror(errno
));
156 /* md5sum the executable file, check man evp for more details */
159 char buffer
[READ_BLOCKSIZE
+ 72];
160 unsigned char md_value
[MD5_SIZE
];
169 n
= fread (buffer
+ sum
, 1, READ_BLOCKSIZE
- sum
, binstream
);
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
));
179 md5_process(&ctx
, buffer
, READ_BLOCKSIZE
);
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
));
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
);
199 int main(int argc
, char **argv
)
202 pid_t pid
, parent_pid
;
207 parent_pid
= getpid ();
209 /* get real user and group ids, effective user id */
214 /* are we running suid root? */
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");
222 /* see if we can get the required capabilities */
223 if (check_capabilities () == 0) {
225 cap_t cap
= cap_init();
227 fprintf (stderr
, "jackstart: cannot get realtime capabilities, current capabilities are:\n");
228 fprintf (stderr
, " %s\n", cap_to_text(cap
, &size
));
229 fprintf (stderr
, " probably running under a kernel with capabilities disabled,\n");
230 fprintf (stderr
, " a suitable kernel would have printed something like \"=eip\"\n\n");
233 /* check the executable, owner, permissions, md5 checksum */
234 if (check_binary(jackd_bin_path
)) {
238 /* set process group to current pid */
239 if (setpgid (0, getpid())) {
240 fprintf (stderr
, "jackstart: failed to set process group: %s\n",
245 /* create pipe to synchronize with jackd */
246 if (pipe (pipe_fds
)) {
247 fprintf (stderr
, "jackstart: could not create pipe: %s\n",
252 /* make sure the file descriptors are the right ones,
253 otherwise dup them, this is to make sure that both
254 jackstart and jackd use the same fds
256 if (pipe_fds
[0] != PIPE_READ_FD
) {
257 if (dup2 (pipe_fds
[0], PIPE_READ_FD
) != PIPE_READ_FD
) {
258 fprintf (stderr
, "jackstart: could not dup pipe read file descriptor: %s\n",
263 if (pipe_fds
[1] != PIPE_WRITE_FD
) {
264 if (dup2(pipe_fds
[1], PIPE_WRITE_FD
)!=PIPE_WRITE_FD
) {
265 fprintf (stderr
, "jackstart: could not dup pipe write file descriptor: %s\n",
270 /* fork off a child to wait for jackd to start */
274 fprintf (stderr
, "jackstart: fork failed\n");
278 /* mother process: drops privileges, execs jackd */
281 /* get rid of any supplemental groups */
282 if (!getuid () && setgroups (0, 0)) {
283 fprintf (stderr
, "jackstart: setgroups failed: %s\n", strerror(errno
));
287 /* set gid and uid */
290 execvp(jackd_bin_path
, argv
);
292 /* we could not start jackd, clean up and exit */
293 fprintf(stderr
, "jackstart: unable to execute %s: %s\n", jackd_bin_path
, strerror(errno
));
294 close (PIPE_WRITE_FD
);
298 /* child process: grants privileges to jackd */
299 close(PIPE_WRITE_FD
);
301 /* wait for jackd to start */
306 /* picking up pipe closure is a tricky business.
307 this seems to work as well as anything else.
310 ret
= read(PIPE_READ_FD
, &c
, 1);
311 fprintf (stderr
, "back from read, ret = %d errno == %s\n", ret
, strerror (errno
));
314 } else if (errno
!= EINTR
) {
319 /* set privileges on jackd process */
320 give_capabilities (parent_pid
);