|
- /* Copyright (C) 2002 Fernando Lopez-Lezcano
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- Jackstart is based on code and concepts found in sucap.c, written by
- Finn Arne Gangstad <finnag@guardian.no> and givertcap.c, written by
- Tommi Ilmonen, Tommi.Ilmonen@hut.fi
-
- */
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <errno.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <pwd.h>
- #include <grp.h>
- #include <unistd.h>
- #include <sys/wait.h>
- #include <errno.h>
- #include <string.h>
-
- #include <config.h>
-
- #undef _POSIX_SOURCE
- #include <sys/capability.h>
-
- #include "jack/start.h"
- #include "md5.h"
- #include "jack_md5.h"
-
- #define READ_BLOCKSIZE 4096
-
- /* JACK_LOCATION must be passed on the gcc command line */
- static char *jackd_bin_path = JACK_LOCATION "/jackd";
-
- static char *jackd_md5_sum = JACKD_MD5_SUM;
-
-
- static int check_capabilities (void)
- {
- cap_t caps = cap_init ();
- cap_flag_value_t cap;
- pid_t pid;
- int have_all_caps = 1;
-
- if (caps == NULL) {
- fprintf (stderr, "jackstart: could not allocate capability working storage\n");
- return 0;
- }
- pid = getpid ();
- cap_clear (caps);
- if (capgetp (pid, caps)) {
- fprintf (stderr, "jackstart: could not get capabilities for process %d\n", pid);
- return 0;
- }
- /* check that we are able to give capabilites to other processes */
- cap_get_flag (caps, CAP_SETPCAP, CAP_EFFECTIVE, &cap);
- if (cap == CAP_CLEAR) {
- have_all_caps = 0;
- goto done;
- }
- /* check that we have the capabilities we want to transfer */
- cap_get_flag (caps, CAP_SYS_NICE, CAP_EFFECTIVE, &cap);
- if (cap == CAP_CLEAR) {
- have_all_caps = 0;
- goto done;
- }
- cap_get_flag (caps, CAP_SYS_RESOURCE, CAP_EFFECTIVE, &cap);
- if (cap == CAP_CLEAR) {
- have_all_caps = 0;
- goto done;
- }
- cap_get_flag (caps, CAP_IPC_LOCK, CAP_EFFECTIVE, &cap);
- if (cap == CAP_CLEAR) {
- have_all_caps = 0;
- goto done;
- }
- done:
- cap_free (caps);
- return have_all_caps;
- }
-
-
- static int give_capabilities (pid_t pid)
- {
- cap_t caps = cap_init ();
- const unsigned caps_size = 4;
- cap_value_t cap_list[] =
- { CAP_SETPCAP, CAP_SYS_NICE, CAP_SYS_RESOURCE, CAP_IPC_LOCK };
-
- if (caps == NULL) {
- fprintf (stderr, "jackstart: could not allocate capability working storage\n");
- return -1;
- }
- cap_clear (caps);
- if (capgetp (pid, caps)) {
- fprintf (stderr, "jackstart: could not get capabilities for process %d\n", pid);
- cap_clear (caps);
- }
- cap_set_flag (caps, CAP_EFFECTIVE, caps_size, cap_list, CAP_SET);
- cap_set_flag (caps, CAP_INHERITABLE, caps_size, cap_list, CAP_SET);
- cap_set_flag (caps, CAP_PERMITTED, caps_size, cap_list, CAP_SET);
- if (capsetp (pid, caps)) {
- fprintf (stderr, "jackstart: could not give capabilities: %s\n", strerror (errno));
- cap_free (caps);
- return -1;
- }
- cap_free (caps);
- return 0;
- }
-
- static int check_binary (const char *binpath)
- {
- struct stat status;
- FILE *binstream;
-
- if (lstat (jackd_bin_path, &status)) {
- fprintf (stderr, "jackstart: could not stat %s: %s\n",
- binpath, strerror (errno));
- return -1;
- }
- if (!(S_ISREG (status.st_mode))) {
- fprintf (stderr, "jackstart: %s is not a regular file\n",
- binpath);
- return -1;
- }
- if (status.st_uid != 0) {
- fprintf (stderr, "jackstart: %s is not owned by root\n",
- binpath);
- return -1;
- }
- if ((status.st_mode & 022) != 0) {
- fprintf (stderr,
- "jackstart: %s mode %o writeable by non-root users\n",
- binpath, status.st_mode & 07777);
- return -1;
- }
- if ((binstream = fopen (binpath, "r")) == NULL) {
- fprintf (stderr, "jackstart: can't open %s for reading: %s\n",
- binpath, strerror (errno));
- return -1;
- } else {
- /* md5sum the executable file, check man evp for more details */
- size_t sum;
- md5_t ctx;
- char buffer[READ_BLOCKSIZE + 72];
- unsigned char md_value[MD5_SIZE];
- char md_string[3];
- int i, j;
-
- md5_init (&ctx);
- while (1) {
- size_t n;
- sum = 0;
- do {
- n = fread (buffer + sum, 1, READ_BLOCKSIZE - sum, binstream);
- sum += n;
- } while (sum < READ_BLOCKSIZE && n != 0);
- if (n == 0 && ferror (binstream)) {
- fprintf (stderr, "jackstart: error while reading %s: %s\n", binpath, strerror (errno));
- return -1;
- }
- if (n == 0) {
- break;
- }
- md5_process (&ctx, buffer, READ_BLOCKSIZE);
- }
- if (sum > 0) {
- md5_process (&ctx, buffer, sum);
- }
- if (fclose (binstream)) {
- fprintf (stderr, "jackstart: could not close %s after reading: %s\n", binpath, strerror (errno));
- }
- md5_finish (&ctx, md_value);
- for (i = 0, j = 0; i < sizeof(md_value); i++, j += 2) {
- sprintf (md_string, "%02x", md_value[i]);
- if (md_string[0] != jackd_md5_sum[j] ||
- md_string[1] != jackd_md5_sum[j + 1]) {
- fprintf (stderr, "jackstart: md5 checksum for %s does not match\n", binpath);
- return -1;
- }
- }
- }
- return 0;
- }
-
- int main (int argc, char **argv)
- {
- uid_t uid, euid;
- pid_t pid, parent_pid;
- gid_t gid;
- int pipe_fds[2];
- int err;
-
- parent_pid = getpid ();
-
- /* get real user and group ids, effective user id */
- uid = getuid ();
- gid = getgid ();
- euid = geteuid ();
-
- /* are we running suid root? */
- if (uid != 0) {
- if (euid != 0) {
- fprintf (stderr, "jackstart: not running suid root, can't use capabilities\n");
- fprintf (stderr, " (currently running with uid=%d and euid=%d),\n", uid, euid);
- fprintf (stderr, " make jackstart suid root or start jackd directly\n\n");
- }
- }
- /* see if we can get the required capabilities */
- if (check_capabilities () == 0) {
- size_t size;
- cap_t cap = cap_init ();
- capgetp (0, cap);
- fprintf (stderr, "jackstart: cannot get realtime capabilities, current capabilities are:\n");
- fprintf (stderr, " %s\n", cap_to_text (cap, &size));
- fprintf (stderr, " probably running under a kernel with capabilities disabled,\n");
- fprintf (stderr, " a suitable kernel would have printed something like \"=eip\"\n\n");
- }
-
- /* check the executable, owner, permissions, md5 checksum */
- if (check_binary (jackd_bin_path)) {
- exit (1);
- }
-
- /* set process group to current pid */
- if (setpgid (0, getpid ())) {
- fprintf (stderr, "jackstart: failed to set process group: %s\n",
- strerror (errno));
- exit (1);
- }
-
- /* create pipe to synchronize with jackd */
- if (pipe (pipe_fds)) {
- fprintf (stderr, "jackstart: could not create pipe: %s\n",
- strerror (errno));
- exit (1);
- }
-
- /* make sure the file descriptors are the right ones,
- otherwise dup them, this is to make sure that both
- jackstart and jackd use the same fds
- */
- if (pipe_fds[0] != PIPE_READ_FD) {
- if (dup2 (pipe_fds[0], PIPE_READ_FD) != PIPE_READ_FD) {
- fprintf (stderr, "jackstart: could not dup pipe read file descriptor: %s\n",
- strerror (errno));
- exit (1);
- }
- }
- if (pipe_fds[1] != PIPE_WRITE_FD) {
- if (dup2 (pipe_fds[1], PIPE_WRITE_FD) != PIPE_WRITE_FD) {
- fprintf (stderr, "jackstart: could not dup pipe write file descriptor: %s\n",
- strerror (errno));
- exit (1);
- }
- }
- /* fork off a child to wait for jackd to start */
- fflush (NULL);
- pid = fork ();
- if (pid == -1) {
- fprintf (stderr, "jackstart: fork failed\n");
- exit (1);
- }
- if (pid) {
- /* mother process: drops privileges, execs jackd */
- close (PIPE_READ_FD);
-
- /* get rid of any supplemental groups */
- if (!getuid () && setgroups (0, 0)) {
- fprintf (stderr, "jackstart: setgroups failed: %s\n", strerror (errno));
- exit (1);
- }
-
- /* set gid and uid */
- setregid (gid, gid);
- setreuid (uid, uid);
- execvp (jackd_bin_path, argv);
-
- /* we could not start jackd, clean up and exit */
- fprintf (stderr, "jackstart: unable to execute %s: %s\n", jackd_bin_path, strerror (errno));
- close (PIPE_WRITE_FD);
- wait (&err);
- exit (1);
- } else {
- /* child process: grants privileges to jackd */
- close (PIPE_WRITE_FD);
-
- /* wait for jackd to start */
- while (1) {
- int ret;
- char c;
-
- /* picking up pipe closure is a tricky business.
- this seems to work as well as anything else.
- */
-
- ret = read (PIPE_READ_FD, &c, 1);
- fprintf (stderr, "back from read, ret = %d errno == %s\n", ret, strerror (errno));
- if (ret == 1) {
- break;
- } else if (errno != EINTR) {
- break;
- }
- }
-
- /* set privileges on jackd process */
- give_capabilities (parent_pid);
- }
- exit (0);
- }
|