/** * Open the libaudit fd if appropriate. */ void bus_audit_init (BusContext *context) { #ifdef HAVE_LIBAUDIT int i; capng_get_caps_process (); /* Work around a bug in libcap-ng < 0.7.7: it leaks a fd, which isn't * close-on-exec. Assume it will be one of the first few fds. */ for (i = 3; i < 42; i++) _dbus_fd_set_close_on_exec (i); if (!capng_have_capability (CAPNG_EFFECTIVE, CAP_AUDIT_WRITE)) return; audit_fd = audit_open (); if (audit_fd < 0) { int e = errno; /* If kernel doesn't support audit, bail out */ if (e == EINVAL || e == EPROTONOSUPPORT || e == EAFNOSUPPORT) return; bus_context_log (context, DBUS_SYSTEM_LOG_WARNING, "Failed to open connection to the audit subsystem: %s", _dbus_strerror (e)); } #endif /* HAVE_LIBAUDIT */ }
static void log_callback (const char *fmt, ...) { va_list ap; va_start(ap, fmt); #ifdef HAVE_LIBAUDIT if (audit_fd >= 0) { capng_get_caps_process(); if (capng_have_capability(CAPNG_EFFECTIVE, CAP_AUDIT_WRITE)) { char buf[PATH_MAX*2]; /* FIXME: need to change this to show real user */ vsnprintf(buf, sizeof(buf), fmt, ap); audit_log_user_avc_message(audit_fd, AUDIT_USER_AVC, buf, NULL, NULL, NULL, getuid()); return; } } #endif /* HAVE_LIBAUDIT */ vsyslog (LOG_USER | LOG_INFO, fmt, ap); va_end(ap); }
INTERNAL_QUAL int rtos_task_check_scheduler(int* scheduler) { #ifdef ORO_OS_LINUX_CAP_NG if(capng_get_caps_process()) { log(Error) << "Failed to retrieve capabilities (lowering to SCHED_OTHER)." <<endlog(); *scheduler = SCHED_OTHER; return -1; } #endif if (*scheduler != SCHED_OTHER && geteuid() != 0 #ifdef ORO_OS_LINUX_CAP_NG && capng_have_capability(CAPNG_EFFECTIVE, CAP_SYS_NICE)==0 #endif ) { // they're not root and they want a real-time priority, which _might_ // be acceptable if they're using pam_limits and have set the rtprio ulimit // (see "/etc/security/limits.conf" and "ulimit -a") struct rlimit r; if ((0 != getrlimit(RLIMIT_RTPRIO, &r)) || (0 == r.rlim_cur)) { log(Warning) << "Lowering scheduler type to SCHED_OTHER for non-privileged users.." <<endlog(); *scheduler = SCHED_OTHER; return -1; } } if (*scheduler != SCHED_OTHER && *scheduler != SCHED_FIFO && *scheduler != SCHED_RR ) { log(Error) << "Unknown scheduler type." <<endlog(); *scheduler = SCHED_OTHER; return -1; } return 0; }
int main(int argc, char **argv) { const char *atsec = ""; /* * Be careful just in case a setgid or setcapped copy of this * helper gets out. */ if (argc != 5) ksft_exit_fail_msg("wrong argc\n"); #ifdef HAVE_GETAUXVAL if (getauxval(AT_SECURE)) atsec = " (AT_SECURE is set)"; else atsec = " (AT_SECURE is not set)"; #endif capng_get_caps_process(); if (capng_have_capability(CAPNG_EFFECTIVE, CAP_NET_BIND_SERVICE) != bool_arg(argv, 1)) { ksft_print_msg("Wrong effective state%s\n", atsec); return 1; } if (capng_have_capability(CAPNG_PERMITTED, CAP_NET_BIND_SERVICE) != bool_arg(argv, 2)) { ksft_print_msg("Wrong permitted state%s\n", atsec); return 1; } if (capng_have_capability(CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE) != bool_arg(argv, 3)) { ksft_print_msg("Wrong inheritable state%s\n", atsec); return 1; } if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != bool_arg(argv, 4)) { ksft_print_msg("Wrong ambient state%s\n", atsec); return 1; } ksft_print_msg("%s: Capabilities after execve were correct\n", "validate_cap:"); return 0; }
int secure_capng_have_capability(capng_type_t which, unsigned int capability) { auto ret = int { capng_have_capability(which,capability) }; bool fail = (ret == CAPNG_FAIL); bool badval = ! ( (ret==0) || (ret==1) ); if (fail||badval) { std::ostringstream oss; oss<<"Error: " << (fail ? "FAILED":"") << " " << (badval ? "BAD-VALUE":"") << " (ret="<<ret<<") in " << __func__ << " for which="<<which<<" capability="<<capability<<"."; throw capmodpp_error(oss.str()); } return ret; }
INTERNAL_QUAL int rtos_task_check_priority(int* scheduler, int* priority) { int ret = 0; // check scheduler first. ret = rtos_task_check_scheduler(scheduler); // correct priority if (*scheduler == SCHED_OTHER) { if ( *priority != 0 ) { if (*priority != LowestPriority) log(Warning) << "Forcing priority ("<<*priority<<") of thread with SCHED_OTHER policy to 0." <<endlog(); *priority = 0; ret = -1; } } else { // SCHED_FIFO/SCHED_RR: if (*priority <= 0){ log(Warning) << "Forcing priority ("<<*priority<<") of thread with !SCHED_OTHER policy to 1." <<endlog(); *priority = 1; ret = -1; } if (*priority > 99){ log(Warning) << "Forcing priority ("<<*priority<<") of thread with !SCHED_OTHER policy to 99." <<endlog(); *priority = 99; ret = -1; } // and limit them according to pam_Limits (only if not root) if ( geteuid() != 0 #ifdef ORO_OS_LINUX_CAP_NG && !capng_have_capability(CAPNG_EFFECTIVE, CAP_SYS_NICE) #endif ) { struct rlimit r; if (0 == getrlimit(RLIMIT_RTPRIO, &r)) { if (*priority > (int)r.rlim_cur) { log(Warning) << "Forcing priority ("<<*priority<<") of thread with !SCHED_OTHER policy to the pam_limit of " << r.rlim_cur <<endlog(); *priority = r.rlim_cur; ret = -1; } } else { // this should not be possible, but do something intelligent *priority = 1; ret = -1; } } } return ret; }
static int has_cap(enum cap_type which, unsigned int i) { switch (which) { case CAP_TYPE_EFFECTIVE: case CAP_TYPE_BOUNDING: case CAP_TYPE_INHERITABLE: case CAP_TYPE_PERMITTED: return capng_have_capability((capng_type_t)which, i); case CAP_TYPE_AMBIENT: return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, (unsigned long) i, 0UL, 0UL); default: warnx(_("invalid capability type")); return -1; } }
/* Returns the number of capabilities printed. */ static int print_caps(FILE *f, capng_type_t which) { int i, n = 0, max = real_cap_last_cap(); for (i = 0; i <= max; i++) { if (capng_have_capability(which, i)) { const char *name = capng_capability_to_name(i); if (n) fputc(',', f); if (name) fputs(name, f); else /* cap-ng has very poor handling of * CAP_LAST_CAP changes. This is the * best we can do. */ printf("cap_%d", i); n++; } } return n; }
/*! \brief POSIX 1003.1e capabilities. */ static void setup_capabilities(void) { #ifdef HAVE_CAP_NG_H /* Drop all capabilities. */ if (capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP)) { capng_clear(CAPNG_SELECT_BOTH); /* Retain ability to set capabilities and FS access. */ capng_type_t tp = CAPNG_EFFECTIVE|CAPNG_PERMITTED; capng_update(CAPNG_ADD, tp, CAP_SETPCAP); capng_update(CAPNG_ADD, tp, CAP_DAC_OVERRIDE); capng_update(CAPNG_ADD, tp, CAP_CHOWN); /* Storage ownership. */ /* Allow binding to privileged ports. * (Not inheritable) */ capng_update(CAPNG_ADD, tp, CAP_NET_BIND_SERVICE); /* Allow setuid/setgid. */ capng_update(CAPNG_ADD, tp, CAP_SETUID); capng_update(CAPNG_ADD, tp, CAP_SETGID); /* Allow priorities changing. */ capng_update(CAPNG_ADD, tp, CAP_SYS_NICE); /* Apply. */ if (capng_apply(CAPNG_SELECT_BOTH) < 0) { log_error("failed to set process capabilities (%s)", strerror(errno)); } } else { log_info("user UID %d is not allowed to set capabilities, " "skipping", getuid()); } #endif /* HAVE_CAP_NG_H */ }
int main(int argc, char *argv[]) { char *unikernel; enum { QEMU, KVM, UKVM, UNIX } hypervisor; if (argc < 3) { fprintf(stderr, "usage: runner HYPERVISOR UNIKERNEL [ ARGS... ]\n"); fprintf(stderr, "HYPERVISOR: qemu | kvm | ukvm | unix\n"); return 1; } if (strcmp(argv[1], "qemu") == 0) hypervisor = QEMU; else if (strcmp(argv[1], "kvm") == 0) hypervisor = KVM; else if (strcmp(argv[1], "ukvm") == 0) hypervisor = UKVM; else if (strcmp(argv[1], "unix") == 0) hypervisor = UNIX; else { warnx("error: Invalid hypervisor: %s", argv[1]); return 1; } unikernel = argv[2]; /* * Remaining arguments are to be passed on to the unikernel. */ argv += 3; argc -= 3; /* * Check we have CAP_NET_ADMIN. */ if (capng_get_caps_process() != 0) { warnx("error: capng_get_caps_process() failed"); return 1; } if (!capng_have_capability(CAPNG_EFFECTIVE, CAP_NET_ADMIN)) { warnx("error: CAP_NET_ADMIN is required"); return 1; } /* * Connect to netlink, load link cache from kernel. */ struct nl_sock *sk; struct nl_cache *link_cache; int err; sk = nl_socket_alloc(); assert(sk); err = nl_connect(sk, NETLINK_ROUTE); if (err < 0) { warnx("nl_connect() failed: %s", nl_geterror(err)); return 1; } err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache); if (err < 0) { warnx("rtnl_link_alloc_cache() failed: %s", nl_geterror(err)); return 1; } /* * Retrieve container network configuration -- IP address and * default gateway. */ struct rtnl_link *l_veth; l_veth = rtnl_link_get_by_name(link_cache, VETH_LINK_NAME); if (l_veth == NULL) { warnx("error: Could not get link information for %s", VETH_LINK_NAME); return 1; } struct nl_addr *veth_addr; err = get_link_inet_addr(sk, l_veth, &veth_addr); if (err) { warnx("error: Unable to determine IP address of %s", VETH_LINK_NAME); return 1; } struct nl_addr *gw_addr; err = get_default_gw_inet_addr(sk, &gw_addr); if (err) { warnx("error: get_deGfault_gw_inet_addr() failed"); return 1; } if (gw_addr == NULL) { warnx("error: No default gateway found. This is currently " "not supported"); return 1; } /* * Create bridge and tap interface, enslave veth and tap interfaces to * bridge. */ err = create_bridge_link(sk, BRIDGE_LINK_NAME); if (err < 0) { warnx("create_bridge_link(%s) failed: %s", BRIDGE_LINK_NAME, nl_geterror(err)); return 1; } int tap_fd; if (hypervisor == UKVM) err = create_tap_link(TAP_LINK_NAME, &tap_fd); else err = create_tap_link(TAP_LINK_NAME, NULL); if (err != 0) { warnx("create_tap_link(%s) failed: %s", TAP_LINK_NAME, strerror(err)); return 1; } /* Refill link cache with newly-created interfaces */ nl_cache_refill(sk, link_cache); struct rtnl_link *l_bridge; l_bridge = rtnl_link_get_by_name(link_cache, BRIDGE_LINK_NAME); if (l_bridge == NULL) { warnx("error: Could not get link information for %s", BRIDGE_LINK_NAME); return 1; } struct rtnl_link *l_tap; l_tap = rtnl_link_get_by_name(link_cache, TAP_LINK_NAME); if (l_tap == NULL) { warnx("error: Could not get link information for %s", TAP_LINK_NAME); return 1; } err = rtnl_link_enslave(sk, l_bridge, l_veth); if (err < 0) { warnx("error: Unable to enslave %s to %s: %s", VETH_LINK_NAME, BRIDGE_LINK_NAME, nl_geterror(err)); return 1; } err = rtnl_link_enslave(sk, l_bridge, l_tap); if (err < 0) { warnx("error: Unable to enslave %s to %s: %s", TAP_LINK_NAME, BRIDGE_LINK_NAME, nl_geterror(err)); return 1; } /* * Flush all IPv4 addresses from the veth interface. This is now safe * as we are good to commit and have retrieved the existing configuration. */ struct rtnl_addr *flush_addr; flush_addr = rtnl_addr_alloc(); assert(flush_addr); rtnl_addr_set_ifindex(flush_addr, rtnl_link_get_ifindex(l_veth)); rtnl_addr_set_family(flush_addr, AF_INET); rtnl_addr_set_local(flush_addr, veth_addr); err = rtnl_addr_delete(sk, flush_addr, 0); if (err < 0) { warnx("error: Could not flush addresses on %s: %s", VETH_LINK_NAME, nl_geterror(err)); return 1; } rtnl_addr_put(flush_addr); /* * Bring up the tap and bridge interfaces. */ struct rtnl_link *l_up; l_up = rtnl_link_alloc(); assert(l_up); /* You'd think set_operstate was the thing to do here. It's not. */ rtnl_link_set_flags(l_up, IFF_UP); err = rtnl_link_change(sk, l_tap, l_up, 0); if (err < 0) { warnx("error: rtnl_link_change(%s, UP) failed: %s", TAP_LINK_NAME, nl_geterror(err)); return 1; } err = rtnl_link_change(sk, l_bridge, l_up, 0); if (err < 0) { warnx("error: rtnl_link_change(%s, UP) failed: %s", BRIDGE_LINK_NAME, nl_geterror(err)); return 1; } rtnl_link_put(l_up); /* * Collect network configuration data. */ char ip[AF_INET_BUFSIZE]; if (inet_ntop(AF_INET, nl_addr_get_binary_addr(veth_addr), ip, sizeof ip) == NULL) { perror("inet_ntop()"); return 1; } char uarg_ip[AF_INET_BUFSIZE]; unsigned int prefixlen = nl_addr_get_prefixlen(veth_addr); snprintf(uarg_ip, sizeof uarg_ip, "%s/%u", ip, prefixlen); char uarg_gw[AF_INET_BUFSIZE]; if (inet_ntop(AF_INET, nl_addr_get_binary_addr(gw_addr), uarg_gw, sizeof uarg_gw) == NULL) { perror("inet_ntop()"); return 1; } /* * Build unikernel and hypervisor arguments. */ ptrvec* uargpv = pvnew(); char *uarg_buf; /* * QEMU/KVM: * /usr/bin/qemu-system-x86_64 <qemu args> -kernel <unikernel> -append "<unikernel args>" */ if (hypervisor == QEMU || hypervisor == KVM) { pvadd(uargpv, "/usr/bin/qemu-system-x86_64"); pvadd(uargpv, "-nodefaults"); pvadd(uargpv, "-no-acpi"); pvadd(uargpv, "-display"); pvadd(uargpv, "none"); pvadd(uargpv, "-serial"); pvadd(uargpv, "stdio"); pvadd(uargpv, "-m"); pvadd(uargpv, "512"); if (hypervisor == KVM) { pvadd(uargpv, "-enable-kvm"); pvadd(uargpv, "-cpu"); pvadd(uargpv, "host"); } else { /* * Required for AESNI use in Mirage. */ pvadd(uargpv, "-cpu"); pvadd(uargpv, "Westmere"); } pvadd(uargpv, "-device"); char *guest_mac = generate_mac(); assert(guest_mac); err = asprintf(&uarg_buf, "virtio-net-pci,netdev=n0,mac=%s", guest_mac); assert(err != -1); pvadd(uargpv, uarg_buf); pvadd(uargpv, "-netdev"); err = asprintf(&uarg_buf, "tap,id=n0,ifname=%s,script=no,downscript=no", TAP_LINK_NAME); assert(err != -1); pvadd(uargpv, uarg_buf); pvadd(uargpv, "-kernel"); pvadd(uargpv, unikernel); pvadd(uargpv, "-append"); /* * TODO: Replace any occurences of ',' with ',,' in -append, because * QEMU arguments are insane. */ char cmdline[1024]; char *cmdline_p = cmdline; size_t cmdline_free = sizeof cmdline; for (; *argv; argc--, argv++) { size_t alen = snprintf(cmdline_p, cmdline_free, "%s%s", *argv, (argc > 1) ? " " : ""); if (alen >= cmdline_free) { warnx("error: Command line too long"); return 1; } cmdline_free -= alen; cmdline_p += alen; } size_t alen = snprintf(cmdline_p, cmdline_free, "--ipv4=%s --ipv4-gateway=%s", uarg_ip, uarg_gw); if (alen >= cmdline_free) { warnx("error: Command line too long"); return 1; } pvadd(uargpv, cmdline); } /* * UKVM: * /unikernel/ukvm <ukvm args> <unikernel> -- <unikernel args> */ else if (hypervisor == UKVM) { pvadd(uargpv, "/unikernel/ukvm"); err = asprintf(&uarg_buf, "--net=@%d", tap_fd); assert(err != -1); pvadd(uargpv, uarg_buf); pvadd(uargpv, "--"); pvadd(uargpv, unikernel); for (; *argv; argc--, argv++) { pvadd(uargpv, *argv); } err = asprintf(&uarg_buf, "--ipv4=%s", uarg_ip); assert(err != -1); pvadd(uargpv, uarg_buf); err = asprintf(&uarg_buf, "--ipv4-gateway=%s", uarg_gw); assert(err != -1); pvadd(uargpv, uarg_buf); } /* * UNIX: * <unikernel> <unikernel args> */ else if (hypervisor == UNIX) { pvadd(uargpv, unikernel); err = asprintf(&uarg_buf, "--interface=%s", TAP_LINK_NAME); assert(err != -1); pvadd(uargpv, uarg_buf); for (; *argv; argc--, argv++) { pvadd(uargpv, *argv); } err = asprintf(&uarg_buf, "--ipv4=%s", uarg_ip); assert(err != -1); pvadd(uargpv, uarg_buf); err = asprintf(&uarg_buf, "--ipv4-gateway=%s", uarg_gw); assert(err != -1); pvadd(uargpv, uarg_buf); } char **uargv = (char **)pvfinal(uargpv); /* * Done with netlink, free all resources and close socket. */ rtnl_link_put(l_veth); rtnl_link_put(l_bridge); rtnl_link_put(l_tap); nl_addr_put(veth_addr); nl_addr_put(gw_addr); nl_cache_free(link_cache); nl_close(sk); nl_socket_free(sk); /* * Drop all capabilities except CAP_NET_BIND_SERVICE. */ capng_clear(CAPNG_SELECT_BOTH); capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED | CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE); if (capng_apply(CAPNG_SELECT_BOTH) != 0) { warnx("error: Could not drop capabilities"); return 1; } /* * Run the unikernel. */ err = execv(uargv[0], uargv); warn("error: execv() of %s failed", uargv[0]); return 1; }
static void bump_cap(unsigned int cap) { if (capng_have_capability(CAPNG_PERMITTED, cap)) capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, cap); }
/*! * \brief Thread entrypoint function. * * When a thread is created and started, it immediately enters this function. * Depending on thread state, it either enters runnable or * blocks until it is awakened. * * This function also handles "ThreadIdle" state to quickly suspend and resume * threads and mitigate thread creation costs. Also, thread runnable may * be changed to alter the thread behavior on runtime */ static void *thread_ep(void *data) { // Check data dthread_t *thread = (dthread_t *)data; if (thread == 0) { return 0; } // Check if is a member of unit dt_unit_t *unit = thread->unit; if (unit == 0) { return 0; } // Unblock SIGALRM sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGALRM); pthread_sigmask(SIG_UNBLOCK, &mask, NULL); rcu_register_thread(); dbg_dt("dthreads: [%p] entered ep\n", thread); /* Drop capabilities except FS access. */ #ifdef HAVE_CAP_NG_H if (capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP)) { capng_type_t tp = CAPNG_EFFECTIVE|CAPNG_PERMITTED; capng_clear(CAPNG_SELECT_BOTH); capng_update(CAPNG_ADD, tp, CAP_DAC_OVERRIDE); capng_apply(CAPNG_SELECT_BOTH); } #endif /* HAVE_CAP_NG_H */ // Run loop for (;;) { // Check thread state lock_thread_rw(thread); if (thread->state == ThreadDead) { dbg_dt("dthreads: [%p] marked as dead\n", thread); unlock_thread_rw(thread); break; } // Update data thread->data = thread->_adata; runnable_t _run = thread->run; // Start runnable if thread is marked Active if ((thread->state == ThreadActive) && (thread->run != 0)) { unlock_thread_rw(thread); dbg_dt("dthreads: [%p] entering runnable\n", thread); _run(thread); dbg_dt("dthreads: [%p] exited runnable\n", thread); } else { unlock_thread_rw(thread); } // If the runnable was cancelled, start new iteration lock_thread_rw(thread); if (thread->state & ThreadCancelled) { dbg_dt("dthreads: [%p] cancelled\n", thread); thread->state &= ~ThreadCancelled; unlock_thread_rw(thread); continue; } unlock_thread_rw(thread); // Runnable finished without interruption, mark as Idle pthread_mutex_lock(&unit->_notify_mx); lock_thread_rw(thread); if (thread->state & ThreadActive) { thread->state &= ~ThreadActive; thread->state |= ThreadIdle; } // Go to sleep if idle if (thread->state & ThreadIdle) { unlock_thread_rw(thread); // Signalize state change unit_signalize_change(unit); // Wait for notification from unit dbg_dt("dthreads: [%p] going idle\n", thread); pthread_cond_wait(&unit->_notify, &unit->_notify_mx); pthread_mutex_unlock(&unit->_notify_mx); dbg_dt("dthreads: [%p] resumed from idle\n", thread); } else { unlock_thread_rw(thread); pthread_mutex_unlock(&unit->_notify_mx); } } // Thread destructor if (thread->destruct) { dbg_dt("dthreads: [%p] entering destructor\n", thread); thread->destruct(thread); dbg_dt("dthreads: [%p] exited destructor\n", thread); } // Report thread state change dbg_dt("dthreads: [%p] thread finished\n", thread); unit_signalize_change(unit); dbg_dt("dthreads: [%p] thread exited ep\n", thread); lock_thread_rw(thread); thread->state |= ThreadJoinable; unlock_thread_rw(thread); rcu_unregister_thread(); // Return return 0; }
/** * Changes the user and group the bus is running as. * * @param user the user to become * @param error return location for errors * @returns #FALSE on failure */ dbus_bool_t _dbus_change_to_daemon_user (const char *user, DBusError *error) { dbus_uid_t uid; dbus_gid_t gid; DBusString u; _dbus_string_init_const (&u, user); if (!_dbus_get_user_id_and_primary_group (&u, &uid, &gid)) { dbus_set_error (error, DBUS_ERROR_FAILED, "User '%s' does not appear to exist?", user); return FALSE; } /* If we were root */ if (_dbus_geteuid () == 0) { int rc; int have_audit_write; have_audit_write = capng_have_capability (CAPNG_PERMITTED, CAP_AUDIT_WRITE); capng_clear (CAPNG_SELECT_BOTH); /* Only attempt to retain CAP_AUDIT_WRITE if we had it when * starting. See: * https://bugs.freedesktop.org/show_bug.cgi?id=49062#c9 */ if (have_audit_write) capng_update (CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_AUDIT_WRITE); rc = capng_change_id (uid, gid, CAPNG_DROP_SUPP_GRP); if (rc) { switch (rc) { default: dbus_set_error (error, DBUS_ERROR_FAILED, "Failed to drop capabilities: %s\n", _dbus_strerror (errno)); break; case -4: dbus_set_error (error, _dbus_error_from_errno (errno), "Failed to set GID to %lu: %s", gid, _dbus_strerror (errno)); break; case -5: _dbus_warn ("Failed to drop supplementary groups: %s\n", _dbus_strerror (errno)); break; case -6: dbus_set_error (error, _dbus_error_from_errno (errno), "Failed to set UID to %lu: %s", uid, _dbus_strerror (errno)); break; case -7: dbus_set_error (error, _dbus_error_from_errno (errno), "Failed to unset keep-capabilities: %s\n", _dbus_strerror (errno)); break; } return FALSE; } } return TRUE; }
static void report(void) { int rc, escalated = 0, need_comma = 0; uid_t uid, euid, suid; gid_t gid, egid, sgid; // Refresh what we have for capabilities if (capng_get_caps_process()) { printf("Error getting capabilities\n"); exit(1); } // Check user credentials getresuid(&uid, &euid, &suid); getresgid(&gid, &egid, &sgid); if (no_child) { if ((uid != euid && uid != 0) || capng_have_capability(CAPNG_EFFECTIVE, CAP_SETUID)) { printf("Attempting to regain root..."); setuid(0); getresuid(&uid, &euid, &suid); if (uid == 0) { printf("SUCCESS - PRIVILEGE ESCALATION POSSIBLE\n"); setgid(0); getresgid(&gid, &egid, &sgid); escalated = 1; } else printf("FAILED\n"); } printf("Child "); } printf("User credentials uid:%d euid:%d suid:%d\n", uid, euid, suid); if (no_child) printf("Child "); printf("Group credentials gid:%d egid:%d sgid:%d\n", gid, egid, sgid); if (uid != euid || gid != egid) printf("Note: app has mismatching credentials!!\n"); // Check capabilities if (text) { if (capng_have_capabilities(CAPNG_SELECT_CAPS) == CAPNG_NONE) { if (no_child) printf("Child capabilities: none\n"); else printf("Current capabilities: none\n"); } else { if (no_child) printf("Child "); printf("Effective: "); capng_print_caps_text(CAPNG_PRINT_STDOUT, CAPNG_EFFECTIVE); printf("\n"); if (no_child) printf("Child "); printf("Permitted: "); capng_print_caps_text(CAPNG_PRINT_STDOUT, CAPNG_PERMITTED); printf("\n"); if (no_child) printf("Child "); printf("Inheritable: "); capng_print_caps_text(CAPNG_PRINT_STDOUT, CAPNG_INHERITABLE); printf("\n"); if (no_child) printf("Child "); printf("Bounding Set: "); capng_print_caps_text(CAPNG_PRINT_STDOUT, CAPNG_BOUNDING_SET); printf("\n"); } } else { if (capng_have_capabilities(CAPNG_SELECT_CAPS) == CAPNG_NONE) { if (no_child) printf("Child capabilities: none\n"); else printf("Current capabilities: none\n"); } else { if (no_child) printf("Child capabilities:\n"); capng_print_caps_numeric(CAPNG_PRINT_STDOUT, CAPNG_SELECT_BOTH); } } // Now check securebits flags #ifdef PR_SET_SECUREBITS if (no_child) printf("Child "); printf("securebits flags: "); rc = prctl(PR_GET_SECUREBITS, 1 << SECURE_NOROOT); if (rc & (1 << SECURE_NOROOT)) { printf("NOROOT"); need_comma = 1; } rc = prctl(PR_GET_SECUREBITS, 1 << SECURE_NOROOT_LOCKED); if (rc & (1 << SECURE_NOROOT_LOCKED)) { if (need_comma) printf(", "); printf("NOROOT_LOCKED"); need_comma = 1; } rc = prctl(PR_GET_SECUREBITS, 1 << SECURE_NO_SETUID_FIXUP); if (rc & (1 << SECURE_NO_SETUID_FIXUP)) { if (need_comma) printf(", "); printf("NO_SETUID_FIXUP"); need_comma = 1; } rc = prctl(PR_GET_SECUREBITS, 1 << SECURE_NO_SETUID_FIXUP_LOCKED); if (rc & (1 << SECURE_NO_SETUID_FIXUP_LOCKED)) { if (need_comma) printf(", "); printf("NO_SETUID_FIXUP_LOCKED"); need_comma = 1; } if (need_comma == 0) printf("none"); printf("\n"); #endif // Now do child process checks if (no_child == 0 || escalated) { printf("Attempting direct access to shadow..."); if (access("/etc/shadow", R_OK) == 0) printf("SUCCESS\n"); else printf("FAILED (%s)\n", strerror(errno)); } if (no_child == 0) { printf("Attempting to access shadow by child process..."); rc = system("cat /etc/shadow > /dev/null 2>&1"); if (rc == 0) printf("SUCCESS\n"); else printf("FAILED\n"); if (text) system("/usr/bin/captest --no-child --text"); else system("/usr/bin/captest --no-child"); } }
int main(void) { int rc, i, len, last = get_last_cap(); char *text; void *saved; puts("Doing basic bit tests..."); capng_clear(CAPNG_SELECT_BOTH); if (capng_have_capabilities(CAPNG_SELECT_BOTH) != CAPNG_NONE) { puts("Failed clearing capabilities"); abort(); } saved = capng_save_state(); capng_fill(CAPNG_SELECT_BOTH); if (capng_have_capabilities(CAPNG_SELECT_BOTH) != CAPNG_FULL) { puts("Failed filling capabilities"); abort(); } // Need to detect if version 1 or 2 capabilities text = capng_print_caps_numeric(CAPNG_PRINT_BUFFER, CAPNG_SELECT_CAPS); len = strlen(text); free(text); if (len < 80 && last > 30) // The kernel & headers are mismatched last = 30; // Now test that restore still works capng_restore_state(&saved); if (capng_have_capabilities(CAPNG_SELECT_BOTH) != CAPNG_NONE) { puts("Failed restoring capabilities"); abort(); } printf("Doing advanced bit tests for %d capabilities...\n", last); for (i=0; i<=last; i++) { const char *name; capng_clear(CAPNG_SELECT_BOTH); rc = capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, i); if (rc) { puts("Failed update test 1"); abort(); } rc = capng_have_capability(CAPNG_EFFECTIVE, i); if (rc == 0) { puts("Failed have capability test 1"); capng_print_caps_numeric(CAPNG_PRINT_STDOUT, CAPNG_SELECT_CAPS); abort(); } if(capng_have_capabilities(CAPNG_SELECT_CAPS)!=CAPNG_PARTIAL){ puts("Failed have capabilities test 1"); capng_print_caps_numeric(CAPNG_PRINT_STDOUT, CAPNG_SELECT_CAPS); abort(); } #if CAP_LAST_CAP > 31 rc = capng_update(CAPNG_ADD, CAPNG_BOUNDING_SET, i); if (rc) { puts("Failed bset update test 2"); abort(); } rc = capng_have_capability(CAPNG_BOUNDING_SET, i); if (rc == 0) { puts("Failed bset have capability test 2"); capng_print_caps_numeric(CAPNG_PRINT_STDOUT, CAPNG_SELECT_BOTH); abort(); } if(capng_have_capabilities(CAPNG_SELECT_BOUNDS)!=CAPNG_PARTIAL){ puts("Failed bset have capabilities test 2"); capng_print_caps_numeric(CAPNG_PRINT_STDOUT, CAPNG_SELECT_BOTH); abort(); } #endif text=capng_print_caps_text(CAPNG_PRINT_BUFFER, CAPNG_EFFECTIVE); if (text == NULL) { puts("Failed getting print text to buffer"); abort(); } name = capng_capability_to_name(i); if (name == NULL) { printf("Failed converting capability %d to name\n", i); abort(); } if (strcmp(text, name)) { puts("Failed print text comparison"); printf("%s != %s\n", text, name); abort(); } free(text); // Now make sure the mask part is working capng_fill(CAPNG_SELECT_BOTH); rc = capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, i); if (rc) { puts("Failed update test 3"); abort(); } // Should be partial if(capng_have_capabilities(CAPNG_SELECT_CAPS)!=CAPNG_PARTIAL){ puts("Failed have capabilities test 3"); capng_print_caps_numeric(CAPNG_PRINT_STDOUT, CAPNG_SELECT_CAPS); abort(); } // Add back the bit and should be full capabilities rc = capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, i); if (rc) { puts("Failed update test 4"); abort(); } if (capng_have_capabilities(CAPNG_SELECT_CAPS) != CAPNG_FULL){ puts("Failed have capabilities test 4"); capng_print_caps_numeric(CAPNG_PRINT_STDOUT, CAPNG_SELECT_CAPS); abort(); } } // Now test the updatev function capng_clear(CAPNG_SELECT_BOTH); rc = capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_CHOWN, CAP_FOWNER, CAP_KILL, -1); if (rc) { puts("Failed updatev test"); abort(); } rc = capng_have_capability(CAPNG_EFFECTIVE, CAP_CHOWN) && capng_have_capability(CAPNG_EFFECTIVE, CAP_FOWNER) && capng_have_capability(CAPNG_EFFECTIVE, CAP_KILL); if (rc == 0) { puts("Failed have updatev capability test"); capng_print_caps_numeric(CAPNG_PRINT_STDOUT, CAPNG_SELECT_CAPS); abort(); } return EXIT_SUCCESS; }