summaryrefslogtreecommitdiff
path: root/src/cap-ng.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cap-ng.c')
-rw-r--r--src/cap-ng.c949
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;
+ }
+}
+