| /* 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> |
| #if !defined(ANDROID) |
| #include <stdio_ext.h> |
| #endif |
| #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, "re"); |
| if (f == NULL) |
| return -1; |
| #if !defined(ANDROID) |
| __fsetlocking(f, FSETLOCKING_BYCALLER); |
| #endif |
| 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 (n == NULL) |
| n = "unknown"; |
| 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; |
| } |
| } |
| |