diff options
author | Dariusz Michaluk <d.michaluk@samsung.com> | 2024-02-16 12:29:59 +0100 |
---|---|---|
committer | Dariusz Michaluk <d.michaluk@samsung.com> | 2024-02-16 12:29:59 +0100 |
commit | 3ec0912f27cde058107543db7e8e6a65e44d18b3 (patch) | |
tree | 02a7601c2964f9c712b919a223dda094ff0c2369 /src/cap-ng.c | |
parent | c591394f75d0462d9d9a17d66c1bcf800181a294 (diff) | |
download | libcap-ng-upstream.tar.gz libcap-ng-upstream.tar.bz2 libcap-ng-upstream.zip |
Imported Upstream version 0.8.4upstream/0.8.4upstream
Diffstat (limited to 'src/cap-ng.c')
-rw-r--r-- | src/cap-ng.c | 733 |
1 files changed, 644 insertions, 89 deletions
diff --git a/src/cap-ng.c b/src/cap-ng.c index b68aa62..d178e35 100644 --- a/src/cap-ng.c +++ b/src/cap-ng.c @@ -1,5 +1,5 @@ /* libcap-ng.c -- - * Copyright 2009-10 Red Hat Inc., Durham, North Carolina. + * Copyright 2009-10, 2013, 2017, 2020-21 Red Hat Inc. * All Rights Reserved. * * This library is free software; you can redistribute it and/or @@ -12,9 +12,10 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the GNU Lesser General Public License + * along with this program; see the file COPYING.LIB. If not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1335, USA. * * Authors: * Steve Grubb <sgrubb@redhat.com> @@ -28,33 +29,65 @@ #include <stdio_ext.h> #include <stdlib.h> #include <sys/prctl.h> +#include <pwd.h> #include <grp.h> #include <sys/stat.h> -#include <stdarg.h> #include <errno.h> +#include <fcntl.h> +#include <endian.h> #include <byteswap.h> +#ifdef HAVE_PTHREAD_H +#include <pthread.h> // For pthread_atfork +#endif #ifdef HAVE_SYSCALL_H #include <sys/syscall.h> #endif #ifdef HAVE_LINUX_SECUREBITS_H #include <linux/securebits.h> #endif +#ifdef HAVE_LINUX_MAGIC_H +#include <sys/vfs.h> +#include <linux/magic.h> +#endif +# define hidden __attribute__ ((visibility ("hidden"))) +unsigned int last_cap hidden = 0; /* * Some milestones of when things became available: * 2.6.24 kernel XATTR_NAME_CAPS * 2.6.25 kernel PR_CAPBSET_DROP, CAPABILITY_VERSION_2 * 2.6.26 kernel PR_SET_SECUREBITS, SECURE_*_LOCKED, VERSION_3 + * 3.5 kernel PR_SET_NO_NEW_PRIVS + * 4.3 kernel PR_CAP_AMBIENT + * 4.14 kernel VFS_CAP_REVISION_3 */ +#ifdef PR_CAPBSET_DROP +static int HAVE_PR_CAPBSET_DROP = 0; +#endif +#ifdef PR_SET_SECUREBITS +static int HAVE_PR_SET_SECUREBITS = 0; +#endif +#ifdef PR_SET_NO_NEW_PRIVS +static int HAVE_PR_SET_NO_NEW_PRIVS = 0; +#endif +#ifdef PR_CAP_AMBIENT +static int HAVE_PR_CAP_AMBIENT = 0; +#endif /* External syscall prototypes */ extern int capset(cap_user_header_t header, cap_user_data_t data); extern int capget(cap_user_header_t header, const cap_user_data_t data); +// Local functions +static void update_bounding_set(capng_act_t action, unsigned int capability, + unsigned int idx); +static void update_ambient_set(capng_act_t action, unsigned int capability, + unsigned int idx); + // Local defines #define MASK(x) (1U << (x)) #ifdef PR_CAPBSET_DROP -#define UPPER_MASK ~(unsigned)((~0U)<<(CAP_LAST_CAP-31)) +#define UPPER_MASK ~((~0U)<<(last_cap-31)) #else // For v1 systems UPPER_MASK will never be used #define UPPER_MASK (unsigned)(~0U) @@ -62,16 +95,22 @@ extern int capget(cap_user_header_t header, const cap_user_data_t data); // Re-define cap_valid so its uniform between V1 and V3 #undef cap_valid -#define cap_valid(x) ((x) <= CAP_LAST_CAP) +#define cap_valid(x) ((x) <= last_cap) // If we don't have the xattr library, then we can't // compile-in file system capabilities -#ifndef HAVE_ATTR_XATTR_H +#if !defined(HAVE_ATTR_XATTR_H) && !defined (HAVE_SYS_XATTR_H) #undef VFS_CAP_U32 #endif #ifdef VFS_CAP_U32 - #include <attr/xattr.h> + #ifdef HAVE_SYS_XATTR_H + #include <sys/xattr.h> + #else + #ifdef HAVE_ATTR_XATTR_H + #include <attr/xattr.h> + #endif + #endif #if __BYTE_ORDER == __BIG_ENDIAN #define FIXUP(x) bswap_32(x) #else @@ -110,12 +149,21 @@ extern int capget(cap_user_header_t header, const cap_user_data_t data); #endif /* Setuid apps run by uid 0 don't get caps back */ #ifndef SECURE_NO_SETUID_FIXUP -#define SECURE_NO_SETUID_FIXUP 2 +#define SECURE_NO_SETUID_FIXUP 2 #endif #ifndef SECURE_NO_SETUID_FIXUP_LOCKED #define SECURE_NO_SETUID_FIXUP_LOCKED 3 /* make bit-2 immutable */ #endif +#ifndef VFS_CAP_U32 +#define VFS_CAP_U32 2 +#endif + +#if (VFS_CAP_U32 != 2) +#error VFS_CAP_U32 does not match the library, you need a new version +#endif + + // States: new, allocated, initted, updated, applied typedef enum { CAPNG_NEW, CAPNG_ERROR, CAPNG_ALLOCATED, CAPNG_INIT, CAPNG_UPDATED, CAPNG_APPLIED } capng_states_t; @@ -123,29 +171,133 @@ typedef enum { CAPNG_NEW, CAPNG_ERROR, CAPNG_ALLOCATED, CAPNG_INIT, // Create an easy data struct out of the kernel definitions typedef union { struct __user_cap_data_struct v1; - struct __user_cap_data_struct v3[2]; + struct __user_cap_data_struct v3[VFS_CAP_U32]; } cap_data_t; // This struct keeps all state info struct cap_ng { int cap_ver; + int vfs_cap_ver; struct __user_cap_header_struct hdr; cap_data_t data; capng_states_t state; - __u32 bounds[2]; + __le32 rootid; + __u32 bounds[VFS_CAP_U32]; + __u32 ambient[VFS_CAP_U32]; }; // Global variables with per thread uniqueness -static __thread struct cap_ng m = { 1, +static __thread struct cap_ng m = { 1, 1, {0, 0}, { {0, 0, 0} }, - CAPNG_NEW, + CAPNG_NEW, CAPNG_UNSET_ROOTID, + {0, 0}, {0, 0} }; +/* + * Reset the state so that init gets called to erase everything + */ +static void deinit(void) +{ + m.state = CAPNG_NEW; +} + +static inline int test_cap(unsigned int cap) +{ + // prctl returns 0 or 1 for valid caps, -1 otherwise + return prctl(PR_CAPBSET_READ, cap) >= 0; +} + +// The maximum cap value is determined by VFS_CAP_U32 +#define MAX_CAP_VALUE (VFS_CAP_U32 * sizeof(__le32) * 8) + +static void init_lib(void) __attribute__ ((constructor)); +static void init_lib(void) +{ + // This is so dynamic libraries don't re-init + static unsigned int run_once = 0; + if (run_once) + return; + run_once = 1; + +#ifdef HAVE_PTHREAD_H + pthread_atfork(NULL, NULL, deinit); +#endif + // Detect last cap + if (last_cap == 0) { + int fd; + + // Try to read last cap from procfs + fd = open("/proc/sys/kernel/cap_last_cap", O_RDONLY); + if (fd >= 0) { +#ifdef HAVE_LINUX_MAGIC_H + struct statfs st; + // Bail out if procfs is invalid or fstatfs fails + if (fstatfs(fd, &st) || st.f_type != PROC_SUPER_MAGIC) + goto fail; +#endif + char buf[8]; + int num = read(fd, buf, sizeof(buf) - 1); + if (num > 0) { + buf[num] = 0; + errno = 0; + unsigned int val = strtoul(buf, NULL, 10); + if (errno == 0) + last_cap = val; + } +fail: + close(fd); + } + // Run a binary search over capabilities + if (last_cap == 0) { + // starting with last_cap=MAX_CAP_VALUE means we always know + // that cap1 is invalid after the first iteration + last_cap = MAX_CAP_VALUE; + unsigned int cap0 = 0, cap1 = MAX_CAP_VALUE; + + while (cap0 < last_cap) { + if (test_cap(last_cap)) + cap0 = last_cap; + else + cap1 = last_cap; + + last_cap = (cap0 + cap1) / 2U; + } + } + } + // Detect prctl options at runtime +#ifdef PR_CAPBSET_DROP + errno = 0; + prctl(PR_CAPBSET_READ, 0, 0, 0, 0); + if (!errno) + HAVE_PR_CAPBSET_DROP = 1; +#endif +#ifdef PR_SET_SECUREBITS + errno = 0; + prctl(PR_GET_SECUREBITS, 0, 0, 0, 0); + if (!errno) + HAVE_PR_SET_SECUREBITS = 1; +#endif +#ifdef PR_SET_NO_NEW_PRIVS + errno = 0; + prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0); + if (!errno) + HAVE_PR_SET_NO_NEW_PRIVS = 1; +#endif +#ifdef PR_CAP_AMBIENT + errno = 0; + prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, 0, 0, 0); + if (!errno) + HAVE_PR_CAP_AMBIENT = 1; +#endif +} static void init(void) { + // This is so static libs get initialized + init_lib(); + if (m.state != CAPNG_NEW) return; @@ -161,12 +313,19 @@ static void init(void) return; } +#if VFS_CAP_REVISION == VFS_CAP_REVISION_1 + m.vfs_cap_ver = 1; +#else + m.vfs_cap_ver = 2; // Intentionally set to 2 for both 2 & 3 +#endif + memset(&m.data, 0, sizeof(cap_data_t)); #ifdef HAVE_SYSCALL_H m.hdr.pid = (unsigned)syscall(__NR_gettid); #else m.hdr.pid = (unsigned)getpid(); #endif + m.rootid = CAPNG_UNSET_ROOTID; m.state = CAPNG_ALLOCATED; } @@ -180,8 +339,16 @@ void capng_clear(capng_select_t set) if (set & CAPNG_SELECT_CAPS) memset(&m.data, 0, sizeof(cap_data_t)); #ifdef PR_CAPBSET_DROP +if (HAVE_PR_CAPBSET_DROP) { if (set & CAPNG_SELECT_BOUNDS) memset(m.bounds, 0, sizeof(m.bounds)); +} +#endif +#ifdef PR_CAP_AMBIENT +if (HAVE_PR_CAP_AMBIENT) { + if (set & CAPNG_SELECT_AMBIENT) + memset(m.ambient, 0, sizeof(m.ambient)); +} #endif m.state = CAPNG_INIT; } @@ -208,11 +375,22 @@ void capng_fill(capng_select_t set) } } #ifdef PR_CAPBSET_DROP +if (HAVE_PR_CAPBSET_DROP) { if (set & CAPNG_SELECT_BOUNDS) { unsigned i; for (i=0; i<sizeof(m.bounds)/sizeof(__u32); i++) m.bounds[i] = 0xFFFFFFFFU; } +} +#endif +#ifdef PR_CAP_AMBIENT +if (HAVE_PR_CAP_AMBIENT) { + if (set & CAPNG_SELECT_AMBIENT) { + unsigned i; + for (i=0; i<sizeof(m.ambient)/sizeof(__u32); i++) + m.ambient[i] = 0xFFFFFFFFU; + } +} #endif m.state = CAPNG_INIT; } @@ -227,34 +405,128 @@ void capng_setpid(int pid) m.hdr.pid = pid; } +int capng_get_rootid(void) +{ +#ifdef VFS_CAP_REVISION_3 + return m.rootid; +#else + return CAPNG_UNSET_ROOTID; +#endif +} + +int capng_set_rootid(int rootid) +{ +#ifdef VFS_CAP_REVISION_3 + if (m.state == CAPNG_NEW) + init(); + if (m.state == CAPNG_ERROR) + return -1; + + if (rootid < 0) + return -1; + + m.rootid = rootid; + m.vfs_cap_ver = 3; + + return 0; +#else + return -1; +#endif +} + #ifdef PR_CAPBSET_DROP static int get_bounding_set(void) { char buf[64]; FILE *f; + int rc; - snprintf(buf, sizeof(buf), "/proc/%u/status", m.hdr.pid ? m.hdr.pid : + snprintf(buf, sizeof(buf), "/proc/%d/status", m.hdr.pid ? m.hdr.pid : #ifdef HAVE_SYSCALL_H - (unsigned)syscall(__NR_gettid)); + (int)syscall(__NR_gettid)); #else - (unsigned)getpid(); + (int)getpid(); #endif - f = fopen(buf, "r"); - if (f == NULL) - return -1; - __fsetlocking(f, FSETLOCKING_BYCALLER); - while (fgets(buf, sizeof(buf), f)) { - if (strncmp(buf, "CapB", 4)) - continue; - sscanf(buf, "CapBnd: %08x%08x", &m.bounds[1], &m.bounds[0]); + f = fopen(buf, "re"); + if (f) { + __fsetlocking(f, FSETLOCKING_BYCALLER); + while (fgets(buf, sizeof(buf), f)) { + if (strncmp(buf, "CapB", 4)) + continue; + sscanf(buf, "CapBnd: %08x%08x", + &m.bounds[1], &m.bounds[0]); + fclose(f); + return 0; + } + // Didn't find bounding set, fall through and try prctl way fclose(f); - return 0; } - fclose(f); - return -1; + // Might be in a container with no procfs - do it the hard way + memset(m.bounds, 0, sizeof(m.bounds)); + unsigned int i = 0; + do { + rc = prctl(PR_CAPBSET_READ, i, 0, 0, 0); + if (rc < 0) + return -1; + + // Just add set bits + if (rc) + update_bounding_set(CAPNG_ADD, i%32, i>>5); + i++; + } while (cap_valid(i)); + + return 0; +} +#endif + +#ifdef PR_CAP_AMBIENT +static int get_ambient_set(void) +{ + char buf[64]; + FILE *f; + int rc; + + snprintf(buf, sizeof(buf), "/proc/%d/status", m.hdr.pid ? m.hdr.pid : +#ifdef HAVE_SYSCALL_H + (int)syscall(__NR_gettid)); +#else + (int)getpid(); +#endif + f = fopen(buf, "re"); + if (f) { + __fsetlocking(f, FSETLOCKING_BYCALLER); + while (fgets(buf, sizeof(buf), f)) { + if (strncmp(buf, "CapA", 4)) + continue; + sscanf(buf, "CapAmb: %08x%08x", + &m.ambient[1], &m.ambient[0]); + fclose(f); + return 0; + } + fclose(f); + // Didn't find ambient set, fall through and try prctl way + } + // Might be in a container with no procfs - do it the hard way + memset(m.ambient, 0, sizeof(m.ambient)); + unsigned int i = 0; + do { + rc = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, i, 0, 0); + if (rc < 0) + return -1; + + // Just add set bits + if (rc) + update_ambient_set(CAPNG_ADD, i%32, i>>5); + i++; + } while (cap_valid(i)); + + return 0; } #endif +/* + * Returns 0 on success and -1 on failure + */ int capng_get_caps_process(void) { int rc; @@ -268,9 +540,18 @@ int capng_get_caps_process(void) if (rc == 0) { m.state = CAPNG_INIT; #ifdef PR_CAPBSET_DROP +if (HAVE_PR_CAPBSET_DROP) { rc = get_bounding_set(); if (rc < 0) m.state = CAPNG_ERROR; +} +#endif +#ifdef PR_CAP_AMBIENT +if (HAVE_PR_CAP_AMBIENT) { + rc = get_ambient_set(); + if (rc < 0) + m.state = CAPNG_ERROR; +} #endif } @@ -278,10 +559,14 @@ int capng_get_caps_process(void) } #ifdef VFS_CAP_U32 +#ifdef VFS_CAP_REVISION_3 +static int load_data(const struct vfs_ns_cap_data *filedata, int size) +#else static int load_data(const struct vfs_cap_data *filedata, int size) +#endif { unsigned int magic; - + if (m.cap_ver == 1) return -1; // Should never get here but just in case @@ -289,15 +574,22 @@ static int load_data(const struct vfs_cap_data *filedata, int size) switch (magic & VFS_CAP_REVISION_MASK) { case VFS_CAP_REVISION_1: - m.cap_ver = 1; + m.vfs_cap_ver = 1; if (size != XATTR_CAPS_SZ_1) return -1; break; case VFS_CAP_REVISION_2: - m.cap_ver = 2; + m.vfs_cap_ver = 2; if (size != XATTR_CAPS_SZ_2) return -1; break; +#ifdef VFS_CAP_REVISION_3 + case VFS_CAP_REVISION_3: + m.vfs_cap_ver = 3; + if (size != XATTR_CAPS_SZ_3) + return -1; + break; +#endif default: return -1; } @@ -316,6 +608,12 @@ static int load_data(const struct vfs_cap_data *filedata, int size) m.data.v3[0].effective = 0; m.data.v3[1].effective = 0; } +#ifdef VFS_CAP_REVISION_3 + if (size == XATTR_CAPS_SZ_3) { + struct vfs_ns_cap_data *d = (struct vfs_ns_cap_data *)filedata; + m.rootid = FIXUP(d->rootid); + } +#endif return 0; } #endif @@ -326,8 +624,11 @@ int capng_get_caps_fd(int fd) return -1; #else int rc; +#ifdef VFS_CAP_REVISION_3 + struct vfs_ns_cap_data filedata; +#else struct vfs_cap_data filedata; - +#endif if (m.state == CAPNG_NEW) init(); if (m.state == CAPNG_ERROR) @@ -340,6 +641,8 @@ int capng_get_caps_fd(int fd) rc = load_data(&filedata, rc); if (rc == 0) m.state = CAPNG_INIT; + else + m.state = CAPNG_ERROR; // If load data failed, malformed data return rc; #endif @@ -384,10 +687,25 @@ static void update_bounding_set(capng_act_t action, unsigned int capability, unsigned int idx) { #ifdef PR_CAPBSET_DROP +if (HAVE_PR_CAPBSET_DROP) { if (action == CAPNG_ADD) m.bounds[idx] |= MASK(capability); else m.bounds[idx] &= ~(MASK(capability)); +} +#endif +} + +static void update_ambient_set(capng_act_t action, unsigned int capability, + unsigned int idx) +{ +#ifdef PR_CAP_AMBIENT +if (HAVE_PR_CAP_AMBIENT) { + if (action == CAPNG_ADD) + m.ambient[idx] |= MASK(capability); + else + m.ambient[idx] &= ~(MASK(capability)); +} #endif } @@ -409,7 +727,7 @@ int capng_update(capng_act_t action, capng_type_t type, unsigned int capability) if (CAPNG_INHERITABLE & type) v1_update(action, capability, &m.data.v1.inheritable); } else { - int idx; + unsigned int idx; if (capability > 31) { idx = capability>>5; @@ -425,6 +743,8 @@ int capng_update(capng_act_t action, capng_type_t type, unsigned int capability) update_inheritable(action, capability, idx); if (CAPNG_BOUNDING_SET & type) update_bounding_set(action, capability, idx); + if (CAPNG_AMBIENT & type) + update_ambient_set(action, capability, idx); } m.state = CAPNG_UPDATED; @@ -464,51 +784,116 @@ int capng_updatev(capng_act_t action, capng_type_t type, int capng_apply(capng_select_t set) { - int rc = -1; + int rc = 0; // Before updating, we expect that the data is initialized to something if (m.state < CAPNG_INIT) return -1; - if (set & CAPNG_SELECT_BOUNDS) { + if (set & CAPNG_SELECT_BOUNDS) { #ifdef PR_CAPBSET_DROP - void *s = capng_save_state(); - capng_get_caps_process(); +if (HAVE_PR_CAPBSET_DROP) { + struct cap_ng state; + memcpy(&state, &m, sizeof(state)); /* save state */ + if (capng_get_caps_process()) + return -9; if (capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP)) { - int i; - capng_restore_state(&s); - rc = 0; - for (i=0; i <= CAP_LAST_CAP && rc == 0; i++) + unsigned int i; + memcpy(&m, &state, sizeof(m)); /* restore state */ + for (i=0; i <= last_cap; i++) { if (capng_have_capability(CAPNG_BOUNDING_SET, - i) == 0) - rc = prctl(PR_CAPBSET_DROP, i, 0, 0, 0); - if (rc == 0) - m.state = CAPNG_APPLIED; - } else - capng_restore_state(&s); -#else - rc = 0; + i) == 0) { + if (prctl(PR_CAPBSET_DROP, i, 0, 0, 0) <0) { + rc = -2; + goto try_caps; + } + } + } + m.state = CAPNG_APPLIED; + if (get_bounding_set() < 0) { + rc = -3; + goto try_caps; + } + } else { + memcpy(&m, &state, sizeof(m)); /* restore state */ + rc = -4; + goto try_caps; + } +} #endif } + + // Try caps is here so that if someone had SELECT_BOTH and we blew up + // doing the bounding set, we at least try to set any capabilities + // before returning in case the caller also doesn't bother checking + // the return code. +try_caps: if (set & CAPNG_SELECT_CAPS) { - rc = capset((cap_user_header_t)&m.hdr, - (cap_user_data_t)&m.data); - if (rc == 0) + if (capset((cap_user_header_t)&m.hdr, + (cap_user_data_t)&m.data) == 0) m.state = CAPNG_APPLIED; + else + rc = -5; + } + + // Most programs do not and should not mess with ambient capabilities. + // Instead of returning here if rc is set, we'll let it try to + // do something with ambient capabilities in hopes that it's lowering + // capabilities. Again, this is for people that don't check their + // return codes. + // + // Do ambient last so that inheritable and permitted are set by the + // time we get here. + if (set & CAPNG_SELECT_AMBIENT) { +#ifdef PR_CAP_AMBIENT +if (HAVE_PR_CAP_AMBIENT) { + if (capng_have_capabilities(CAPNG_SELECT_AMBIENT) == + CAPNG_NONE) { + if (prctl(PR_CAP_AMBIENT, + PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) < 0) { + rc = -6; + goto out; + } + } else { + unsigned int i; + + // Clear them all + if (prctl(PR_CAP_AMBIENT, + PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) < 0) { + rc = -7; + goto out; + } + for (i=0; i <= last_cap; i++) { + if (capng_have_capability(CAPNG_AMBIENT, i)) + if (prctl(PR_CAP_AMBIENT, + PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0){ + rc = -8; + goto out; + } + } + } + m.state = CAPNG_APPLIED; +} +#endif } +out: return rc; } #ifdef VFS_CAP_U32 +#ifdef VFS_CAP_REVISION_3 +static int save_data(struct vfs_ns_cap_data *filedata, int *size) +#else static int save_data(struct vfs_cap_data *filedata, int *size) +#endif { // Now stuff the data structures - if (m.cap_ver == 1) { + if (m.vfs_cap_ver == 1) { filedata->data[0].permitted = FIXUP(m.data.v1.permitted); filedata->data[0].inheritable = FIXUP(m.data.v1.inheritable); filedata->magic_etc = FIXUP(VFS_CAP_REVISION_1); *size = XATTR_CAPS_SZ_1; - } else { + } else if (m.vfs_cap_ver == 2 || m.vfs_cap_ver == 3) { int eff; if (m.data.v3[0].effective || m.data.v3[1].effective) @@ -522,6 +907,15 @@ static int save_data(struct vfs_cap_data *filedata, int *size) filedata->magic_etc = FIXUP(VFS_CAP_REVISION_2 | eff); *size = XATTR_CAPS_SZ_2; } +#ifdef VFS_CAP_REVISION_3 + if (m.vfs_cap_ver == 3) { + // Kernel doesn't support namespaces with non-0 rootid + if (m.rootid!= 0) + return -1; + filedata->rootid = FIXUP(m.rootid); + *size = XATTR_CAPS_SZ_3; + } +#endif return 0; } @@ -532,8 +926,12 @@ int capng_apply_caps_fd(int fd) #ifndef VFS_CAP_U32 return -1; #else - int rc, size; + int rc, size = 0; +#ifdef VFS_CAP_REVISION_3 + struct vfs_ns_cap_data filedata; +#else struct vfs_cap_data filedata; +#endif struct stat buf; // Before updating, we expect that the data is initialized to something @@ -549,7 +947,11 @@ int capng_apply_caps_fd(int fd) if (capng_have_capabilities(CAPNG_SELECT_CAPS) == CAPNG_NONE) rc = fremovexattr(fd, XATTR_NAME_CAPS); else { - save_data(&filedata, &size); + if (save_data(&filedata, &size)) { + m.state = CAPNG_ERROR; + errno = EINVAL; + return -2; + } rc = fsetxattr(fd, XATTR_NAME_CAPS, &filedata, size, 0); } @@ -560,11 +962,11 @@ int capng_apply_caps_fd(int fd) #endif } -// Change uids keeping/removing only certain capabilities +// Change uids keeping/removing only certain capabilities // flag to drop supp groups int capng_change_id(int uid, int gid, capng_flags_t flag) { - int rc, need_setgid, need_setuid; + int rc, ret, need_setgid, need_setuid; // Before updating, we expect that the data is initialized to something if (m.state < CAPNG_INIT) @@ -572,11 +974,13 @@ int capng_change_id(int uid, int gid, capng_flags_t flag) // Check the current capabilities #ifdef PR_CAPBSET_DROP +if (HAVE_PR_CAPBSET_DROP) { // If newer kernel, we need setpcap to change the bounding set - if (capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP) == 0 && + if (capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP) == 0 && flag & CAPNG_CLEAR_BOUNDING) capng_update(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_SETPCAP); +} #endif if (gid == -1 || capng_have_capability(CAPNG_EFFECTIVE, CAP_SETGID)) need_setgid = 0; @@ -599,35 +1003,67 @@ int capng_change_id(int uid, int gid, capng_flags_t flag) // Change to the temp capabilities rc = capng_apply(CAPNG_SELECT_CAPS); - if (rc < 0) - return -3; + if (rc < 0) { + ret = -3; + goto err_out; + } + + // If we are clearing ambient, only clear since its applied at the end + if (flag & CAPNG_CLEAR_AMBIENT) + capng_clear(CAPNG_SELECT_AMBIENT); // Clear bounding set if needed while we have CAP_SETPCAP if (flag & CAPNG_CLEAR_BOUNDING) { - capng_clear(CAPNG_BOUNDING_SET); + capng_clear(CAPNG_SELECT_BOUNDS); rc = capng_apply(CAPNG_SELECT_BOUNDS); - if (rc) - return -8; + if (rc) { + ret = -8; + goto err_out; + } } // Change gid if (gid != -1) { rc = setresgid(gid, gid, gid); - if (rc) - return -4; + if (rc) { + ret = -4; + goto err_out; + } + } + + // See if we need to init supplemental groups + if ((flag & CAPNG_INIT_SUPP_GRP) && uid != -1) { + struct passwd *pw = getpwuid(uid); + if (pw == NULL) { + ret = -10; + goto err_out; + } + if (gid != -1) { + if (initgroups(pw->pw_name, gid)) { + ret = -5; + goto err_out; + } + } else if (initgroups(pw->pw_name, pw->pw_gid)) { + ret = -5; + goto err_out; + } } // See if we need to unload supplemental groups if ((flag & CAPNG_DROP_SUPP_GRP) && gid != -1) { - if (setgroups(0, NULL)) - return -5; + if (setgroups(0, NULL)) { + ret = -5; + goto err_out; + } } // Change uid if (uid != -1) { rc = setresuid(uid, uid, uid); - if (rc) - return -6; + if (rc) { + ret = -6; + goto err_out; + } } // Tell it we are done keeping capabilities @@ -646,25 +1082,38 @@ int capng_change_id(int uid, int gid, capng_flags_t flag) // Now drop setpcap & apply capng_update(CAPNG_DROP, CAPNG_EFFECTIVE|CAPNG_PERMITTED, CAP_SETPCAP); - rc = capng_apply(CAPNG_SELECT_CAPS); + rc = capng_apply(CAPNG_SELECT_CAPS|CAPNG_SELECT_AMBIENT); if (rc < 0) return -9; // Done m.state = CAPNG_UPDATED; return 0; + +err_out: + prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0); + return ret; } int capng_lock(void) { + // If either fail, return -1 since something is not right #ifdef PR_SET_SECUREBITS +if (HAVE_PR_SET_SECUREBITS) { int rc = prctl(PR_SET_SECUREBITS, 1 << SECURE_NOROOT | 1 << SECURE_NOROOT_LOCKED | 1 << SECURE_NO_SETUID_FIXUP | 1 << SECURE_NO_SETUID_FIXUP_LOCKED, 0, 0, 0); +#ifdef PR_SET_NO_NEW_PRIVS +if (HAVE_PR_SET_NO_NEW_PRIVS) { + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) + return -1; +} +#endif if (rc) return -1; +} #endif return 0; @@ -676,8 +1125,10 @@ capng_results_t capng_have_capabilities(capng_select_t set) int empty = 0, full = 0; // First, try to init with current set - if (m.state < CAPNG_INIT) - capng_get_caps_process(); + if (m.state < CAPNG_INIT) { + if (capng_get_caps_process()) + return CAPNG_FAIL; + } // If we still don't have anything, error out if (m.state < CAPNG_INIT) @@ -702,16 +1153,17 @@ capng_results_t capng_have_capabilities(capng_select_t set) full = 1; else return CAPNG_PARTIAL; - if ((m.data.v3[1].effective & UPPER_MASK) == 0) + if ((m.data.v3[1].effective & UPPER_MASK) == 0 && !full) empty = 1; else if ((m.data.v3[1].effective & UPPER_MASK) == - UPPER_MASK) + UPPER_MASK && !empty) full = 1; else return CAPNG_PARTIAL; } } #ifdef PR_CAPBSET_DROP +if (HAVE_PR_CAPBSET_DROP) { if (set & CAPNG_SELECT_BOUNDS) { if (m.bounds[0] == 0) empty = 1; @@ -726,13 +1178,70 @@ capng_results_t capng_have_capabilities(capng_select_t set) else return CAPNG_PARTIAL; } -#endif +} else + empty = 1; +#endif +#ifdef PR_CAP_AMBIENT +if (HAVE_PR_CAP_AMBIENT) { + if (set & CAPNG_SELECT_AMBIENT) { + if (m.ambient[0] == 0) + empty = 1; + else if (m.ambient[0] == 0xFFFFFFFFU) + full = 1; + else + return CAPNG_PARTIAL; + if ((m.ambient[1] & UPPER_MASK) == 0) + empty = 1; + else if ((m.ambient[1] & UPPER_MASK) == UPPER_MASK) + full = 1; + else + return CAPNG_PARTIAL; + } +} else + empty = 1; +#endif + if (empty == 1 && full == 0) + return CAPNG_NONE; + else if (empty == 0 && full == 1) + return CAPNG_FULL; + + return CAPNG_PARTIAL; +} + +// -1 - error, 0 - no caps, 1 partial caps, 2 full caps +capng_results_t capng_have_permitted_capabilities(void) +{ + int empty = 0, full = 0; + + // First, try to init with current set + if (m.state < CAPNG_INIT) { + if (capng_get_caps_process()) + return CAPNG_FAIL; + } + + // If we still don't have anything, error out + if (m.state < CAPNG_INIT) + return CAPNG_FAIL; + + if (m.data.v3[0].permitted == 0) + empty = 1; + else if (m.data.v3[0].permitted == 0xFFFFFFFFU) + full = 1; + else + return CAPNG_PARTIAL; + + if ((m.data.v3[1].permitted & UPPER_MASK) == 0 && !full) + empty = 1; + else if ((m.data.v3[1].permitted & UPPER_MASK) == UPPER_MASK && !empty) + full = 1; + else + return CAPNG_PARTIAL; if (empty == 1 && full == 0) return CAPNG_NONE; else if (empty == 0 && full == 1) return CAPNG_FULL; - + return CAPNG_PARTIAL; } @@ -754,10 +1263,21 @@ static int check_inheritable(unsigned int capability, unsigned int idx) static int bounds_bit_check(unsigned int capability, unsigned int idx) { #ifdef PR_CAPBSET_DROP +if (HAVE_PR_CAPBSET_DROP) { return MASK(capability) & m.bounds[idx] ? 1 : 0; -#else +} +#endif return 0; +} + +static int ambient_bit_check(unsigned int capability, unsigned int idx) +{ +#ifdef PR_CAP_AMBIENT +if (HAVE_PR_CAP_AMBIENT) { + return MASK(capability) & m.ambient[idx] ? 1 : 0; +} #endif + return 0; } static int v1_check(unsigned int capability, __u32 data) @@ -768,12 +1288,14 @@ static int v1_check(unsigned int capability, __u32 data) int capng_have_capability(capng_type_t which, unsigned int capability) { // First, try to init with current set - if (m.state < CAPNG_INIT) - capng_get_caps_process(); + if (m.state < CAPNG_INIT) { + if (capng_get_caps_process()) + return 0; + } // If we still don't have anything, error out if (m.state < CAPNG_INIT) - return CAPNG_FAIL; + return 0; if (m.cap_ver == 1 && capability > 31) return 0; if (!cap_valid(capability)) @@ -803,6 +1325,8 @@ int capng_have_capability(capng_type_t which, unsigned int capability) return check_inheritable(capability, idx); else if (which == CAPNG_BOUNDING_SET) return bounds_bit_check(capability, idx); + else if (which == CAPNG_AMBIENT) + return ambient_bit_check(capability, idx); } return 0; } @@ -833,17 +1357,25 @@ char *capng_print_caps_numeric(capng_print_t where, capng_select_t set) m.data.v3[0].permitted, m.data.v3[1].inheritable & UPPER_MASK, m.data.v3[0].inheritable); - } } #ifdef PR_CAPBSET_DROP +if (HAVE_PR_CAPBSET_DROP) { if (set & CAPNG_SELECT_BOUNDS) printf("Bounding Set: %08X, %08X\n", m.bounds[1] & UPPER_MASK, m.bounds[0]); +} +#endif +#ifdef PR_CAP_AMBIENT +if (HAVE_PR_CAP_AMBIENT) { + if (set & CAPNG_SELECT_AMBIENT) + printf("Ambient : %08X, %08X\n", + m.ambient[1] & UPPER_MASK, m.ambient[0]); +} #endif } else if (where == CAPNG_PRINT_BUFFER) { if (set & CAPNG_SELECT_CAPS) { - // Make it big enough for bounding set, too + // Make it big enough for bounding & ambient set, too ptr = malloc(160); if (m.cap_ver == 1) { snprintf(ptr, 160, @@ -868,9 +1400,11 @@ char *capng_print_caps_numeric(capng_print_t where, capng_select_t set) } if (set & CAPNG_SELECT_BOUNDS) { #ifdef PR_CAPBSET_DROP +if (HAVE_PR_CAPBSET_DROP) { char *s; - if (ptr == NULL ){ - ptr = malloc(40); + // If ptr is NULL, we only room for bounding and ambient + if (ptr == NULL ) { + ptr = malloc(80); if (ptr == NULL) return ptr; *ptr = 0; @@ -879,6 +1413,26 @@ char *capng_print_caps_numeric(capng_print_t where, capng_select_t set) s = ptr + strlen(ptr); snprintf(s, 40, "Bounding Set: %08X, %08X\n", m.bounds[1] & UPPER_MASK, m.bounds[0]); +} +#endif + } + if (set & CAPNG_SELECT_AMBIENT) { +#ifdef PR_CAP_AMBIENT +if (HAVE_PR_CAP_AMBIENT) { + char *s; + // If ptr is NULL, we only room for ambient + if (ptr == NULL ) { + ptr = malloc(40); + if (ptr == NULL) + return ptr; + *ptr = 0; + s = ptr; + } else + s = ptr + strlen(ptr); + snprintf(s, 40, "Ambient Set: %08X, %08X\n", + m.ambient[1] & UPPER_MASK, + m.ambient[0]); +} #endif } } @@ -888,16 +1442,17 @@ char *capng_print_caps_numeric(capng_print_t where, capng_select_t set) char *capng_print_caps_text(capng_print_t where, capng_type_t which) { - unsigned int i; - int once = 0, cnt = 0; + unsigned int i, once = 0, cnt = 0; char *ptr = NULL; if (m.state < CAPNG_INIT) return ptr; - for (i=0; i<=CAP_LAST_CAP; i++) { + for (i=0; i<=last_cap; i++) { if (capng_have_capability(which, i)) { const char *n = capng_capability_to_name(i); + if (n == NULL) + n = "unknown"; if (where == CAPNG_PRINT_STDOUT) { if (once == 0) { printf("%s", n); @@ -907,7 +1462,7 @@ char *capng_print_caps_text(capng_print_t where, capng_type_t which) } else if (where == CAPNG_PRINT_BUFFER) { int len; if (once == 0) { - ptr = malloc(CAP_LAST_CAP*18); + ptr = malloc(last_cap*20); if (ptr == NULL) return ptr; len = sprintf(ptr+cnt, "%s", n); |