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_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; }
int main(int argc, char **argv) { cap_t old_caps; uid_t uid; pid_t pid, parent_pid; gid_t gid; int pipe_fds[2]; /* this program should not be made setuid-0 */ if (getuid() && !geteuid()) { usage(); } /* check that we have at least 3 arguments */ if (argc < 4) { usage(); } /* Convert username to uid */ { struct passwd *pw = getpwnam(argv[1]); if (!pw) { fprintf(stderr, "sucap: No such user: %s\n", argv[1]); exit(1); } uid = pw->pw_uid; } /* Convert groupname to gid */ { struct group *gr = getgrnam(argv[2]); if (!gr) { fprintf(stderr, "sucap: No such group: %s\n", argv[2]); exit(1); } gid = gr->gr_gid; } /* set process group to current pid */ if (setpgid(0, getpid())) { perror("sucap: Failed to set process group"); exit(1); } if (pipe(pipe_fds)) { perror("sucap: pipe() failed"); exit(1); } parent_pid = getpid(); old_caps = cap_init(); if (capgetp(0, old_caps)) { perror("sucap: capgetp"); exit(1); } { ssize_t x; printf("Caps: %s\n", cap_to_text(old_caps, &x)); } /* fork off a child to do the hard work */ fflush(NULL); pid = fork(); if (pid == -1) { perror("sucap: fork failed"); exit(1); } /* 1. mother process sets gid and uid * 2. child process sets capabilities of mother process * 3. mother process execs whatever is to be executed */ if (pid) { /* Mother process. */ close(pipe_fds[0]); /* Get rid of any supplemental groups */ if (!getuid() && setgroups(0, 0)) { perror("sucap: setgroups failed"); exit(1); } /* Set gid and uid (this probably clears capabilities) */ setregid(gid, gid); setreuid(uid, uid); { ssize_t x; cap_t cap = cap_init(); capgetp(0, cap); printf("Caps: %s\n", cap_to_text(cap, &x)); } printf("[debug] uid:%d, real uid:%d\n", geteuid(), getuid()); /* Signal child that we want our privileges updated */ close(pipe_fds[1]); /* Child hangs in blocking read */ /* Wait for child process to set our privileges */ { int status = 0; if (wait(&status) == -1) { perror("sucap: wait failed"); } if (!WIFEXITED(status) || WEXITSTATUS(status)) { fprintf(stderr, "sucap: child did not exit cleanly.\n"); exit(1); } } { ssize_t x; cap_t cap = cap_init(); capgetp(0, cap); printf("Caps: %s\n", cap_to_text(cap, &x)); } /* printf("[debug] uid:%d, real uid:%d\n", geteuid(), getuid()); */ /* exec the program indicated by args 2 ... */ execvp(argv[3], argv+3); /* if we fall through to here, our exec failed -- announce the fact */ fprintf(stderr, "Unable to execute command: %s\n", strerror(errno)); usage(); } else { /* Child process */ close(pipe_fds[1]); /* Wait for mother process to setuid */ wait_on_fd(pipe_fds[0]); /* Set privileges on mother process */ if (capsetp(parent_pid, old_caps)) { perror("sucaps: capsetp"); _exit(1); } /* exit to signal mother process that we are ready */ _exit(0); } 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); }