aboutsummaryrefslogtreecommitdiff
path: root/libcap
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2020-02-17 14:00:46 -0800
committerAndrew G. Morgan <morgan@kernel.org>2020-02-23 16:00:02 -0800
commit943b011b5e53624eb9cab4e96c1985326e077cdd (patch)
tree42342c209e84ac378aaf6dc15af5701966baab1c /libcap
parent51ed0ec9b78ef321e5feba3780aefbc4d0246449 (diff)
downloadlibcap-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.c20
-rw-r--r--libcap/cap_flag.c122
-rw-r--r--libcap/cap_proc.c312
-rw-r--r--libcap/cap_text.c105
-rw-r--r--libcap/include/sys/capability.h52
-rw-r--r--libcap/libcap.h26
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 */