int main(int argc, char *argv[]) { cap_t caps; pid_t pid; int r; /* Create child; child commences execution in childFunc() */ printf("******* info of the parent process - start ********\n"); caps = cap_get_proc(); printf("Before unshare, the capabilities are:\n"); printf("capabilities: %s\n", cap_to_text(caps, NULL)); r = unshare(CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER ); if(r == -1) { printf("unshare failed: %s\n", strerror(errno)); exit(EXIT_FAILURE); } caps = cap_get_proc(); printf("After unshare, the capabilities are:\n"); printf("capabilities: %s\n", cap_to_text(caps, NULL)); printf("the process pid is: %ld\n", (long)getpid()); r = execlp("sh", "sh", (char *)0); if(r == -1) { printf("execlp failed: %s\n", strerror(errno)); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }
int fork_drop_and_exec(int keepperms, cap_t expected_caps) { int pid; int ret = 0; char buf[200], *p; char *capstxt; cap_t actual_caps; static int seqno; pid = fork(); if (pid < 0) tst_brkm(TFAIL | TERRNO, NULL, "%s: failed fork\n", __func__); if (pid == 0) { drop_root(keepperms); print_my_caps(); sprintf(buf, "%d", seqno); ret = execlp(TSTPATH, TSTPATH, buf, NULL); capstxt = cap_to_text(expected_caps, NULL); snprintf(buf, 200, "failed to run as %s\n", capstxt); cap_free(capstxt); write_to_fifo(buf); tst_brkm(TFAIL, NULL, "%s: exec failed\n", __func__); } else { p = buf; while (1) { int c, s; read_from_fifo(buf); c = sscanf(buf, "%d", &s); if (c == 1 && s == seqno) break; tst_resm(TINFO, "got a bad seqno (c=%d, s=%d, seqno=%d)", c, s, seqno); } p = index(buf, '.'); if (!p) tst_brkm(TFAIL, NULL, "got a bad message from print_caps\n"); p += 1; actual_caps = cap_from_text(p); if (cap_compare(actual_caps, expected_caps) != 0) { capstxt = cap_to_text(expected_caps, NULL); tst_resm(TINFO, "Expected to run as .%s., ran as .%s..\n", capstxt, p); tst_resm(TINFO, "those are not the same\n"); cap_free(capstxt); ret = -1; } cap_free(actual_caps); seqno++; } return ret; }
/** * cap_restore: * @r: capability set saved by cap_save() * * Restore the set of current capabilities specified by @r. * * Returns: whether the operation was successful. **/ void g_process_cap_restore(cap_t r) { gboolean rc; if (!process_opts.caps) return; rc = cap_set_proc(r) != -1; cap_free(r); if (!rc) { gchar *cap_text; cap_text = cap_to_text(r, NULL); msg_error("Error managing capability set, cap_set_proc returned an error", evt_tag_str("caps", cap_text), evt_tag_errno("error", errno), NULL); cap_free(cap_text); return; } return; }
static PyObject * strcaps_get_file(PyObject *self, PyObject *args) { // (int) fd / (str) path / (file) file PyObject *file; if (!PyArg_ParseTuple(args, "O", &file)) return NULL; cap_t caps; if (PyFile_Check(file)) caps = cap_get_fd(PyObject_AsFileDescriptor(file)); else if (PyInt_Check(file)) caps = cap_get_fd(PyInt_AsLong(file)); else if (PyString_Check(file)) caps = cap_get_file(PyString_AsString(file)); else if (PyUnicode_Check(file)) { PyObject *file_dec = PyUnicode_AsEncodedString( file, Py_FileSystemDefaultEncoding, "strict" ); if (file_dec == NULL) return NULL; caps = cap_get_file(PyString_AsString(file_dec)); Py_DECREF(file_dec); } else { PyErr_SetString( PyExc_TypeError, "Expecting file object, descriptor int or path string" ); return NULL; } size_t strcaps_len; char *strcaps; if (caps == NULL) { if (errno == ENODATA) { strcaps = "\0"; strcaps_len = 0; } else { PyErr_SetFromErrno(PyExc_OSError); return NULL; } } else strcaps = cap_to_text(caps, &strcaps_len); cap_free(caps); return Py_BuildValue("s#", strcaps, strcaps_len); }; // (str) caps
int main(int argc, char *argv[]) { #ifdef HAVE_LIBCAP cap_t cap = cap_get_proc(); int fd; int seqno = 0; char buf[2000]; if (argc > 1) seqno = atoi(argv[1]); if (!cap) { perror("print_caps - cap_get_proc"); exit(1); } fd = open(FIFOFILE, O_WRONLY); if (!fd) { perror("print_caps: open fifo"); exit(2); } snprintf(buf, 2000, "%d.%s", seqno, cap_to_text(cap, NULL)); write(fd, buf, strlen(buf)+1); close(fd); cap_free(cap); #endif return 0; }
static int bin_cap(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) { int ret = 0; cap_t caps; if(*argv) { unmetafy(*argv, NULL); caps = cap_from_text(*argv); if(!caps) { zwarnnam(nam, "invalid capability string"); return 1; } if(cap_set_proc(caps)) { zwarnnam(nam, "can't change capabilities: %e", errno); ret = 1; } } else { char *result = NULL; ssize_t length; caps = cap_get_proc(); if(caps) result = cap_to_text(caps, &length); if(!caps || !result) { zwarnnam(nam, "can't get capabilities: %e", errno); ret = 1; } else puts(result); } cap_free(caps); return ret; }
void debug_print_caps(char *when) { char buf[2000]; tst_resm(TINFO, "%s", when); snprintf(buf, 2000, "%s", cap_to_text(cap_get_proc(), NULL)); tst_resm(TINFO, "%s", buf); }
void print_my_caps() { cap_t cap = cap_get_proc(); char *txt = cap_to_text(cap, NULL); tst_resm(TINFO, "\ncaps are %s\n", txt); cap_free(cap); cap_free(txt); }
static void lower_my_caps(void) { struct __user_cap_header_struct caphdr = { .version = _LINUX_CAPABILITY_VERSION }; cap_user_data_t capdata; ssize_t capstrlen = 0; cap_t my_cap; char *cap_text; int capsz; (void) capget(&caphdr, NULL); switch (caphdr.version) { case _LINUX_CAPABILITY_VERSION_1: capsz = _LINUX_CAPABILITY_U32S_1; break; case _LINUX_CAPABILITY_VERSION_2: capsz = _LINUX_CAPABILITY_U32S_2; break; default: abort(); /* can't happen */ } capdata = gsh_calloc(capsz, sizeof(struct __user_cap_data_struct)); caphdr.pid = getpid(); if (capget(&caphdr, capdata) != 0) LogFatal(COMPONENT_INIT, "Failed to query capabilities for process, errno=%u", errno); /* Set the capability bitmask to remove CAP_SYS_RESOURCE */ if (capdata->effective & CAP_TO_MASK(CAP_SYS_RESOURCE)) capdata->effective &= ~CAP_TO_MASK(CAP_SYS_RESOURCE); if (capdata->permitted & CAP_TO_MASK(CAP_SYS_RESOURCE)) capdata->permitted &= ~CAP_TO_MASK(CAP_SYS_RESOURCE); if (capdata->inheritable & CAP_TO_MASK(CAP_SYS_RESOURCE)) capdata->inheritable &= ~CAP_TO_MASK(CAP_SYS_RESOURCE); if (capset(&caphdr, capdata) != 0) LogFatal(COMPONENT_INIT, "Failed to set capabilities for process, errno=%u", errno); else LogEvent(COMPONENT_INIT, "CAP_SYS_RESOURCE was successfully removed for proper quota management in FSAL"); /* Print newly set capabilities (same as what CLI "getpcaps" displays */ my_cap = cap_get_proc(); cap_text = cap_to_text(my_cap, &capstrlen); LogEvent(COMPONENT_INIT, "currenty set capabilities are: %s", cap_text); cap_free(cap_text); cap_free(my_cap); gsh_free(capdata); }
static EVTTAG * evt_tag_cap_t(const char *tag, cap_t cap) { gchar *cap_text = cap_to_text(cap, NULL); EVTTAG *evt_tag = evt_tag_str(tag, cap_text); cap_free(cap_text); return evt_tag; }
static void display_creds_and_caps(char *msg) { cap_t caps; printf("%seUID = %ld; eGID = %ld; ", msg, (long) geteuid(), (long) getegid()); caps = cap_get_proc(); printf("capabilities: %s\n", cap_to_text(caps, NULL)); }
void *chg_uid_gid(void *arg) { cap_t newcaps; cap_t mycaps; int ret; test_msg("Aux thread runs as UID: %d; GID: %d\n", getuid(), getgid()); newcaps = cap_from_text("cap_setgid,cap_setuid=+eip"); if (!newcaps) { pr_perror("Failed to get capability struct\n"); exit(1); } ret = cap_set_proc(newcaps); if (ret) { pr_perror("Failed to set capabilities for the process\n"); exit(1); } mycaps = cap_get_proc(); if (!mycaps) { pr_perror("Failed to get child thread capabilities\n"); exit_group(2); } test_msg("Child capabilities: %s\n", cap_to_text(mycaps, NULL)); test_msg("Changing UID/GID in child thread to %d:%d\n", uid, gid); ret = syscall(SYS_setresgid, gid, gid, gid); if (ret >= 0) { syscall(SYS_setresuid, uid, uid, uid); } else if (ret < 0) { pr_perror("Failed to change UID/GID\n"); exit_group(2); } gid = getgid(); uid = getuid(); test_msg("Now aux thread runs as UID: %d; GID: %d\n", uid, gid); test_msg("Child thread is waiting for main thread's signal\n"); task_waiter_complete(&t, 1); pthread_mutex_lock(&mutex); while (!done) { pthread_cond_wait(&cond, &mutex); } pthread_mutex_unlock(&mutex); test_msg("Child thread returns\n"); return NULL; }
static int child_exec(void *stuff) { cap_t caps; printf("eUID=%ld; eGID=%ld; ", (long)geteuid(), (long)getegid()); caps = cap_get_proc(); printf("capabilities: %s\n", cap_to_text(caps, NULL)); struct clone_args *args = (struct clone_args *)stuff; if (execvp(args->argv[0], args->argv) != 0) { fprintf(stderr, "failed to execvp argments %d\n", strerror(errno)); exit(-1); } // We should never reach here! exit(-1); }
static void show_capabilities(void) { cap_t caps; char *text; caps = cap_get_proc(); assert_se(caps); text = cap_to_text(caps, NULL); assert_se(text); log_info("Capabilities:%s", text); cap_free(caps); cap_free(text); }
static void dropCapability(cap_value_t capability) { cap_t caps; cap_value_t cap_list[] = { capability }; caps = cap_get_proc(); if (caps == nullptr) { Log::error("Error: cap_get_proc() failed."); std::exit(1); } char *capText = cap_to_text(caps, nullptr); Log::info("Capabilities first: " + std::string(capText)); cap_free(capText); if (cap_set_flag(caps, CAP_EFFECTIVE, sizeof(cap_list)/sizeof(cap_list[0]), cap_list, CAP_CLEAR) == -1 || cap_set_flag(caps, CAP_PERMITTED, sizeof(cap_list)/sizeof(cap_list[0]), cap_list, CAP_CLEAR) == -1) { Log::error("Error: cap_set_flag() failed."); std::exit(1); } if (cap_set_proc(caps) == -1) { Log::error("Error: cap_set_proc() failed."); std::exit(1); } capText = cap_to_text(caps, nullptr); Log::info("Capabilities now: " + std::string(capText)); cap_free(capText); cap_free(caps); }
static PyObject * strcaps_get_process(PyObject *self, PyObject *args) { // (int) pid or None int pid = 0; if (!PyArg_ParseTuple(args, "|i", &pid)) return NULL; cap_t caps; if (!pid) caps = cap_get_proc(); else caps = cap_get_pid(pid); size_t strcaps_len; char *strcaps; if (caps == NULL) { if (errno == ENODATA) { strcaps = "\0"; strcaps_len = 0; } else { PyErr_SetFromErrno(PyExc_OSError); return NULL; } } else strcaps = cap_to_text(caps, &strcaps_len); cap_free(caps); return Py_BuildValue("s#", strcaps, strcaps_len); }; // (str) caps
/** * g_process_cap_modify: * @capability: capability to turn off or on * @onoff: specifies whether the capability should be enabled or disabled * * This function modifies the current permitted set of capabilities by * enabling or disabling the capability specified in @capability. * * Returns: whether the operation was successful. **/ gboolean g_process_cap_modify(int capability, int onoff) { cap_t caps; if (!process_opts.caps) return TRUE; /* * if libcap or kernel doesn't support cap_syslog, then resort to * cap_sys_admin */ if (capability == CAP_SYSLOG && (!have_capsyslog || CAP_SYSLOG == -1)) capability = CAP_SYS_ADMIN; caps = cap_get_proc(); if (!caps) return FALSE; if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &capability, onoff) == -1) { msg_error("Error managing capability set, cap_set_flag returned an error", evt_tag_errno("error", errno), NULL); cap_free(caps); return FALSE; } if (cap_set_proc(caps) == -1) { gchar *cap_text; cap_text = cap_to_text(caps, NULL); msg_error("Error managing capability set, cap_set_proc returned an error", evt_tag_str("caps", cap_text), evt_tag_errno("error", errno), NULL); cap_free(cap_text); cap_free(caps); return FALSE; } cap_free(caps); return TRUE; }
static int /* Startup function for cloned child */ childFunc(void *arg) { cap_t caps; for (;;) { printf("eUID = %ld; eGID = %ld; ", (long) geteuid(), (long) getegid()); caps = cap_get_proc(); printf("capabilities: %s\n", cap_to_text(caps, NULL)); if (arg == NULL) break; sleep(5); } return 0; }
static PyObject * pycap_get_proc(PyObject *self, PyObject *noargs) { char *cap_str; cap_t cap; ssize_t cap_str_len; if ((cap = cap_get_proc()) == NULL) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } if ((cap_str = cap_to_text(cap, &cap_str_len)) == NULL) { PyErr_SetFromErrno(PyExc_OSError); cap_free(cap); return NULL; } cap_free(cap); return PyString_FromStringAndSize(cap_str, cap_str_len); }
static int bin_getcap(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) { int ret = 0; do { char *result = NULL; ssize_t length; cap_t caps; caps = cap_get_file(unmetafy(dupstring(*argv), NULL)); if(caps) result = cap_to_text(caps, &length); if (!caps || !result) { zwarnnam(nam, "%s: %e", *argv, errno); ret = 1; } else printf("%s %s\n", *argv, result); cap_free(caps); } while(*++argv); return ret; }
char * do_cap_get_file (const char *path) { cap_t cap; char *r, *ret; CHROOT_IN; cap = cap_get_file (path); CHROOT_OUT; if (cap == NULL) { reply_with_perror ("%s", path); return NULL; } r = cap_to_text (cap, NULL); if (r == NULL) { reply_with_perror ("cap_to_text"); cap_free (cap); return NULL; } cap_free (cap); /* 'r' is not an ordinary pointer that can be freed with free(3)! * In the current implementation of libcap, if you try to do that it * will segfault. We have to duplicate this into an ordinary * buffer, then call cap_free (r). */ ret = strdup (r); if (ret == NULL) { reply_with_perror ("strdup"); cap_free (r); return NULL; } cap_free (r); return ret; /* caller frees */ }
int main(int, char **) { cap_t cap = cap_get_proc(); const char *text = cap_to_text(cap, 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); }
static void dropCapability( #ifdef __linux cap_value_t capability #endif ) { #ifdef __linux cap_t caps; cap_value_t cap_list[] = { capability }; caps = cap_get_proc(); if (caps == nullptr) { Log::error("Error: cap_get_proc() failed."); exit(1); } if (cap_set_flag(caps, CAP_EFFECTIVE, sizeof(cap_list)/sizeof(cap_list[0]), cap_list, CAP_CLEAR) == -1 || cap_set_flag(caps, CAP_PERMITTED, sizeof(cap_list)/sizeof(cap_list[0]), cap_list, CAP_CLEAR) == -1) { Log::error("Error: cap_set_flag() failed."); exit(1); } if (cap_set_proc(caps) == -1) { Log::error("Error: cap_set_proc() failed."); exit(1); } char *capText = cap_to_text(caps, nullptr); Log::info("Capabilities now: " + std::string(capText)); cap_free(capText); cap_free(caps); #endif // We assume that on non-Linux we don't need to be root to be able to hardlink to files we // don't own, so drop root. if (geteuid() == 0 && getuid() != 0) { // The program is setuid root. Not normal on Linux where we use setcap, but if this // needs to run on non-Linux Unixes, setuid root is what it will bneed to be to be able // to do chroot(). if (setuid(getuid()) != 0) { Log::error("Error: setuid() failed."); } } #if ENABLE_DEBUG if (geteuid() == 0 && getuid() == 0) { #ifdef __linux // Argh, awful hack if (capability == CAP_FOWNER) return; #endif // Running under sudo, probably because being debugged? Let's drop super-user rights. if (uid == 0) { struct passwd *nobody = getpwnam("nobody"); if (nobody) uid = nobody->pw_uid; else uid = 65534; } if (setuid(uid) != 0) { Log::error("setuid() failed."); } } #endif }
int main(int argc, char *argv[], char *envp[]) { pid_t child; unsigned i; child = 0; for (i=1; i<argc; ++i) { if (!memcmp("--drop=", argv[i], 4)) { char *ptr; cap_t orig, raised_for_setpcap; /* * We need to do this here because --inh=XXX may have reset * orig and it isn't until we are within the --drop code that * we know what the prevailing (orig) pI value is. */ orig = cap_get_proc(); if (orig == NULL) { perror("Capabilities not available"); exit(1); } raised_for_setpcap = cap_dup(orig); if (raised_for_setpcap == NULL) { fprintf(stderr, "BSET modification requires CAP_SETPCAP\n"); exit(1); } if (cap_set_flag(raised_for_setpcap, CAP_EFFECTIVE, 1, raise_setpcap, CAP_SET) != 0) { perror("unable to select CAP_SETPCAP"); exit(1); } if (strcmp("all", argv[i]+7) == 0) { unsigned j = 0; while (CAP_IS_SUPPORTED(j)) { if (cap_drop_bound(j) != 0) { char *name_ptr; name_ptr = cap_to_name(j); fprintf(stderr, "Unable to drop bounding capability [%s]\n", name_ptr); cap_free(name_ptr); exit(1); } j++; } } else { for (ptr = argv[i]+7; (ptr = strtok(ptr, ",")); ptr = NULL) { /* find name for token */ cap_value_t cap; int status; if (cap_from_name(ptr, &cap) != 0) { fprintf(stderr, "capability [%s] is unknown to libcap\n", ptr); exit(1); } if (cap_set_proc(raised_for_setpcap) != 0) { perror("unable to raise CAP_SETPCAP for BSET changes"); exit(1); } status = prctl(PR_CAPBSET_DROP, cap); if (cap_set_proc(orig) != 0) { perror("unable to lower CAP_SETPCAP post BSET change"); exit(1); } if (status) { fprintf(stderr, "failed to drop [%s=%u]\n", ptr, cap); exit(1); } } } cap_free(raised_for_setpcap); cap_free(orig); } else if (!memcmp("--inh=", argv[i], 6)) { cap_t all, raised_for_setpcap; char *text; char *ptr; all = cap_get_proc(); if (all == NULL) { perror("Capabilities not available"); exit(1); } if (cap_clear_flag(all, CAP_INHERITABLE) != 0) { perror("libcap:cap_clear_flag() internal error"); exit(1); } raised_for_setpcap = cap_dup(all); if ((raised_for_setpcap != NULL) && (cap_set_flag(raised_for_setpcap, CAP_EFFECTIVE, 1, raise_setpcap, CAP_SET) != 0)) { cap_free(raised_for_setpcap); raised_for_setpcap = NULL; } text = cap_to_text(all, NULL); cap_free(all); if (text == NULL) { perror("Fatal error concerning process capabilities"); exit(1); } ptr = malloc(10 + strlen(argv[i]+6) + strlen(text)); if (ptr == NULL) { perror("Out of memory for inh set"); exit(1); } if (argv[i][6] && strcmp("none", argv[i]+6)) { sprintf(ptr, "%s %s+i", text, argv[i]+6); } else { strcpy(ptr, text); } all = cap_from_text(ptr); if (all == NULL) { perror("Fatal error internalizing capabilities"); exit(1); } cap_free(text); free(ptr); if (raised_for_setpcap != NULL) { /* * This is only for the case that pP does not contain * the requested change to pI.. Failing here is not * indicative of the cap_set_proc(all) failing (always). */ (void) cap_set_proc(raised_for_setpcap); cap_free(raised_for_setpcap); raised_for_setpcap = NULL; } if (cap_set_proc(all) != 0) { perror("Unable to set inheritable capabilities"); exit(1); } /* * Since status is based on orig, we don't want to restore * the previous value of 'all' again here! */ cap_free(all); } else if (!memcmp("--caps=", argv[i], 7)) { cap_t all, raised_for_setpcap; raised_for_setpcap = cap_get_proc(); if (raised_for_setpcap == NULL) { perror("Capabilities not available"); exit(1); } if ((raised_for_setpcap != NULL) && (cap_set_flag(raised_for_setpcap, CAP_EFFECTIVE, 1, raise_setpcap, CAP_SET) != 0)) { cap_free(raised_for_setpcap); raised_for_setpcap = NULL; } all = cap_from_text(argv[i]+7); if (all == NULL) { fprintf(stderr, "unable to interpret [%s]\n", argv[i]); exit(1); } if (raised_for_setpcap != NULL) { /* * This is only for the case that pP does not contain * the requested change to pI.. Failing here is not * indicative of the cap_set_proc(all) failing (always). */ (void) cap_set_proc(raised_for_setpcap); cap_free(raised_for_setpcap); raised_for_setpcap = NULL; } if (cap_set_proc(all) != 0) { fprintf(stderr, "Unable to set capabilities [%s]\n", argv[i]); exit(1); } /* * Since status is based on orig, we don't want to restore * the previous value of 'all' again here! */ cap_free(all); } else if (!memcmp("--keep=", argv[i], 7)) { unsigned value; int set; value = strtoul(argv[i]+7, NULL, 0); set = prctl(PR_SET_KEEPCAPS, value); if (set < 0) { fprintf(stderr, "prctl(PR_SET_KEEPCAPS, %u) failed: %s\n", value, strerror(errno)); exit(1); } } else if (!memcmp("--chroot=", argv[i], 9)) { int status; cap_t orig, raised_for_chroot; orig = cap_get_proc(); if (orig == NULL) { perror("Capabilities not available"); exit(1); } raised_for_chroot = cap_dup(orig); if (raised_for_chroot == NULL) { perror("Unable to duplicate capabilities"); exit(1); } if (cap_set_flag(raised_for_chroot, CAP_EFFECTIVE, 1, raise_chroot, CAP_SET) != 0) { perror("unable to select CAP_SET_SYS_CHROOT"); exit(1); } if (cap_set_proc(raised_for_chroot) != 0) { perror("unable to raise CAP_SYS_CHROOT"); exit(1); } cap_free(raised_for_chroot); status = chroot(argv[i]+9); if (cap_set_proc(orig) != 0) { perror("unable to lower CAP_SYS_CHROOT"); exit(1); } /* * Given we are now in a new directory tree, its good practice * to start off in a sane location */ status = chdir("/"); cap_free(orig); if (status != 0) { fprintf(stderr, "Unable to chroot/chdir to [%s]", argv[i]+9); exit(1); } } else if (!memcmp("--secbits=", argv[i], 10)) { unsigned value; int status; value = strtoul(argv[i]+10, NULL, 0); status = prctl(PR_SET_SECUREBITS, value); if (status < 0) { fprintf(stderr, "failed to set securebits to 0%o/0x%x\n", value, value); exit(1); } } else if (!memcmp("--forkfor=", argv[i], 10)) { unsigned value; value = strtoul(argv[i]+10, NULL, 0); if (value == 0) { goto usage; } child = fork(); if (child < 0) { perror("unable to fork()"); } else if (!child) { sleep(value); exit(0); } } else if (!memcmp("--killit=", argv[i], 9)) { int retval, status; pid_t result; unsigned value; value = strtoul(argv[i]+9, NULL, 0); if (!child) { fprintf(stderr, "no forked process to kill\n"); exit(1); } retval = kill(child, value); if (retval != 0) { perror("Unable to kill child process"); exit(1); } result = waitpid(child, &status, 0); if (result != child) { fprintf(stderr, "waitpid didn't match child: %u != %u\n", child, result); exit(1); } if (WTERMSIG(status) != value) { fprintf(stderr, "child terminated with odd signal (%d != %d)\n" , value, WTERMSIG(status)); exit(1); } } else if (!memcmp("--uid=", argv[i], 6)) { unsigned value; int status; value = strtoul(argv[i]+6, NULL, 0); status = setuid(value); if (status < 0) { fprintf(stderr, "Failed to set uid=%u: %s\n", value, strerror(errno)); exit(1); } } else if (!memcmp("--gid=", argv[i], 6)) { unsigned value; int status; value = strtoul(argv[i]+6, NULL, 0); status = setgid(value); if (status < 0) { fprintf(stderr, "Failed to set gid=%u: %s\n", value, strerror(errno)); exit(1); } } else if (!memcmp("--groups=", argv[i], 9)) { char *ptr, *buf; long length, max_groups; gid_t *group_list; int g_count; length = sysconf(_SC_GETGR_R_SIZE_MAX); buf = calloc(1, length); if (NULL == buf) { fprintf(stderr, "No memory for [%s] operation\n", argv[i]); exit(1); } max_groups = sysconf(_SC_NGROUPS_MAX); group_list = calloc(max_groups, sizeof(gid_t)); if (NULL == group_list) { fprintf(stderr, "No memory for gid list\n"); exit(1); } g_count = 0; for (ptr = argv[i] + 9; (ptr = strtok(ptr, ",")); ptr = NULL, g_count++) { if (max_groups <= g_count) { fprintf(stderr, "Too many groups specified (%d)\n", g_count); exit(1); } if (!isdigit(*ptr)) { struct group *g, grp; getgrnam_r(ptr, &grp, buf, length, &g); if (NULL == g) { fprintf(stderr, "Failed to identify gid for group [%s]\n", ptr); exit(1); } group_list[g_count] = g->gr_gid; } else { group_list[g_count] = strtoul(ptr, NULL, 0); } } free(buf); if (setgroups(g_count, group_list) != 0) { fprintf(stderr, "Failed to setgroups.\n"); exit(1); } free(group_list); } else if (!memcmp("--user="******"User [%s] not known\n", user); exit(1); } ngroups = MAX_GROUPS; status = getgrouplist(user, pwd->pw_gid, groups, &ngroups); if (status < 1) { perror("Unable to get group list for user"); exit(1); } status = setgroups(ngroups, groups); if (status != 0) { perror("Unable to set group list for user"); exit(1); } status = setgid(pwd->pw_gid); if (status < 0) { fprintf(stderr, "Failed to set gid=%u(user=%s): %s\n", pwd->pw_gid, user, strerror(errno)); exit(1); } status = setuid(pwd->pw_uid); if (status < 0) { fprintf(stderr, "Failed to set uid=%u(user=%s): %s\n", pwd->pw_uid, user, strerror(errno)); exit(1); } } else if (!memcmp("--decode=", argv[i], 9)) { unsigned long long value; unsigned cap; const char *sep = ""; /* Note, if capabilities become longer than 64-bits we'll need to fixup the following code.. */ value = strtoull(argv[i]+9, NULL, 16); printf("0x%016llx=", value); for (cap=0; (cap < 64) && (value >> cap); ++cap) { if (value & (1ULL << cap)) { char *ptr; ptr = cap_to_name(cap); if (ptr != NULL) { printf("%s%s", sep, ptr); cap_free(ptr); } else { printf("%s%u", sep, cap); } sep = ","; } } printf("\n"); } else if (!memcmp("--supports=", argv[i], 11)) {
int main(int argc, char **argv) { int tried_to_cap_setfcap = 0; char buffer[MAXCAP+1]; int retval, quiet=0, verify=0; cap_t mycaps; cap_value_t capflag; if (argc < 3) { usage(); } mycaps = cap_get_proc(); if (mycaps == NULL) { fprintf(stderr, "warning - unable to get process capabilities" " (old libcap?)\n"); } while (--argc > 0) { const char *text; cap_t cap_d; if (!strcmp(*++argv, "-q")) { quiet = 1; continue; } if (!strcmp(*argv, "-v")) { verify = 1; continue; } if (!strcmp(*argv, "-r")) { cap_d = NULL; } else { if (!strcmp(*argv,"-")) { retval = read_caps(quiet, *argv, buffer); if (retval) usage(); text = buffer; } else { text = *argv; } cap_d = cap_from_text(text); if (cap_d == NULL) { perror("fatal error"); usage(); } #ifdef DEBUG { ssize_t length; const char *result; result = cap_to_text(cap_d, &length); fprintf(stderr, "caps set to: [%s]\n", result); } #endif } if (--argc <= 0) usage(); /* * Set the filesystem capability for this file. */ if (verify) { cap_t cap_on_file; int cmp; if (cap_d == NULL) { cap_d = cap_from_text("="); } cap_on_file = cap_get_file(*++argv); if (cap_on_file == NULL) { cap_on_file = cap_from_text("="); } cmp = cap_compare(cap_on_file, cap_d); cap_free(cap_on_file); if (cmp != 0) { if (!quiet) { printf("%s differs in [%s%s%s]\n", *argv, CAP_DIFFERS(cmp, CAP_PERMITTED) ? "p" : "", CAP_DIFFERS(cmp, CAP_INHERITABLE) ? "i" : "", CAP_DIFFERS(cmp, CAP_EFFECTIVE) ? "e" : ""); } exit(1); } if (!quiet) { printf("%s: OK\n", *argv); } } else { if (!tried_to_cap_setfcap) { capflag = CAP_SETFCAP; /* * Raise the effective CAP_SETFCAP. */ if (cap_set_flag(mycaps, CAP_EFFECTIVE, 1, &capflag, CAP_SET) != 0) { perror("unable to manipulate CAP_SETFCAP - " "try a newer libcap?"); exit(1); } if (cap_set_proc(mycaps) != 0) { perror("unable to set CAP_SETFCAP effective capability"); exit(1); } tried_to_cap_setfcap = 1; } retval = cap_set_file(*++argv, cap_d); if (retval != 0) { fprintf(stderr, "Failed to set capabilities on file `%s' (%s)\n", argv[0], strerror(errno)); usage(); } } if (cap_d) { cap_free(cap_d); } } exit(0); }
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) { char *opt, *p, *str; int told = 0; int use_checklist = 0; int systemmode = 0; int suseconfig = 0; FILE *fp; char line[512]; char *part[4]; int i, pcnt, lcnt; int inpart; mode_t mode; struct perm *e; struct stat stb, stb2; struct passwd *pwd = 0; struct group *grp = 0; uid_t uid; gid_t gid; int fd, r; int errors = 0; cap_t caps = NULL; while (argc > 1) { opt = argv[1]; if (!strcmp(opt, "--")) break; if (*opt == '-' && opt[1] == '-') opt++; if (!strcmp(opt, "-system")) { argc--; argv++; systemmode = 1; continue; } // hidden option for use by suseconfig only if (!strcmp(opt, "-suseconfig")) { argc--; argv++; suseconfig = 1; systemmode = 1; continue; } if (!strcmp(opt, "-fscaps")) { argc--; argv++; have_fscaps = 1; continue; } if (!strcmp(opt, "-no-fscaps")) { argc--; argv++; have_fscaps = 0; continue; } if (!strcmp(opt, "-s") || !strcmp(opt, "-set")) { do_set=1; argc--; argv++; continue; } if (!strcmp(opt, "-warn")) { do_set=0; argc--; argv++; continue; } if (!strcmp(opt, "-n") || !strcmp(opt, "-noheader")) { told = 1; argc--; argv++; continue; } if (!strcmp(opt, "-e") || !strcmp(opt, "-examine")) { argc--; argv++; if (argc == 1) { fprintf(stderr, "examine: argument required\n"); exit(1); } add_checklist(argv[1]); use_checklist = 1; argc--; argv++; continue; } if (!strcmp(opt, "-level")) { argc--; argv++; if (argc == 1) { fprintf(stderr, "level: argument required\n"); exit(1); } force_level = argv[1]; argc--; argv++; continue; } if (!strcmp(opt, "-f") || !strcmp(opt, "-files")) { argc--; argv++; if (argc == 1) { fprintf(stderr, "files: argument required\n"); exit(1); } if ((fp = fopen(argv[1], "r")) == 0) { fprintf(stderr, "files: %s: %s\n", argv[1], strerror(errno)); exit(1); } while (readline(fp, line, sizeof(line))) { if (!*line) continue; add_checklist(line); } fclose(fp); use_checklist = 1; argc--; argv++; continue; } if (!strcmp(opt, "-r") || !strcmp(opt, "-root")) { argc--; argv++; if (argc == 1) { fprintf(stderr, "root: argument required\n"); exit(1); } root = argv[1]; rootl = strlen(root); if (*root != '/') { fprintf(stderr, "root: must begin with '/'\n"); exit(1); } argc--; argv++; continue; } if (*opt == '-') usage(!strcmp(opt, "-h") || !strcmp(opt, "-help") ? 0 : 1); break; } if (systemmode) { const char file[] = "/etc/sysconfig/security"; parse_sysconf(file); if(do_set == -1) { if (default_set < 0) { fprintf(stderr, "permissions handling disabled in %s\n", file); exit(0); } if (suseconfig && default_set) { char* module = getenv("ONLY_MODULE"); if (!module || strcmp(module, "permissions")) { puts("no permissions will be changed if not called explicitly"); default_set = 0; } } do_set = default_set; } if (force_level) { char *p = strtok(force_level, " "); do { add_level(p); } while ((p = strtok(NULL, " "))); } if (!nlevel) add_level("secure"); add_level("local"); // always add local for (i = 1; i < argc; i++) { add_checklist(argv[i]); use_checklist = 1; continue; } collect_permfiles(); } else if (argc <= 1) usage(1); else { npermfiles = argc-1; permfiles = &argv[1]; } if (have_fscaps == 1 && !check_fscaps_enabled()) { fprintf(stderr, "Warning: running kernel does not support fscaps\n"); } if (do_set == -1) do_set = 0; // add fake list entries for all files to check for (i = 0; i < nchecklist; i++) add_permlist(checklist[i], "unknown", "unknown", 0); for (i = 0; i < npermfiles; i++) { if ((fp = fopen(permfiles[i], "r")) == 0) { perror(argv[i]); exit(1); } lcnt = 0; struct perm* last = NULL; int extline; while (readline(fp, line, sizeof(line))) { extline = 0; lcnt++; if (*line == 0 || *line == '#' || *line == '$') continue; inpart = 0; pcnt = 0; for (p = line; *p; p++) { if (*p == ' ' || *p == '\t') { *p = 0; if (inpart) { pcnt++; inpart = 0; } continue; } if (pcnt == 0 && !inpart && *p == '+') { extline = 1; break; } if (!inpart) { inpart = 1; if (pcnt == 3) break; part[pcnt] = p; } } if (extline) { if (!last) { BAD_LINE(); continue; } if (!strncmp(p, "+capabilities ", 14)) { if (have_fscaps != 1) continue; p += 14; caps = cap_from_text(p); if (caps) { cap_free(last->caps); last->caps = caps; } continue; } BAD_LINE(); continue; } if (inpart) pcnt++; if (pcnt != 3) { BAD_LINE(); continue; } part[3] = part[2]; part[2] = strchr(part[1], ':'); if (!part[2]) part[2] = strchr(part[1], '.'); if (!part[2]) { BAD_LINE(); continue; } *part[2]++ = 0; mode = strtoul(part[3], part + 3, 8); if (mode > 07777 || part[3][0]) { BAD_LINE(); continue; } last = add_permlist(part[0], part[1], part[2], mode); } fclose(fp); } euid = geteuid(); for (e = permlist; e; e = e->next) { if (use_checklist && !in_checklist(e->file+rootl)) continue; if (lstat(e->file, &stb)) continue; if (S_ISLNK(stb.st_mode)) continue; if (!e->mode && !strcmp(e->owner, "unknown")) { char uids[16], gids[16]; pwd = getpwuid(stb.st_uid); grp = getgrgid(stb.st_gid); if (!pwd) sprintf(uids, "%d", stb.st_uid); if (!grp) sprintf(gids, "%d", stb.st_gid); fprintf(stderr, "%s: cannot verify %s:%s %04o - not listed in /etc/permissions\n", e->file+rootl, pwd?pwd->pw_name:uids, grp?grp->gr_name:gids, (int)(stb.st_mode&07777)); pwd = 0; grp = 0; continue; } if ((!pwd || strcmp(pwd->pw_name, e->owner)) && (pwd = getpwnam(e->owner)) == 0) { fprintf(stderr, "%s: unknown user %s\n", e->file+rootl, e->owner); continue; } if ((!grp || strcmp(grp->gr_name, e->group)) && (grp = getgrnam(e->group)) == 0) { fprintf(stderr, "%s: unknown group %s\n", e->file+rootl, e->group); continue; } uid = pwd->pw_uid; gid = grp->gr_gid; caps = cap_get_file(e->file); if (!caps) { cap_free(caps); caps = NULL; if (errno == EOPNOTSUPP) { //fprintf(stderr, "%s: fscaps not supported\n", e->file+rootl); cap_free(e->caps); e->caps = NULL; } } if (e->caps) { e->mode &= 0777; } int perm_ok = (stb.st_mode & 07777) == e->mode; int owner_ok = stb.st_uid == uid && stb.st_gid == gid; int caps_ok = 0; if (!caps && !e->caps) caps_ok = 1; else if (caps && e->caps && !cap_compare(e->caps, caps)) caps_ok = 1; if (perm_ok && owner_ok && caps_ok) continue; if (!told) { told = 1; printf("Checking permissions and ownerships - using the permissions files\n"); for (i = 0; i < npermfiles; i++) printf("\t%s\n", permfiles[i]); if (rootl) { printf("Using root %s\n", root); } } if (!do_set) printf("%s should be %s:%s %04o", e->file+rootl, e->owner, e->group, e->mode); else printf("setting %s to %s:%s %04o", e->file+rootl, e->owner, e->group, e->mode); if (!caps_ok && e->caps) { str = cap_to_text(e->caps, NULL); printf(" \"%s\"", str); cap_free(str); } printf(". (wrong"); if (!owner_ok) { pwd = getpwuid(stb.st_uid); grp = getgrgid(stb.st_gid); if (pwd) printf(" owner/group %s", pwd->pw_name); else printf(" owner/group %d", stb.st_uid); if (grp) printf(":%s", grp->gr_name); else printf(":%d", stb.st_gid); pwd = 0; grp = 0; } if (!perm_ok) printf(" permissions %04o", (int)(stb.st_mode & 07777)); if (!caps_ok) { if (!perm_ok || !owner_ok) { fputc(',', stdout); } if (caps) { str = cap_to_text(caps, NULL); printf(" capabilities \"%s\"", str); cap_free(str); } else fputs(" missing capabilities", stdout); } putchar(')'); putchar('\n'); if (!do_set) continue; fd = -1; if (S_ISDIR(stb.st_mode)) { fd = open(e->file, O_RDONLY|O_DIRECTORY|O_NONBLOCK|O_NOFOLLOW); if (fd == -1) { perror(e->file); errors++; continue; } } else if (S_ISREG(stb.st_mode)) { fd = open(e->file, O_RDONLY|O_NONBLOCK|O_NOFOLLOW); if (fd == -1) { perror(e->file); errors++; continue; } if (fstat(fd, &stb2)) continue; if (stb.st_mode != stb2.st_mode || stb.st_nlink != stb2.st_nlink || stb.st_dev != stb2.st_dev || stb.st_ino != stb2.st_ino) { fprintf(stderr, "%s: too fluctuating\n", e->file+rootl); errors++; continue; } if (stb.st_nlink > 1 && !safepath(e->file, 0, 0)) { fprintf(stderr, "%s: on an insecure path\n", e->file+rootl); errors++; continue; } else if (e->mode & 06000) { /* extra checks for s-bits */ if (!safepath(e->file, (e->mode & 02000) == 0 ? uid : 0, (e->mode & 04000) == 0 ? gid : 0)) { fprintf(stderr, "%s: will not give away s-bits on an insecure path\n", e->file+rootl); errors++; continue; } } } else if (strncmp(e->file, "/dev/", 5) != 0) // handle special files only in /dev { fprintf(stderr, "%s: don't know what to do with that type of file\n", e->file+rootl); errors++; continue; } if (euid == 0 && !owner_ok) { /* if we change owner or group of a setuid file the bit gets reset so also set perms again */ if (e->mode & 06000) perm_ok = 0; if (fd >= 0) r = fchown(fd, uid, gid); else r = chown(e->file, uid, gid); if (r) { fprintf(stderr, "%s: chown: %s\n", e->file+rootl, strerror(errno)); errors++; } if (fd >= 0) r = fstat(fd, &stb); else r = lstat(e->file, &stb); if (r) { fprintf(stderr, "%s: too fluctuating\n", e->file+rootl); errors++; continue; } } if (!perm_ok) { if (fd >= 0) r = fchmod(fd, e->mode); else r = chmod(e->file, e->mode); if (r) { fprintf(stderr, "%s: chmod: %s\n", e->file+rootl, strerror(errno)); errors++; } } if (!caps_ok) { if (fd >= 0) r = cap_set_fd(fd, e->caps); else r = cap_set_file(e->file, e->caps); if (r) { fprintf(stderr, "%s: cap_set_file: %s\n", e->file+rootl, strerror(errno)); errors++; } } if (fd >= 0) close(fd); } if (errors) { fprintf(stderr, "ERROR: not all operations were successful.\n"); exit(1); } exit(0); }
// Given the path to this program, fetch its configured capability set // (as set by `setcap ... /path/to/file`) and raise those capabilities // into the Ambient set. static int make_caps_ambient(const char *selfPath) { cap_t caps = cap_get_file(selfPath); if(!caps) { if(getenv(wrapperDebug)) fprintf(stderr, "no caps set or could not retrieve the caps for this file, not doing anything..."); return 1; } // We use `cap_to_text` and iteration over the tokenized result // string because, as of libcap's current release, there is no // facility for retrieving an array of `cap_value_t`'s that can be // given to `prctl` in order to lift that capability into the // Ambient set. // // Some discussion was had around shot-gunning all of the // capabilities we know about into the Ambient set but that has a // security smell and I deemed the risk of the current // implementation crashing the program to be lower than the risk // of a privilege escalation security hole being introduced by // raising all capabilities, even ones we didn't intend for the // program, into the Ambient set. // // `cap_t` which is returned by `cap_get_*` is an opaque type and // even if we could retrieve the bitmasks (which, as far as I can // tell we cannot) in order to get the `cap_value_t` // representation for each capability we would have to take the // total number of capabilities supported and iterate over the // sequence of integers up-to that maximum total, testing each one // against the bitmask ((bitmask >> n) & 1) to see if it's set and // aggregating each "capability integer n" that is set in the // bitmask. // // That, combined with the fact that we can't easily get the // bitmask anyway seemed much more brittle than fetching the // `cap_t`, transforming it into a textual representation, // tokenizing the string, and using `cap_from_name` on the token // to get the `cap_value_t` that we need for `prctl`. There is // indeed risk involved if the output string format of // `cap_to_text` ever changes but at this time the combination of // factors involving the below list have led me to the conclusion // that the best implementation at this time is reading then // parsing with *lots of documentation* about why we're doing it // this way. // // 1. No explicit API for fetching an array of `cap_value_t`'s or // for transforming a `cap_t` into such a representation // 2. The risk of a crash is lower than lifting all capabilities // into the Ambient set // 3. libcap is depended on heavily in the Linux ecosystem so // there is a high chance that the output representation of // `cap_to_text` will not change which reduces our risk that // this parsing step will cause a crash // // The preferred method, should it ever be available in the // future, would be to use libcap API's to transform the result // from a `cap_get_*` into an array of `cap_value_t`'s that can // then be given to prctl. // // - Parnell ssize_t capLen; char* capstr = cap_to_text(caps, &capLen); cap_free(caps); // TODO: For now, we assume that cap_to_text always starts its // result string with " =" and that the first capability is listed // immediately after that. We should verify this. assert(capLen >= 2); capstr += 2; char* saveptr = NULL; for(char* tok = strtok_r(capstr, ",", &saveptr); tok; tok = strtok_r(NULL, ",", &saveptr)) { cap_value_t capnum; if (cap_from_name(tok, &capnum)) { if(getenv(wrapperDebug)) fprintf(stderr, "cap_from_name failed, skipping: %s", tok); } else if (capnum == CAP_SETPCAP) { // Check for the cap_setpcap capability, we set this on the // wrapper so it can elevate the capabilities to the Ambient // set but we do not want to propagate it down into the // wrapped program. // // TODO: what happens if that's the behavior you want // though???? I'm preferring a strict vs. loose policy here. if(getenv(wrapperDebug)) fprintf(stderr, "cap_setpcap in set, skipping it\n"); } else { set_ambient_cap(capnum); if(getenv(wrapperDebug)) fprintf(stderr, "raised %s into the Ambient capability set\n", tok); } } cap_free(capstr); return 0; }