blob: 078a8c380a022fea106201a8bb3e21e370f35404 [file] [log] [blame]
/*
* Copyright (c) 1997-8,2007,2011,2019 Andrew G Morgan <morgan@kernel.org>
*
* This file deals with getting and setting capabilities on processes.
*/
#define _GNU_SOURCE
#include <sys/prctl.h>
#include <sys/psx_syscall.h>
#include <sys/securebits.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <grp.h>
#include <linux/limits.h>
#include "libcap.h"
/*
* libcap uses this abstraction for all system calls that change
* kernel managed capability state. This permits the user to redirect
* it for testing and also to better implement posix semantics when
* using pthreads.
*/
static long int _cap_syscall(long int syscall_nr,
long int arg1, long int arg2, long int arg3)
{
return syscall(syscall_nr, arg1, arg2, arg3);
}
static long int _cap_syscall6(long int syscall_nr,
long int arg1, long int arg2, long int arg3,
long int arg4, long int arg5, long int arg6)
{
return syscall(syscall_nr, arg1, arg2, arg3, arg4, arg5, arg6);
}
static long int (*_libcap_wsyscall3)(long int, long int, long int, long int)
= _cap_syscall;
static long int (*_libcap_wsyscall6)(long int, long int, long int, long int,
long int, long int, long int) = _cap_syscall6;
/*
* This gets reset to 0 if we are *not* linked with libpsx.
*/
static int _libcap_overrode_syscalls = 1;
/*
* psx_load_syscalls() is weakly defined so we can have it overriden
* by libpsx if that library is linked. Specifically, when libcap
* calls psx_load_sycalls() it is prepared to override the default
* values for the syscalls that libcap uses to change security state.
* As can be seen here this present function is mostly a
* no-op. However, if libpsx is linked, the one present in that
* library (not being weak) will replace this one and the
* _libcap_overrode_syscalls value isn't forced to zero.
*/
__attribute__((weak))
void psx_load_syscalls(long int (**syscall_fn)(long int,
long int, long int, long int),
long int (**syscall6_fn)(long int,
long int, long int, long int,
long int, long int, long int))
{
_libcap_overrode_syscalls = 0;
}
/*
* cap_set_syscall overrides the state setting syscalls that libcap does.
* Generally, you don't need to call this manually: libcap tries hard to
* set things up appropriately.
*/
void cap_set_syscall(long int (*new_syscall)(long int,
long int, long int, long int),
long int (*new_syscall6)(long int, long int,
long int, long int,
long int, long int,
long int)) {
if (new_syscall == NULL) {
psx_load_syscalls(&_libcap_wsyscall3, &_libcap_wsyscall6);
} else {
_libcap_wsyscall3 = new_syscall;
_libcap_wsyscall6 = new_syscall6;
}
}
static int _libcap_capset(cap_user_header_t header, const cap_user_data_t data)
{
return _libcap_wsyscall3(SYS_capset, (long int) header, (long int) data, 0);
}
static int _libcap_wprctl3(long int pr_cmd, long int arg1, long int arg2)
{
return _libcap_wsyscall3(SYS_prctl, pr_cmd, arg1, arg2);
}
static int _libcap_wprctl6(long int pr_cmd, long int arg1, long int arg2,
long int arg3, long int arg4, long int arg5)
{
return _libcap_wsyscall6(SYS_prctl, pr_cmd, arg1, arg2, arg3, arg4, arg5);
}
/*
* cap_get_proc obtains the capability set for the current process.
*/
cap_t cap_get_proc(void)
{
cap_t result;
/* allocate a new capability set */
result = cap_init();
if (result) {
_cap_debug("getting current process' capabilities");
/* fill the capability sets via a system call */
if (capget(&result->head, &result->u[0].set)) {
cap_free(result);
result = NULL;
}
}
return result;
}
int cap_set_proc(cap_t cap_d)
{
int retval;
if (!good_cap_t(cap_d)) {
errno = EINVAL;
return -1;
}
_cap_debug("setting process capabilities");
retval = _libcap_capset(&cap_d->head, &cap_d->u[0].set);
return retval;
}
/* the following two functions are not required by POSIX */
/* read the caps on a specific process */
int capgetp(pid_t pid, cap_t cap_d)
{
int error;
if (!good_cap_t(cap_d)) {
errno = EINVAL;
return -1;
}
_cap_debug("getting process capabilities for proc %d", pid);
cap_d->head.pid = pid;
error = capget(&cap_d->head, &cap_d->u[0].set);
cap_d->head.pid = 0;
return error;
}
/* allocate space for and return capabilities of target process */
cap_t cap_get_pid(pid_t pid)
{
cap_t result;
result = cap_init();
if (result) {
if (capgetp(pid, result) != 0) {
int my_errno;
my_errno = errno;
cap_free(result);
errno = my_errno;
result = NULL;
}
}
return result;
}
/*
* set the caps on a specific process/pg etc.. The kernel has long
* since deprecated this asynchronus interface.
*/
int capsetp(pid_t pid, cap_t cap_d)
{
int error;
if (!good_cap_t(cap_d)) {
errno = EINVAL;
return -1;
}
_cap_debug("setting process capabilities for proc %d", pid);
cap_d->head.pid = pid;
error = capset(&cap_d->head, &cap_d->u[0].set);
cap_d->head.version = _LIBCAP_CAPABILITY_VERSION;
cap_d->head.pid = 0;
return error;
}
/* the kernel api requires unsigned long arguments */
#define pr_arg(x) ((unsigned long) x)
/* get a capability from the bounding set */
int cap_get_bound(cap_value_t cap)
{
int result;
result = prctl(PR_CAPBSET_READ, pr_arg(cap), pr_arg(0));
if (result < 0) {
errno = -result;
return -1;
}
return result;
}
/* drop a capability from the bounding set */
int cap_drop_bound(cap_value_t cap)
{
int result;
result = _libcap_wprctl3(PR_CAPBSET_DROP, pr_arg(cap), pr_arg(0));
if (result < 0) {
errno = -result;
return -1;
}
return result;
}
/* get a capability from the ambient set */
int cap_get_ambient(cap_value_t cap)
{
int result;
result = prctl(PR_CAP_AMBIENT, pr_arg(PR_CAP_AMBIENT_IS_SET),
pr_arg(cap), pr_arg(0), pr_arg(0));
if (result < 0) {
errno = -result;
return -1;
}
return result;
}
/* modify a single ambient capability value */
int cap_set_ambient(cap_value_t cap, cap_flag_value_t set)
{
int result, val;
switch (set) {
case CAP_SET:
val = PR_CAP_AMBIENT_RAISE;
break;
case CAP_CLEAR:
val = PR_CAP_AMBIENT_LOWER;
break;
default:
errno = EINVAL;
return -1;
}
result = _libcap_wprctl6(PR_CAP_AMBIENT, pr_arg(val), pr_arg(cap),
pr_arg(0), pr_arg(0), pr_arg(0));
if (result < 0) {
errno = -result;
return -1;
}
return result;
}
/*
* cap_reset_ambient erases all ambient capabilities - this reads the
* ambient caps before performing the erase to workaround the corner
* case where the set is empty already but the ambient cap API is
* locked.
*/
int cap_reset_ambient()
{
int olderrno = errno;
cap_value_t c;
int result = 0;
for (c = 0; !result; c++) {
result = cap_get_ambient(c);
if (result == -1) {
errno = olderrno;
return 0;
}
}
result = _libcap_wprctl6(PR_CAP_AMBIENT, pr_arg(PR_CAP_AMBIENT_CLEAR_ALL),
pr_arg(0), pr_arg(0), pr_arg(0), pr_arg(0));
if (result < 0) {
errno = -result;
return -1;
}
return result;
}
/*
* Read the security mode of the current process.
*/
unsigned cap_get_secbits(void)
{
return (unsigned) prctl(PR_GET_SECUREBITS, pr_arg(0), pr_arg(0));
}
/*
* Set the security mode of the current process.
*/
int cap_set_secbits(unsigned bits)
{
return _libcap_wprctl3(PR_SET_SECUREBITS, bits, 0);
}
/*
* Some predefined
*/
#define CAP_SECURED_BITS_BASIC \
(SECBIT_NOROOT | SECBIT_NOROOT_LOCKED | \
SECBIT_NO_SETUID_FIXUP | SECBIT_NO_SETUID_FIXUP_LOCKED | \
SECBIT_KEEP_CAPS_LOCKED)
#define CAP_SECURED_BITS_AMBIENT (CAP_SECURED_BITS_BASIC | \
SECBIT_NO_CAP_AMBIENT_RAISE | SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED)
/*
* cap_set_mode locks the overarching capability framework of the
* present process and thus its children to a predefined flavor. Once
* set, these modes cannot be undone by the affected process tree and
* can only be done by "cap_setpcap" permitted processes. Note, a side
* effect of this function, whether it succeeds or fails, is to clear
* atleast the CAP_EFFECTIVE flags for the current process.
*/
int cap_set_mode(cap_mode_t flavor)
{
const cap_value_t raise_cap_setpcap[] = {CAP_SETPCAP};
cap_t working = cap_get_proc();
unsigned secbits = CAP_SECURED_BITS_AMBIENT;
int ret = cap_set_flag(working, CAP_EFFECTIVE,
1, raise_cap_setpcap, CAP_SET);
ret = ret | cap_set_proc(working);
if (ret == 0) {
cap_flag_t c;
switch (flavor) {
case CAP_MODE_NOPRIV:
/* fall through */
case CAP_MODE_PURE1E_INIT:
(void) cap_clear_flag(working, CAP_INHERITABLE);
/* fall through */
case CAP_MODE_PURE1E:
if (!CAP_AMBIENT_SUPPORTED()) {
secbits = CAP_SECURED_BITS_BASIC;
} else {
ret = cap_reset_ambient();
if (ret) {
break; /* ambient dropping failed */
}
}
ret = cap_set_secbits(secbits);
if (flavor != CAP_MODE_NOPRIV) {
break;
}
/* just for "case CAP_MODE_NOPRIV:" */
for (c = 0; cap_get_bound(c) >= 0; c++) {
(void) cap_drop_bound(c);
}
(void) cap_clear_flag(working, CAP_PERMITTED);
break;
default:
errno = EINVAL;
ret = -1;
break;
}
}
(void) cap_clear_flag(working, CAP_EFFECTIVE);
ret = cap_set_proc(working) | ret;
(void) cap_free(working);
return ret;
}
/*
* cap_get_mode attempts to determine what the current capability mode
* is. If it can find no match in the libcap pre-defined modes, it
* returns CAP_MODE_UNCERTAIN.
*/
cap_mode_t cap_get_mode(void)
{
unsigned secbits = cap_get_secbits();
if ((secbits & CAP_SECURED_BITS_BASIC) != CAP_SECURED_BITS_BASIC) {
return CAP_MODE_UNCERTAIN;
}
/* validate ambient is not set */
int olderrno = errno;
int ret = 0;
cap_value_t c;
for (c = 0; !ret; c++) {
ret = cap_get_ambient(c);
if (ret == -1) {
errno = olderrno;
if (c && secbits != CAP_SECURED_BITS_AMBIENT) {
return CAP_MODE_UNCERTAIN;
}
break;
}
if (ret) {
return CAP_MODE_UNCERTAIN;
}
}
cap_t working = cap_get_proc();
cap_t empty = cap_init();
int cf = cap_compare(empty, working);
cap_free(empty);
cap_free(working);
if (CAP_DIFFERS(cf, CAP_INHERITABLE)) {
return CAP_MODE_PURE1E;
}
if (CAP_DIFFERS(cf, CAP_PERMITTED) || CAP_DIFFERS(cf, CAP_EFFECTIVE)) {
return CAP_MODE_PURE1E_INIT;
}
for (c = 0; ; c++) {
int v = cap_get_bound(c);
if (v == -1) {
break;
}
if (v) {
return CAP_MODE_PURE1E_INIT;
}
}
return CAP_MODE_NOPRIV;
}
/*
* cap_setuid attempts to set the uid of the process without dropping
* any permitted capabilities in the process. A side effect of a call
* to this function is that the effective set will be cleared by the
* time the function returns.
*/
int cap_setuid(uid_t uid)
{
const cap_value_t raise_cap_setuid[] = {CAP_SETUID};
cap_t working = cap_get_proc();
(void) cap_set_flag(working, CAP_EFFECTIVE,
1, raise_cap_setuid, CAP_SET);
/*
* Note, we are cognizant of not using glibc's setuid in the case
* that we've modified the way libcap is doing setting
* syscalls. This is because prctl needs to be working in a POSIX
* compliant way for the code below to work, so we are either
* all-broken or not-broken and don't allow for "sort of working".
*/
(void) _libcap_wprctl3(PR_SET_KEEPCAPS, 1, 0);
int ret = cap_set_proc(working);
if (ret == 0) {
if (_libcap_overrode_syscalls) {
ret = _libcap_wsyscall3(SYS_setuid, (long int) uid, 0, 0);
if (ret < 0) {
errno = -ret;
ret = -1;
}
} else {
ret = setuid(uid);
}
}
int olderrno = errno;
(void) _libcap_wprctl3(PR_SET_KEEPCAPS, 0, 0);
(void) cap_clear_flag(working, CAP_EFFECTIVE);
(void) cap_set_proc(working);
(void) cap_free(working);
errno = olderrno;
return ret;
}
#if defined(__arm__) || defined(__i386__) || \
defined(__i486__) || defined(__i586__) || defined(__i686__)
#define sys_setgroups_variant SYS_setgroups32
#else
#define sys_setgroups_variant SYS_setgroups
#endif
/*
* cap_setgroups combines setting the gid with changing the set of
* supplemental groups for a user into one call that raises the needed
* capabilities to do it for the duration of the call. A side effect
* of a call to this function is that the effective set will be
* cleared by the time the function returns.
*/
int cap_setgroups(gid_t gid, size_t ngroups, const gid_t groups[])
{
const cap_value_t raise_cap_setgid[] = {CAP_SETGID};
cap_t working = cap_get_proc();
(void) cap_set_flag(working, CAP_EFFECTIVE,
1, raise_cap_setgid, CAP_SET);
/*
* Note, we are cognizant of not using glibc's setgid etc in the
* case that we've modified the way libcap is doing setting
* syscalls. This is because prctl needs to be working in a POSIX
* compliant way for the other functions of this file so we are
* all-broken or not-broken and don't allow for "sort of working".
*/
int ret = cap_set_proc(working);
if (_libcap_overrode_syscalls) {
if (ret == 0) {
ret = _libcap_wsyscall3(SYS_setgid, (long int) gid, 0, 0);
}
if (ret == 0) {
ret = _libcap_wsyscall3(sys_setgroups_variant, (long int) ngroups,
(long int) groups, 0);
}
if (ret < 0) {
errno = -ret;
ret = -1;
}
} else {
if (ret == 0) {
ret = setgid(gid);
}
if (ret == 0) {
ret = setgroups(ngroups, groups);
}
}
int olderrno = errno;
(void) cap_clear_flag(working, CAP_EFFECTIVE);
(void) cap_set_proc(working);
(void) cap_free(working);
errno = olderrno;
return ret;
}