diff options
Diffstat (limited to 'src/cap-ng.c')
-rw-r--r-- | src/cap-ng.c | 949 |
1 files changed, 949 insertions, 0 deletions
diff --git a/src/cap-ng.c b/src/cap-ng.c new file mode 100644 index 0000000..b68aa62 --- /dev/null +++ b/src/cap-ng.c @@ -0,0 +1,949 @@ +/* libcap-ng.c -- + * Copyright 2009-10 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * 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 + * + * Authors: + * Steve Grubb <sgrubb@redhat.com> + */ + +#include "config.h" +#include "cap-ng.h" +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <sys/prctl.h> +#include <grp.h> +#include <sys/stat.h> +#include <stdarg.h> +#include <errno.h> +#include <byteswap.h> +#ifdef HAVE_SYSCALL_H +#include <sys/syscall.h> +#endif +#ifdef HAVE_LINUX_SECUREBITS_H +#include <linux/securebits.h> +#endif + +/* + * 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 + */ + +/* 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 defines +#define MASK(x) (1U << (x)) +#ifdef PR_CAPBSET_DROP +#define UPPER_MASK ~(unsigned)((~0U)<<(CAP_LAST_CAP-31)) +#else +// For v1 systems UPPER_MASK will never be used +#define UPPER_MASK (unsigned)(~0U) +#endif + +// Re-define cap_valid so its uniform between V1 and V3 +#undef cap_valid +#define cap_valid(x) ((x) <= CAP_LAST_CAP) + +// If we don't have the xattr library, then we can't +// compile-in file system capabilities +#ifndef HAVE_ATTR_XATTR_H +#undef VFS_CAP_U32 +#endif + +#ifdef VFS_CAP_U32 + #include <attr/xattr.h> + #if __BYTE_ORDER == __BIG_ENDIAN + #define FIXUP(x) bswap_32(x) + #else + #define FIXUP(x) (x) + #endif +#endif + +#ifndef _LINUX_CAPABILITY_VERSION_1 +#define _LINUX_CAPABILITY_VERSION_1 0x19980330 +#endif +#ifndef _LINUX_CAPABILITY_VERSION_2 +#define _LINUX_CAPABILITY_VERSION_2 0x20071026 +#endif +#ifndef _LINUX_CAPABILITY_VERSION_3 +#define _LINUX_CAPABILITY_VERSION_3 0x20080522 +#endif + +// This public API went private in the 2.6.36 kernel - hope it never changes +#ifndef XATTR_CAPS_SUFFIX +#define XATTR_CAPS_SUFFIX "capability" +#endif +#ifndef XATTR_SECURITY_PREFIX +#define XATTR_SECURITY_PREFIX "security." +#endif +#ifndef XATTR_NAME_CAPS +#define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX +#endif + + +/* Child processes can't get caps back */ +#ifndef SECURE_NOROOT +#define SECURE_NOROOT 0 +#endif +#ifndef SECURE_NOROOT_LOCKED +#define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */ +#endif +/* Setuid apps run by uid 0 don't get caps back */ +#ifndef SECURE_NO_SETUID_FIXUP +#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 + +// States: new, allocated, initted, updated, applied +typedef enum { CAPNG_NEW, CAPNG_ERROR, CAPNG_ALLOCATED, CAPNG_INIT, + CAPNG_UPDATED, CAPNG_APPLIED } capng_states_t; + +// 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]; +} cap_data_t; + +// This struct keeps all state info +struct cap_ng +{ + int cap_ver; + struct __user_cap_header_struct hdr; + cap_data_t data; + capng_states_t state; + __u32 bounds[2]; +}; + +// Global variables with per thread uniqueness +static __thread struct cap_ng m = { 1, + {0, 0}, + { {0, 0, 0} }, + CAPNG_NEW, + {0, 0} }; + + +static void init(void) +{ + if (m.state != CAPNG_NEW) + return; + + memset(&m.hdr, 0, sizeof(m.hdr)); + (void)capget(&m.hdr, NULL); // Returns -EINVAL + if (m.hdr.version == _LINUX_CAPABILITY_VERSION_3 || + m.hdr.version == _LINUX_CAPABILITY_VERSION_2) { + m.cap_ver = 3; + } else if (m.hdr.version == _LINUX_CAPABILITY_VERSION_1) { + m.cap_ver = 1; + } else { + m.state = CAPNG_ERROR; + return; + } + + 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.state = CAPNG_ALLOCATED; +} + +void capng_clear(capng_select_t set) +{ + if (m.state == CAPNG_NEW) + init(); + if (m.state == CAPNG_ERROR) + return; + + if (set & CAPNG_SELECT_CAPS) + memset(&m.data, 0, sizeof(cap_data_t)); +#ifdef PR_CAPBSET_DROP + if (set & CAPNG_SELECT_BOUNDS) + memset(m.bounds, 0, sizeof(m.bounds)); +#endif + m.state = CAPNG_INIT; +} + +void capng_fill(capng_select_t set) +{ + if (m.state == CAPNG_NEW) + init(); + if (m.state == CAPNG_ERROR) + return; + + if (set & CAPNG_SELECT_CAPS) { + if (m.cap_ver == 1) { + m.data.v1.effective = 0x7FFFFFFFU; + m.data.v1.permitted = 0x7FFFFFFFU; + m.data.v1.inheritable = 0; + } else { + m.data.v3[0].effective = 0xFFFFFFFFU; + m.data.v3[0].permitted = 0xFFFFFFFFU; + m.data.v3[0].inheritable = 0; + m.data.v3[1].effective = 0xFFFFFFFFU; + m.data.v3[1].permitted = 0xFFFFFFFFU; + m.data.v3[1].inheritable = 0; + } + } +#ifdef 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 + m.state = CAPNG_INIT; +} + +void capng_setpid(int pid) +{ + if (m.state == CAPNG_NEW) + init(); + if (m.state == CAPNG_ERROR) + return; + + m.hdr.pid = pid; +} + +#ifdef PR_CAPBSET_DROP +static int get_bounding_set(void) +{ + char buf[64]; + FILE *f; + + snprintf(buf, sizeof(buf), "/proc/%u/status", m.hdr.pid ? m.hdr.pid : +#ifdef HAVE_SYSCALL_H + (unsigned)syscall(__NR_gettid)); +#else + (unsigned)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]); + fclose(f); + return 0; + } + fclose(f); + return -1; +} +#endif + +int capng_get_caps_process(void) +{ + int rc; + + if (m.state == CAPNG_NEW) + init(); + if (m.state == CAPNG_ERROR) + return -1; + + rc = capget((cap_user_header_t)&m.hdr, (cap_user_data_t)&m.data); + if (rc == 0) { + m.state = CAPNG_INIT; +#ifdef PR_CAPBSET_DROP + rc = get_bounding_set(); + if (rc < 0) + m.state = CAPNG_ERROR; +#endif + } + + return rc; +} + +#ifdef VFS_CAP_U32 +static int load_data(const struct vfs_cap_data *filedata, int size) +{ + unsigned int magic; + + if (m.cap_ver == 1) + return -1; // Should never get here but just in case + + magic = FIXUP(filedata->magic_etc); + switch (magic & VFS_CAP_REVISION_MASK) + { + case VFS_CAP_REVISION_1: + m.cap_ver = 1; + if (size != XATTR_CAPS_SZ_1) + return -1; + break; + case VFS_CAP_REVISION_2: + m.cap_ver = 2; + if (size != XATTR_CAPS_SZ_2) + return -1; + break; + default: + return -1; + } + + // Now stuff the data structures + m.data.v3[0].permitted = FIXUP(filedata->data[0].permitted); + m.data.v3[1].permitted = FIXUP(filedata->data[1].permitted); + m.data.v3[0].inheritable = FIXUP(filedata->data[0].inheritable); + m.data.v3[1].inheritable = FIXUP(filedata->data[1].inheritable); + if (magic & VFS_CAP_FLAGS_EFFECTIVE) { + m.data.v3[0].effective = + m.data.v3[0].permitted | m.data.v3[0].inheritable; + m.data.v3[1].effective = + m.data.v3[1].permitted | m.data.v3[1].inheritable; + } else { + m.data.v3[0].effective = 0; + m.data.v3[1].effective = 0; + } + return 0; +} +#endif + +int capng_get_caps_fd(int fd) +{ +#ifndef VFS_CAP_U32 + return -1; +#else + int rc; + struct vfs_cap_data filedata; + + if (m.state == CAPNG_NEW) + init(); + if (m.state == CAPNG_ERROR) + return -1; + + rc = fgetxattr(fd, XATTR_NAME_CAPS, &filedata, sizeof(filedata)); + if (rc <= 0) + return -1; + + rc = load_data(&filedata, rc); + if (rc == 0) + m.state = CAPNG_INIT; + + return rc; +#endif +} + +static void v1_update(capng_act_t action, unsigned int capability, __u32 *data) +{ + if (action == CAPNG_ADD) + *data |= MASK(capability); + else + *data &= ~(MASK(capability)); +} + +static void update_effective(capng_act_t action, unsigned int capability, + unsigned int idx) +{ + if (action == CAPNG_ADD) + m.data.v3[idx].effective |= MASK(capability); + else + m.data.v3[idx].effective &= ~(MASK(capability)); +} + +static void update_permitted(capng_act_t action, unsigned int capability, + unsigned int idx) +{ + if (action == CAPNG_ADD) + m.data.v3[idx].permitted |= MASK(capability); + else + m.data.v3[idx].permitted &= ~(MASK(capability)); +} + +static void update_inheritable(capng_act_t action, unsigned int capability, + unsigned int idx) +{ + if (action == CAPNG_ADD) + m.data.v3[idx].inheritable |= MASK(capability); + else + m.data.v3[idx].inheritable &= ~(MASK(capability)); +} + +static void update_bounding_set(capng_act_t action, unsigned int capability, + unsigned int idx) +{ +#ifdef PR_CAPBSET_DROP + if (action == CAPNG_ADD) + m.bounds[idx] |= MASK(capability); + else + m.bounds[idx] &= ~(MASK(capability)); +#endif +} + +int capng_update(capng_act_t action, capng_type_t type, unsigned int capability) +{ + // Before updating, we expect that the data is initialized to something + if (m.state < CAPNG_INIT) + return -1; + if (!cap_valid(capability)) { + errno = EINVAL; + return -1; + } + + if (m.cap_ver == 1) { + if (CAPNG_EFFECTIVE & type) + v1_update(action, capability, &m.data.v1.effective); + if (CAPNG_PERMITTED & type) + v1_update(action, capability, &m.data.v1.permitted); + if (CAPNG_INHERITABLE & type) + v1_update(action, capability, &m.data.v1.inheritable); + } else { + int idx; + + if (capability > 31) { + idx = capability>>5; + capability %= 32; + } else + idx = 0; + + if (CAPNG_EFFECTIVE & type) + update_effective(action, capability, idx); + if (CAPNG_PERMITTED & type) + update_permitted(action, capability, idx); + if (CAPNG_INHERITABLE & type) + update_inheritable(action, capability, idx); + if (CAPNG_BOUNDING_SET & type) + update_bounding_set(action, capability, idx); + } + + m.state = CAPNG_UPDATED; + return 0; +} + +int capng_updatev(capng_act_t action, capng_type_t type, + unsigned int capability, ...) +{ + int rc; + unsigned int cap; + va_list ap; + + rc = capng_update(action, type, capability); + if (rc) + return rc; + va_start(ap, capability); + cap = va_arg(ap, unsigned int); + while (cap_valid(cap)) { + rc = capng_update(action, type, cap); + if (rc) + break; + cap = va_arg(ap, unsigned int); + } + va_end(ap); + + // See if planned exit or invalid + if (cap == (unsigned)-1) + rc = 0; + else { + rc = -1; + errno = EINVAL; + } + + return rc; +} + +int capng_apply(capng_select_t set) +{ + int rc = -1; + + // Before updating, we expect that the data is initialized to something + if (m.state < CAPNG_INIT) + return -1; + + if (set & CAPNG_SELECT_BOUNDS) { +#ifdef PR_CAPBSET_DROP + void *s = capng_save_state(); + capng_get_caps_process(); + 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++) + 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; +#endif + } + if (set & CAPNG_SELECT_CAPS) { + rc = capset((cap_user_header_t)&m.hdr, + (cap_user_data_t)&m.data); + if (rc == 0) + m.state = CAPNG_APPLIED; + } + return rc; +} + +#ifdef VFS_CAP_U32 +static int save_data(struct vfs_cap_data *filedata, int *size) +{ + // Now stuff the data structures + if (m.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 { + int eff; + + if (m.data.v3[0].effective || m.data.v3[1].effective) + eff = VFS_CAP_FLAGS_EFFECTIVE; + else + eff = 0; + filedata->data[0].permitted = FIXUP(m.data.v3[0].permitted); + filedata->data[0].inheritable = FIXUP(m.data.v3[0].inheritable); + filedata->data[1].permitted = FIXUP(m.data.v3[1].permitted); + filedata->data[1].inheritable = FIXUP(m.data.v3[1].inheritable); + filedata->magic_etc = FIXUP(VFS_CAP_REVISION_2 | eff); + *size = XATTR_CAPS_SZ_2; + } + + return 0; +} +#endif + +int capng_apply_caps_fd(int fd) +{ +#ifndef VFS_CAP_U32 + return -1; +#else + int rc, size; + struct vfs_cap_data filedata; + struct stat buf; + + // Before updating, we expect that the data is initialized to something + if (m.state < CAPNG_INIT) + return -1; + + if (fstat(fd, &buf) != 0) + return -1; + if (S_ISLNK(buf.st_mode) || !S_ISREG(buf.st_mode)) { + errno = EINVAL; + return -1; + } + if (capng_have_capabilities(CAPNG_SELECT_CAPS) == CAPNG_NONE) + rc = fremovexattr(fd, XATTR_NAME_CAPS); + else { + save_data(&filedata, &size); + rc = fsetxattr(fd, XATTR_NAME_CAPS, &filedata, size, 0); + } + + if (rc == 0) + m.state = CAPNG_APPLIED; + + return rc; +#endif +} + +// 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; + + // Before updating, we expect that the data is initialized to something + if (m.state < CAPNG_INIT) + return -1; + + // Check the current capabilities +#ifdef PR_CAPBSET_DROP + // If newer kernel, we need setpcap to change the bounding set + 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; + else { + need_setgid = 1; + capng_update(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED, + CAP_SETGID); + } + if (uid == -1 || capng_have_capability(CAPNG_EFFECTIVE, CAP_SETUID)) + need_setuid = 0; + else { + need_setuid = 1; + capng_update(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED, + CAP_SETUID); + } + + // Tell system we want to keep caps across uid change + if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) + return -2; + + // Change to the temp capabilities + rc = capng_apply(CAPNG_SELECT_CAPS); + if (rc < 0) + return -3; + + // Clear bounding set if needed while we have CAP_SETPCAP + if (flag & CAPNG_CLEAR_BOUNDING) { + capng_clear(CAPNG_BOUNDING_SET); + rc = capng_apply(CAPNG_SELECT_BOUNDS); + if (rc) + return -8; + } + + // Change gid + if (gid != -1) { + rc = setresgid(gid, gid, gid); + if (rc) + return -4; + } + + // See if we need to unload supplemental groups + if ((flag & CAPNG_DROP_SUPP_GRP) && gid != -1) { + if (setgroups(0, NULL)) + return -5; + } + + // Change uid + if (uid != -1) { + rc = setresuid(uid, uid, uid); + if (rc) + return -6; + } + + // Tell it we are done keeping capabilities + rc = prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0); + if (rc) + return -7; + + // Now throw away CAP_SETPCAP so no more changes + if (need_setgid) + capng_update(CAPNG_DROP, CAPNG_EFFECTIVE|CAPNG_PERMITTED, + CAP_SETGID); + if (need_setuid) + capng_update(CAPNG_DROP, CAPNG_EFFECTIVE|CAPNG_PERMITTED, + CAP_SETUID); + + // Now drop setpcap & apply + capng_update(CAPNG_DROP, CAPNG_EFFECTIVE|CAPNG_PERMITTED, + CAP_SETPCAP); + rc = capng_apply(CAPNG_SELECT_CAPS); + if (rc < 0) + return -9; + + // Done + m.state = CAPNG_UPDATED; + return 0; +} + +int capng_lock(void) +{ +#ifdef 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); + if (rc) + return -1; +#endif + + return 0; +} + +// -1 - error, 0 - no caps, 1 partial caps, 2 full caps +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 we still don't have anything, error out + if (m.state < CAPNG_INIT) + return CAPNG_FAIL; + + if (set & CAPNG_SELECT_CAPS) { + if (m.cap_ver == 1) { + if (m.data.v1.effective == 0) + empty = 1; + // after fill, 30 bits starts from upper to lower + else if (m.data.v1.effective == 0x7FFFFFFFU) + full = 1; + // actual capabilities read from system + else if (m.data.v1.effective == 0xFFFFFEFFU) + full = 1; + else + return CAPNG_PARTIAL; + } else { + if (m.data.v3[0].effective == 0) + empty = 1; + else if (m.data.v3[0].effective == 0xFFFFFFFFU) + full = 1; + else + return CAPNG_PARTIAL; + if ((m.data.v3[1].effective & UPPER_MASK) == 0) + empty = 1; + else if ((m.data.v3[1].effective & UPPER_MASK) == + UPPER_MASK) + full = 1; + else + return CAPNG_PARTIAL; + } + } +#ifdef PR_CAPBSET_DROP + if (set & CAPNG_SELECT_BOUNDS) { + if (m.bounds[0] == 0) + empty = 1; + else if (m.bounds[0] == 0xFFFFFFFFU) + full = 1; + else + return CAPNG_PARTIAL; + if ((m.bounds[1] & UPPER_MASK) == 0) + empty = 1; + else if ((m.bounds[1] & UPPER_MASK) == UPPER_MASK) + full = 1; + else + return CAPNG_PARTIAL; + } +#endif + + if (empty == 1 && full == 0) + return CAPNG_NONE; + else if (empty == 0 && full == 1) + return CAPNG_FULL; + + return CAPNG_PARTIAL; +} + +static int check_effective(unsigned int capability, unsigned int idx) +{ + return MASK(capability) & m.data.v3[idx].effective ? 1 : 0; +} + +static int check_permitted(unsigned int capability, unsigned int idx) +{ + return MASK(capability) & m.data.v3[idx].permitted ? 1 : 0; +} + +static int check_inheritable(unsigned int capability, unsigned int idx) +{ + return MASK(capability) & m.data.v3[idx].inheritable ? 1 : 0; +} + +static int bounds_bit_check(unsigned int capability, unsigned int idx) +{ +#ifdef PR_CAPBSET_DROP + return MASK(capability) & m.bounds[idx] ? 1 : 0; +#else + return 0; +#endif +} + +static int v1_check(unsigned int capability, __u32 data) +{ + return MASK(capability) & data ? 1 : 0; +} + +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 we still don't have anything, error out + if (m.state < CAPNG_INIT) + return CAPNG_FAIL; + if (m.cap_ver == 1 && capability > 31) + return 0; + if (!cap_valid(capability)) + return 0; + + if (m.cap_ver == 1) { + if (which == CAPNG_EFFECTIVE) + return v1_check(capability, m.data.v1.effective); + else if (which == CAPNG_PERMITTED) + return v1_check(capability, m.data.v1.permitted); + else if (which == CAPNG_INHERITABLE) + return v1_check(capability, m.data.v1.inheritable); + } else { + unsigned int idx; + + if (capability > 31) { + idx = capability>>5; + capability %= 32; + } else + idx = 0; + + if (which == CAPNG_EFFECTIVE) + return check_effective(capability, idx); + else if (which == CAPNG_PERMITTED) + return check_permitted(capability, idx); + else if (which == CAPNG_INHERITABLE) + return check_inheritable(capability, idx); + else if (which == CAPNG_BOUNDING_SET) + return bounds_bit_check(capability, idx); + } + return 0; +} + +char *capng_print_caps_numeric(capng_print_t where, capng_select_t set) +{ + char *ptr = NULL; + + if (m.state < CAPNG_INIT) + return ptr; + + if (where == CAPNG_PRINT_STDOUT) { + if (set & CAPNG_SELECT_CAPS) { + if (m.cap_ver == 1) { + printf( "Effective: %08X\n" + "Permitted: %08X\n" + "Inheritable: %08X\n", + m.data.v1.effective, + m.data.v1.permitted, + m.data.v1.inheritable); + } else { + printf( "Effective: %08X, %08X\n" + "Permitted: %08X, %08X\n" + "Inheritable: %08X, %08X\n", + m.data.v3[1].effective & UPPER_MASK, + m.data.v3[0].effective, + m.data.v3[1].permitted & UPPER_MASK, + m.data.v3[0].permitted, + m.data.v3[1].inheritable & UPPER_MASK, + m.data.v3[0].inheritable); + + } + } +#ifdef PR_CAPBSET_DROP + if (set & CAPNG_SELECT_BOUNDS) + printf("Bounding Set: %08X, %08X\n", + m.bounds[1] & UPPER_MASK, m.bounds[0]); +#endif + } else if (where == CAPNG_PRINT_BUFFER) { + if (set & CAPNG_SELECT_CAPS) { + // Make it big enough for bounding set, too + ptr = malloc(160); + if (m.cap_ver == 1) { + snprintf(ptr, 160, + "Effective: %08X\n" + "Permitted: %08X\n" + "Inheritable: %08X\n", + m.data.v1.effective, + m.data.v1.permitted, + m.data.v1.inheritable); + } else { + snprintf(ptr, 160, + "Effective: %08X, %08X\n" + "Permitted: %08X, %08X\n" + "Inheritable: %08X, %08X\n", + m.data.v3[1].effective & UPPER_MASK, + m.data.v3[0].effective, + m.data.v3[1].permitted & UPPER_MASK, + m.data.v3[0].permitted, + m.data.v3[1].inheritable & UPPER_MASK, + m.data.v3[0].inheritable); + } + } + if (set & CAPNG_SELECT_BOUNDS) { +#ifdef PR_CAPBSET_DROP + char *s; + if (ptr == NULL ){ + ptr = malloc(40); + if (ptr == NULL) + return ptr; + *ptr = 0; + s = ptr; + } else + s = ptr + strlen(ptr); + snprintf(s, 40, "Bounding Set: %08X, %08X\n", + m.bounds[1] & UPPER_MASK, m.bounds[0]); +#endif + } + } + + return ptr; +} + +char *capng_print_caps_text(capng_print_t where, capng_type_t which) +{ + unsigned int i; + int once = 0, cnt = 0; + char *ptr = NULL; + + if (m.state < CAPNG_INIT) + return ptr; + + for (i=0; i<=CAP_LAST_CAP; i++) { + if (capng_have_capability(which, i)) { + const char *n = capng_capability_to_name(i); + if (where == CAPNG_PRINT_STDOUT) { + if (once == 0) { + printf("%s", n); + once++; + } else + printf(", %s", n); + } else if (where == CAPNG_PRINT_BUFFER) { + int len; + if (once == 0) { + ptr = malloc(CAP_LAST_CAP*18); + if (ptr == NULL) + return ptr; + len = sprintf(ptr+cnt, "%s", n); + once++; + } else + len = sprintf(ptr+cnt, ", %s", n); + if (len > 0) + cnt+=len; + } + } + } + if (once == 0) { + if (where == CAPNG_PRINT_STDOUT) + printf("none"); + else + ptr = strdup("none"); + } + return ptr; +} + +void *capng_save_state(void) +{ + void *ptr = malloc(sizeof(m)); + if (ptr) + memcpy(ptr, &m, sizeof(m)); + return ptr; +} + +void capng_restore_state(void **state) +{ + if (state) { + void *ptr = *state; + if (ptr) + memcpy(&m, ptr, sizeof(m)); + free(ptr); + *state = NULL; + } +} + |