diff options
author | Andrew G. Morgan <morgan@kernel.org> | 2020-02-17 14:00:46 -0800 |
---|---|---|
committer | Andrew G. Morgan <morgan@kernel.org> | 2020-02-23 16:00:02 -0800 |
commit | 943b011b5e53624eb9cab4e96c1985326e077cdd (patch) | |
tree | 42342c209e84ac378aaf6dc15af5701966baab1c /libcap | |
parent | 51ed0ec9b78ef321e5feba3780aefbc4d0246449 (diff) | |
download | libcap-943b011b5e53624eb9cab4e96c1985326e077cdd.tar.gz |
A convenient IAB abstraction for inherited capability vectors.
Linux supports three flavors of inheritable capability vectors:
- the I (inheritable set) of cap_t
- the A (ambient) alternative to file capabilities
- the B (bounding) vector.
The cap_iab_t collects these together into one object. I exactly equals
that of cap_t, A is what you would expect and B is "blocked" bits which
are ~cap_bound -- ie., 0 = nothing blocked.
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
Diffstat (limited to 'libcap')
-rw-r--r-- | libcap/cap_alloc.c | 20 | ||||
-rw-r--r-- | libcap/cap_flag.c | 122 | ||||
-rw-r--r-- | libcap/cap_proc.c | 312 | ||||
-rw-r--r-- | libcap/cap_text.c | 105 | ||||
-rw-r--r-- | libcap/include/sys/capability.h | 52 | ||||
-rw-r--r-- | libcap/libcap.h | 26 |
6 files changed, 540 insertions, 97 deletions
diff --git a/libcap/cap_alloc.c b/libcap/cap_alloc.c index 57991a5..d517795 100644 --- a/libcap/cap_alloc.c +++ b/libcap/cap_alloc.c @@ -121,6 +121,11 @@ cap_t cap_dup(cap_t cap_d) return result; } +cap_iab_t cap_iab_init(void) { + __u32 *base = calloc(1, sizeof(__u32) + sizeof(struct cap_iab_s)); + *(base++) = CAP_IAB_MAGIC; + return (cap_iab_t) base; +} /* * Scrub and then liberate an internal capability set. @@ -128,10 +133,10 @@ cap_t cap_dup(cap_t cap_d) int cap_free(void *data_p) { - if ( !data_p ) + if (!data_p) return 0; - if ( good_cap_t(data_p) ) { + if (good_cap_t(data_p)) { data_p = -1 + (__u32 *) data_p; memset(data_p, 0, sizeof(__u32) + sizeof(struct _cap_struct)); free(data_p); @@ -139,7 +144,7 @@ int cap_free(void *data_p) return 0; } - if ( good_cap_string(data_p) ) { + if (good_cap_string(data_p)) { size_t length = strlen(data_p) + sizeof(__u32); data_p = -1 + (__u32 *) data_p; memset(data_p, 0, length); @@ -148,6 +153,15 @@ int cap_free(void *data_p) return 0; } + if (good_cap_iab_t(data_p)) { + size_t length = sizeof(struct cap_iab_s) + sizeof(__u32); + data_p = -1 + (__u32 *) data_p; + memset(data_p, 0, length); + free(data_p); + data_p = NULL; + return 0; + } + _cap_debug("don't recognize what we're supposed to liberate"); errno = EINVAL; return -1; diff --git a/libcap/cap_flag.c b/libcap/cap_flag.c index ff3082f..c1ffa0d 100644 --- a/libcap/cap_flag.c +++ b/libcap/cap_flag.c @@ -1,8 +1,10 @@ /* - * Copyright (c) 1997-8,2008 Andrew G. Morgan <morgan@kernel.org> + * Copyright (c) 1997-8,2008,20 Andrew G. Morgan <morgan@kernel.org> * * This file deals with flipping of capabilities on internal * capability sets as specified by POSIX.1e (formerlly, POSIX 6). + * + * It also contains similar code for bit flipping cap_iab_t values. */ #include "libcap.h" @@ -80,16 +82,12 @@ int cap_set_flag(cap_t cap_d, cap_flag_t set, int cap_clear(cap_t cap_d) { if (good_cap_t(cap_d)) { - memset(&(cap_d->u), 0, sizeof(cap_d->u)); return 0; - } else { - _cap_debug("invalid pointer"); errno = EINVAL; return -1; - } } @@ -125,7 +123,6 @@ int cap_clear_flag(cap_t cap_d, cap_flag_t flag) /* * Compare two capability sets */ - int cap_compare(cap_t a, cap_t b) { unsigned i; @@ -148,3 +145,116 @@ int cap_compare(cap_t a, cap_t b) } return result; } + +/* + * cap_iab_get_vector reads the single bit value from an IAB vector set. + */ +cap_flag_value_t cap_iab_get_vector(cap_iab_t iab, cap_iab_vector_t vec, + cap_value_t bit) +{ + if (!good_cap_iab_t(iab) || bit >= cap_max_bits()) { + return 0; + } + + unsigned o = (bit >> 5); + __u32 mask = 1u << (bit & 31); + + switch (vec) { + case CAP_IAB_INH: + return !!(iab->i[o] & mask); + break; + case CAP_IAB_AMB: + return !!(iab->a[o] & mask); + break; + case CAP_IAB_BOUND: + return !!(iab->nb[o] & mask); + break; + default: + return 0; + } +} + +/* + * cap_iab_set_vector sets the bits in an IAB to the value + * raised. Note, setting A implies setting I too, lowering I implies + * lowering A too. The B bits are, however, independently settable. + */ +int cap_iab_set_vector(cap_iab_t iab, cap_iab_vector_t vec, cap_value_t bit, + cap_flag_value_t raised) +{ + if (!good_cap_iab_t(iab) || (raised >> 1) || bit >= cap_max_bits()) { + errno = EINVAL; + return -1; + } + + unsigned o = (bit >> 5); + __u32 on = 1u << (bit & 31); + __u32 mask = ~on; + + switch (vec) { + case CAP_IAB_INH: + iab->i[o] = (iab->i[o] & mask) | (raised ? on : 0); + iab->a[o] &= iab->i[o]; + break; + case CAP_IAB_AMB: + iab->a[o] = (iab->a[o] & mask) | (raised ? on : 0); + iab->i[o] |= iab->a[o]; + break; + case CAP_IAB_BOUND: + iab->nb[o] = (iab->nb[o] & mask) | (raised ? on : 0); + break; + default: + errno = EINVAL; + return -1; + } + + return 0; +} + +/* + * cap_iab_fill copies a bit-vector of capability state from a cap_t + * to a cap_iab_t. Note, because the bounding bits in an iab are to be + * dropped when applied, the copying process, when to a CAP_IAB_BOUND + * vector involves inverting the bits. Also, adjusting I will mask + * bits in A, and adjusting A may implicitly raise bits in I. + */ +int cap_iab_fill(cap_iab_t iab, cap_iab_vector_t vec, + cap_t cap_d, cap_flag_t flag) +{ + if (!good_cap_t(cap_d) || !good_cap_iab_t(iab)) { + errno = EINVAL; + return -1; + } + + switch (flag) { + case CAP_EFFECTIVE: + case CAP_INHERITABLE: + case CAP_PERMITTED: + break; + default: + errno = EINVAL; + return -1; + } + + int i; + for (i = 0; i < _LIBCAP_CAPABILITY_U32S; i++) { + switch (vec) { + case CAP_IAB_INH: + iab->i[i] = cap_d->u[i].flat[flag]; + iab->a[i] &= iab->i[i]; + break; + case CAP_IAB_AMB: + iab->a[i] = cap_d->u[i].flat[flag]; + iab->i[i] |= cap_d->u[i].flat[flag]; + break; + case CAP_IAB_BOUND: + iab->nb[i] = ~cap_d->u[i].flat[flag]; + break; + default: + errno = EINVAL; + return -1; + } + } + + return 0; +} diff --git a/libcap/cap_proc.c b/libcap/cap_proc.c index f0da4a4..f69c661 100644 --- a/libcap/cap_proc.c +++ b/libcap/cap_proc.c @@ -24,8 +24,8 @@ * using pthreads. */ -static long int _cap_syscall(long int syscall_nr, - long int arg1, long int arg2, long int arg3) +static long int _cap_syscall3(long int syscall_nr, + long int arg1, long int arg2, long int arg3) { return syscall(syscall_nr, arg1, arg2, arg3); } @@ -37,10 +37,26 @@ static long int _cap_syscall6(long int syscall_nr, 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; +/* + * to keep the structure of the code conceptually similar in C and Go + * implementations, we introduce this abstraction for invoking state + * writing system calls. In psx+pthreaded code, the fork + * implementation provided by nptl ensures that we can consistently + * use the multithreaded syscalls even in the child after a fork(). + */ +struct syscaller_s { + long int (*three)(long int syscall_nr, + long int arg1, long int arg2, long int arg3); + long int (*six)(long int syscall_nr, + long int arg1, long int arg2, long int arg3, + long int arg4, long int arg5, long int arg6); +}; + +/* use this syscaller for multithreaded code */ +static struct syscaller_s multithread = { + .three = _cap_syscall3, + .six = _cap_syscall6 +}; /* * This gets reset to 0 if we are *not* linked with libpsx. @@ -79,36 +95,37 @@ void cap_set_syscall(long int (*new_syscall)(long int, long int, long int, long int)) { if (new_syscall == NULL) { - psx_load_syscalls(&_libcap_wsyscall3, &_libcap_wsyscall6); + psx_load_syscalls(&multithread.three, &multithread.six); } else { - _libcap_wsyscall3 = new_syscall; - _libcap_wsyscall6 = new_syscall6; + multithread.three = new_syscall; + multithread.six = new_syscall6; } } -static int _libcap_capset(cap_user_header_t header, const cap_user_data_t data) +static int _libcap_capset(struct syscaller_s *sc, + cap_user_header_t header, const cap_user_data_t data) { if (_libcap_overrode_syscalls) { - return _libcap_wsyscall3(SYS_capset, - (long int) header, (long int) data, 0); + return sc->three(SYS_capset, (long int) header, (long int) data, 0); } return capset(header, data); } -static int _libcap_wprctl3(long int pr_cmd, long int arg1, long int arg2) +static int _libcap_wprctl3(struct syscaller_s *sc, + long int pr_cmd, long int arg1, long int arg2) { if (_libcap_overrode_syscalls) { - return _libcap_wsyscall3(SYS_prctl, pr_cmd, arg1, arg2); + return sc->three(SYS_prctl, pr_cmd, arg1, arg2); } return prctl(pr_cmd, arg1, arg2, 0, 0, 0); } -static int _libcap_wprctl6(long int pr_cmd, long int arg1, long int arg2, +static int _libcap_wprctl6(struct syscaller_s *sc, + long int pr_cmd, long int arg1, long int arg2, long int arg3, long int arg4, long int arg5) { if (_libcap_overrode_syscalls) { - return _libcap_wsyscall6(SYS_prctl, pr_cmd, arg1, arg2, - arg3, arg4, arg5); + return sc->six(SYS_prctl, pr_cmd, arg1, arg2, arg3, arg4, arg5); } return prctl(pr_cmd, arg1, arg2, arg3, arg4, arg5); } @@ -135,8 +152,7 @@ cap_t cap_get_proc(void) return result; } -int cap_set_proc(cap_t cap_d) -{ +static int _cap_set_proc(struct syscaller_s *sc, cap_t cap_d) { int retval; if (!good_cap_t(cap_d)) { @@ -145,11 +161,16 @@ int cap_set_proc(cap_t cap_d) } _cap_debug("setting process capabilities"); - retval = _libcap_capset(&cap_d->head, &cap_d->u[0].set); + retval = _libcap_capset(sc, &cap_d->head, &cap_d->u[0].set); return retval; } +int cap_set_proc(cap_t cap_d) +{ + return _cap_set_proc(&multithread, cap_d); +} + /* the following two functions are not required by POSIX */ /* read the caps on a specific process */ @@ -195,7 +216,8 @@ cap_t cap_get_pid(pid_t pid) /* * set the caps on a specific process/pg etc.. The kernel has long - * since deprecated this asynchronus interface. + * since deprecated this asynchronus interface. DON'T EXPECT THIS TO + * EVER WORK AGAIN. */ int capsetp(pid_t pid, cap_t cap_d) @@ -233,13 +255,11 @@ int cap_get_bound(cap_value_t cap) return result; } -/* drop a capability from the bounding set */ - -int cap_drop_bound(cap_value_t cap) +static int _cap_drop_bound(struct syscaller_s *sc, cap_value_t cap) { int result; - result = _libcap_wprctl3(PR_CAPBSET_DROP, pr_arg(cap), pr_arg(0)); + result = _libcap_wprctl3(sc, PR_CAPBSET_DROP, pr_arg(cap), pr_arg(0)); if (result < 0) { errno = -result; return -1; @@ -247,6 +267,12 @@ int cap_drop_bound(cap_value_t cap) return result; } +/* drop a capability from the bounding set */ + +int cap_drop_bound(cap_value_t cap) { + return _cap_drop_bound(&multithread, cap); +} + /* get a capability from the ambient set */ int cap_get_ambient(cap_value_t cap) @@ -261,9 +287,8 @@ int cap_get_ambient(cap_value_t cap) return result; } -/* modify a single ambient capability value */ - -int cap_set_ambient(cap_value_t cap, cap_flag_value_t set) +static int _cap_set_ambient(struct syscaller_s *sc, + cap_value_t cap, cap_flag_value_t set) { int result, val; switch (set) { @@ -277,7 +302,7 @@ int cap_set_ambient(cap_value_t cap, cap_flag_value_t set) errno = EINVAL; return -1; } - result = _libcap_wprctl6(PR_CAP_AMBIENT, pr_arg(val), pr_arg(cap), + result = _libcap_wprctl6(sc, PR_CAP_AMBIENT, pr_arg(val), pr_arg(cap), pr_arg(0), pr_arg(0), pr_arg(0)); if (result < 0) { errno = -result; @@ -287,12 +312,14 @@ int cap_set_ambient(cap_value_t cap, cap_flag_value_t set) } /* - * 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. + * cap_set_ambient modifies a single ambient capability value. */ -int cap_reset_ambient() +int cap_set_ambient(cap_value_t cap, cap_flag_value_t set) +{ + return _cap_set_ambient(&multithread, cap, set); +} + +static int _cap_reset_ambient(struct syscaller_s *sc) { int olderrno = errno; cap_value_t c; @@ -306,7 +333,8 @@ int cap_reset_ambient() } } - result = _libcap_wprctl6(PR_CAP_AMBIENT, pr_arg(PR_CAP_AMBIENT_CLEAR_ALL), + result = _libcap_wprctl6(sc, 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; @@ -316,22 +344,39 @@ int cap_reset_ambient() } /* + * 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() +{ + return _cap_reset_ambient(&multithread); +} + +/* * 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)); } + +static int _cap_set_secbits(struct syscaller_s *sc, unsigned bits) +{ + return _libcap_wprctl3(sc, PR_SET_SECUREBITS, bits, 0); +} + /* * Set the security mode of the current process. */ int cap_set_secbits(unsigned bits) { - return _libcap_wprctl3(PR_SET_SECUREBITS, bits, 0); + return _cap_set_secbits(&multithread, bits); } /* - * Some predefined + * Some predefined constants */ #define CAP_SECURED_BITS_BASIC \ (SECBIT_NOROOT | SECBIT_NOROOT_LOCKED | \ @@ -341,23 +386,16 @@ int cap_set_secbits(unsigned bits) #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) +static cap_value_t raise_cap_setpcap[] = {CAP_SETPCAP}; + +static int _cap_set_mode(struct syscaller_s *sc, 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); + ret = ret | _cap_set_proc(sc, working); if (ret == 0) { cap_flag_t c; @@ -372,12 +410,12 @@ int cap_set_mode(cap_mode_t flavor) if (!CAP_AMBIENT_SUPPORTED()) { secbits = CAP_SECURED_BITS_BASIC; } else { - ret = cap_reset_ambient(); + ret = _cap_reset_ambient(sc); if (ret) { break; /* ambient dropping failed */ } } - ret = cap_set_secbits(secbits); + ret = _cap_set_secbits(sc, secbits); if (flavor != CAP_MODE_NOPRIV) { break; } @@ -385,7 +423,7 @@ int cap_set_mode(cap_mode_t flavor) /* just for "case CAP_MODE_NOPRIV:" */ for (c = 0; cap_get_bound(c) >= 0; c++) { - (void) cap_drop_bound(c); + (void) _cap_drop_bound(sc, c); } (void) cap_clear_flag(working, CAP_PERMITTED); break; @@ -397,12 +435,25 @@ int cap_set_mode(cap_mode_t flavor) } (void) cap_clear_flag(working, CAP_EFFECTIVE); - ret = cap_set_proc(working) | ret; + ret = _cap_set_proc(sc, working) | ret; (void) cap_free(working); return ret; } /* + * 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) +{ + return _cap_set_mode(&multithread, flavor); +} + +/* * 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. @@ -459,13 +510,7 @@ cap_mode_t cap_get_mode(void) 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) +static int _cap_setuid(struct syscaller_s *sc, uid_t uid) { const cap_value_t raise_cap_setuid[] = {CAP_SETUID}; cap_t working = cap_get_proc(); @@ -478,11 +523,11 @@ int cap_setuid(uid_t uid) * 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); + (void) _libcap_wprctl3(sc, PR_SET_KEEPCAPS, 1, 0); + int ret = _cap_set_proc(sc, working); if (ret == 0) { if (_libcap_overrode_syscalls) { - ret = _libcap_wsyscall3(SYS_setuid, (long int) uid, 0, 0); + ret = sc->three(SYS_setuid, (long int) uid, 0, 0); if (ret < 0) { errno = -ret; ret = -1; @@ -492,16 +537,26 @@ int cap_setuid(uid_t uid) } } int olderrno = errno; - (void) _libcap_wprctl3(PR_SET_KEEPCAPS, 0, 0); - + (void) _libcap_wprctl3(sc, PR_SET_KEEPCAPS, 0, 0); (void) cap_clear_flag(working, CAP_EFFECTIVE); - (void) cap_set_proc(working); + (void) _cap_set_proc(sc, working); (void) cap_free(working); errno = olderrno; return ret; } +/* + * 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) +{ + return _cap_setuid(&multithread, uid); +} + #if defined(__arm__) || defined(__i386__) || \ defined(__i486__) || defined(__i586__) || defined(__i686__) #define sys_setgroups_variant SYS_setgroups32 @@ -509,14 +564,8 @@ int cap_setuid(uid_t uid) #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[]) +static int _cap_setgroups(struct syscaller_s *sc, + 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(); @@ -529,14 +578,14 @@ int cap_setgroups(gid_t gid, size_t ngroups, const gid_t groups[]) * 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); + int ret = _cap_set_proc(sc, working); if (_libcap_overrode_syscalls) { if (ret == 0) { - ret = _libcap_wsyscall3(SYS_setgid, (long int) gid, 0, 0); + ret = sc->three(SYS_setgid, (long int) gid, 0, 0); } if (ret == 0) { - ret = _libcap_wsyscall3(sys_setgroups_variant, (long int) ngroups, - (long int) groups, 0); + ret = sc->three(sys_setgroups_variant, (long int) ngroups, + (long int) groups, 0); } if (ret < 0) { errno = -ret; @@ -553,9 +602,116 @@ int cap_setgroups(gid_t gid, size_t ngroups, const gid_t groups[]) int olderrno = errno; (void) cap_clear_flag(working, CAP_EFFECTIVE); - (void) cap_set_proc(working); + (void) _cap_set_proc(sc, working); (void) cap_free(working); errno = olderrno; return ret; } + +/* + * 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[]) +{ + return _cap_setgroups(&multithread, gid, ngroups, groups); +} + +/* + * cap_iab_get_proc returns a cap_iab_t value initialized by the + * current process state related to these iab bits. + */ +cap_iab_t cap_iab_get_proc(void) +{ + cap_iab_t iab = cap_iab_init(); + cap_t current = cap_get_proc(); + cap_iab_fill(iab, CAP_IAB_INH, current, CAP_INHERITABLE); + cap_value_t c; + for (c = cap_max_bits(); c; ) { + --c; + int o = c >> 5; + __u32 mask = 1U << (c & 31); + if (cap_get_bound(c) == 0) { + iab->nb[o] |= mask; + } + if (cap_get_ambient(c) == 1) { + iab->a[o] |= mask; + } + } + return iab; +} + +/* + * _cap_iab_set_proc sets the iab collection using the requested syscaller. + */ +static int _cap_iab_set_proc(struct syscaller_s *sc, cap_iab_t iab) +{ + int ret, i; + cap_t working, temp = cap_get_proc(); + cap_value_t c; + int raising = 0; + + for (i = 0; i < _LIBCAP_CAPABILITY_U32S; i++) { + __u32 newI = iab->i[i]; + __u32 oldIP = temp->u[i].flat[CAP_INHERITABLE] | + temp->u[i].flat[CAP_PERMITTED]; + raising |= (newI & ~oldIP) | iab->a[i] | iab->nb[i]; + temp->u[i].flat[CAP_INHERITABLE] = newI; + + } + + working = cap_dup(temp); + if (raising) { + ret = cap_set_flag(working, CAP_EFFECTIVE, + 1, raise_cap_setpcap, CAP_SET); + if (ret) { + goto defer; + } + } + if ((ret = _cap_set_proc(sc, working))) { + goto defer; + } + if ((ret = _cap_reset_ambient(sc))) { + goto done; + } + + for (c = cap_max_bits(); c-- != 0; ) { + unsigned offset = c >> 5; + __u32 mask = 1U << (c & 31); + if (iab->a[offset] & mask) { + ret = _cap_set_ambient(sc, c, CAP_SET); + if (ret) { + goto done; + } + } + if (iab->nb[offset] & mask) { + /* drop the bounding bit */ + ret = _cap_drop_bound(sc, c); + if (ret) { + goto done; + } + } + } + +done: + (void) cap_set_proc(temp); + +defer: + cap_free(working); + cap_free(temp); + + return ret; +} + +/* + * cap_iab_set_proc sets the iab capability vectors of the current + * process. + */ +int cap_iab_set_proc(cap_iab_t iab) +{ + return _cap_iab_set_proc(&multithread, iab); +} diff --git a/libcap/cap_text.c b/libcap/cap_text.c index 00fbbc6..b2a0584 100644 --- a/libcap/cap_text.c +++ b/libcap/cap_text.c @@ -476,3 +476,108 @@ const char *cap_mode_name(cap_mode_t flavor) { return "UNKNOWN"; } } + +/* + * cap_iab_to_text serializes an iab into a canonical text + * representation. + */ +char *cap_iab_to_text(cap_iab_t iab) +{ + char buf[CAP_TEXT_SIZE+CAP_TEXT_BUFFER_ZONE]; + char *p = buf; + cap_value_t c, cmb = cap_max_bits(); + int first = 1; + + if (good_cap_iab_t(iab)) { + for (c = 0; c < cmb; c++) { + int keep = 0; + int o = c >> 5; + __u32 bit = 1U << (c & 31); + __u32 ib = iab->i[o] & bit; + __u32 ab = iab->a[o] & bit; + __u32 nbb = iab->nb[o] & bit; + if (!(nbb | ab | ib)) { + continue; + } + if (!first) { + *p++ = ','; + } + if (nbb) { + *p++ = '!'; + keep = 1; + } + if (ab) { + *p++ = '^'; + keep = 1; + } else if (nbb && ib) { + *p++ = '%'; + } + if (keep || ib) { + strcpy(p, _cap_names[c]); + p += strlen(p); + first = 0; + } + } + } + *p = '\0'; + return _libcap_strdup(buf); +} + +cap_iab_t cap_iab_from_text(const char *text) +{ + cap_iab_t iab = cap_iab_init(); + if (text != NULL) { + unsigned flags; + for (flags = 0; *text; text++) { + /* consume prefixes */ + switch (*text) { + case '!': + flags |= LIBCAP_IAB_NB_FLAG; + continue; + case '^': + flags |= LIBCAP_IAB_IA_FLAG; + continue; + case '%': + flags |= LIBCAP_IAB_I_FLAG; + continue; + default: + break; + } + if (!flags) { + flags = LIBCAP_IAB_I_FLAG; + } + + /* consume cap name */ + cap_value_t c = lookupname(&text); + if (c == -1) { + goto cleanup; + } + unsigned o = c >> 5; + __u32 mask = 1U << (c & 31); + if (flags & LIBCAP_IAB_I_FLAG) { + iab->i[o] |= mask; + } + if (flags & LIBCAP_IAB_A_FLAG) { + iab->a[o] |= mask; + } + if (flags & LIBCAP_IAB_NB_FLAG) { + iab->nb[o] |= mask; + } + + /* rest should be end or comma */ + if (*text == '\0') { + break; + } + if (*text != ',') { + goto cleanup; + } + flags = 0; + } + } + return iab; + +cleanup: + cap_free(iab); + errno = EINVAL; + return NULL; +} diff --git a/libcap/include/sys/capability.h b/libcap/include/sys/capability.h index 48301fd..12a9baa 100644 --- a/libcap/include/sys/capability.h +++ b/libcap/include/sys/capability.h @@ -2,7 +2,7 @@ * <sys/capability.h> * * Copyright (C) 1997 Aleph One - * Copyright (C) 1997-8,2008,2019 Andrew G. Morgan <morgan@kernel.org> + * Copyright (C) 1997,8, 2008,19,20 Andrew G. Morgan <morgan@kernel.org> * * defunct POSIX.1e Standard: 25.2 Capabilities <sys/capability.h> */ @@ -57,11 +57,35 @@ extern cap_value_t cap_max_bits(void); * Set identifiers */ typedef enum { - CAP_EFFECTIVE=0, /* Specifies the effective flag */ - CAP_PERMITTED=1, /* Specifies the permitted flag */ - CAP_INHERITABLE=2 /* Specifies the inheritable flag */ + CAP_EFFECTIVE = 0, /* Specifies the effective flag */ + CAP_PERMITTED = 1, /* Specifies the permitted flag */ + CAP_INHERITABLE = 2 /* Specifies the inheritable flag */ } cap_flag_t; +typedef enum { + CAP_IAB_INH = 2, + CAP_IAB_AMB = 3, + CAP_IAB_BOUND = 4 +} cap_iab_vector_t; + +/* + * An opaque generalization of the inheritable bits that includes both + * what ambient bits to raise and what bounding bits to *lower* (aka + * drop). None of these bits once set, using cap_iab_set(), affect + * the running process but are consulted, through the execve() system + * call, by the kernel. Note, the ambient bits ('A') of the running + * process are fragile with respect to other aspects of the "posix" + * (cap_t) operations: most importantly, 'A' cannot ever hold bits not + * present in the intersection of 'pI' and 'pP'. The kernel + * immediately drops all ambient caps whenever such a situation + * arises. Typically, the ambient bits are used to support a naive + * capability inheritance model - at odds with the POSIX (sic) model + * of inheritance where inherited (pI) capabilities need to also be + * wanted by the executed binary (fI) in order to become raised + * through exec. + */ +typedef struct cap_iab_s *cap_iab_t; + /* * These are the states available to each capability */ @@ -73,7 +97,6 @@ typedef enum { /* * User-space capability manipulation routines */ - typedef unsigned cap_mode_t; #define CAP_MODE_UNCERTAIN ((cap_mode_t) 0) #define CAP_MODE_NOPRIV ((cap_mode_t) 1) @@ -81,9 +104,10 @@ typedef unsigned cap_mode_t; #define CAP_MODE_PURE1E ((cap_mode_t) 3) /* libcap/cap_alloc.c */ -extern cap_t cap_dup(cap_t); -extern int cap_free(void *); -extern cap_t cap_init(void); +extern cap_t cap_dup(cap_t); +extern int cap_free(void *); +extern cap_t cap_init(void); +extern cap_iab_t cap_iab_init(void); /* libcap/cap_flag.c */ extern int cap_get_flag(cap_t, cap_value_t, cap_flag_t, cap_flag_value_t *); @@ -92,6 +116,12 @@ extern int cap_set_flag(cap_t, cap_flag_t, int, const cap_value_t *, extern int cap_clear(cap_t); extern int cap_clear_flag(cap_t, cap_flag_t); +extern cap_flag_value_t cap_iab_get_vector(cap_iab_t, cap_iab_vector_t, + cap_value_t); +extern int cap_iab_set_vector(cap_iab_t, cap_iab_vector_t, cap_value_t, + cap_flag_value_t); +extern int cap_iab_fill(cap_iab_t, cap_iab_vector_t, cap_t, cap_flag_t); + /* libcap/cap_file.c */ extern cap_t cap_get_fd(int); extern cap_t cap_get_file(const char *); @@ -125,6 +155,9 @@ extern char * cap_to_text(cap_t, ssize_t *); extern int cap_from_name(const char *, cap_value_t *); extern char * cap_to_name(cap_value_t); +extern char * cap_iab_to_text(cap_iab_t bits); +extern cap_iab_t cap_iab_from_text(const char *summary); + #define CAP_DIFFERS(result, flag) (((result) & (1 << (flag))) != 0) extern int cap_compare(cap_t, cap_t); @@ -145,6 +178,9 @@ extern int cap_set_secbits(unsigned bits); extern int cap_setuid(uid_t uid); extern int cap_setgroups(gid_t gid, size_t ngroups, const gid_t groups[]); +extern cap_iab_t cap_iab_get_proc(void); +extern int cap_iab_set_proc(cap_iab_t iab); + /* * system calls - look to libc for function to system call * mapping. Note, libcap does not use capset directly, but permits the diff --git a/libcap/libcap.h b/libcap/libcap.h index 8118182..e7def87 100644 --- a/libcap/libcap.h +++ b/libcap/libcap.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997 Andrew G Morgan <morgan@kernel.org> + * Copyright (c) 1997,2020 Andrew G Morgan <morgan@kernel.org> * * This file contains internal definitions for the various functions in * this small capability library. @@ -28,7 +28,7 @@ #ifndef _LINUX_CAPABILITY_U32S_1 # define _LINUX_CAPABILITY_U32S_1 1 -#endif /* ndef _LINUX_CAPABILITY_U32S */ +#endif /* ndef _LINUX_CAPABILITY_U32S_1 */ /* * Do we match the local kernel? @@ -127,6 +127,9 @@ struct _cap_struct { /* string magic for cap_free */ #define CAP_S_MAGIC 0xCA95D0 +/* iab set magic for cap_free */ +#define CAP_IAB_MAGIC 0xCA9AB + /* * kernel API cap set abstraction */ @@ -142,6 +145,7 @@ struct _cap_struct { #define __libcap_check_magic(c,magic) ((c) && *(-1+(__u32 *)(c)) == (magic)) #define good_cap_t(c) __libcap_check_magic(c, CAP_T_MAGIC) #define good_cap_string(c) __libcap_check_magic(c, CAP_S_MAGIC) +#define good_cap_iab_t(c) __libcap_check_magic(c, CAP_IAB_MAGIC) /* * These match CAP_DIFFERS() expectations @@ -222,4 +226,22 @@ extern int capsetp(pid_t pid, cap_t cap_d); val = min ? min : fallback; \ } while(0) +/* + * cap_iab_s holds a collection of inheritable capability bits. The i + * bits are inheritable (these are the same as those in cap_t), the a + * bits are ambient bits (which cannot be a superset of i&p), and nb + * are the bits that will be dropped from the bounding set when + * applied. + */ +struct cap_iab_s { + __u32 i[_LIBCAP_CAPABILITY_U32S]; + __u32 a[_LIBCAP_CAPABILITY_U32S]; + __u32 nb[_LIBCAP_CAPABILITY_U32S]; +}; + +#define LIBCAP_IAB_I_FLAG (1U << CAP_IAB_INH) +#define LIBCAP_IAB_A_FLAG (1U << CAP_IAB_AMB) +#define LIBCAP_IAB_IA_FLAG (LIBCAP_IAB_I_FLAG | LIBCAP_IAB_A_FLAG) +#define LIBCAP_IAB_NB_FLAG (1U << CAP_IAB_BOUND) + #endif /* LIBCAP_H */ |