| /* |
| * Copyright (c) 1997-8,2019,2021 Andrew G Morgan <morgan@kernel.org> |
| * |
| * This file deals with allocation and deallocation of internal |
| * capability sets as specified by POSIX.1e (formerlly, POSIX 6). |
| */ |
| |
| #include "libcap.h" |
| |
| /* |
| * Make start up atomic. |
| */ |
| static __u8 __libcap_mutex; |
| |
| /* |
| * These get set via the pre-main() executed constructor function below it. |
| */ |
| static cap_value_t _cap_max_bits; |
| |
| __attribute__((constructor (300))) void _libcap_initialize() |
| { |
| _cap_mu_lock(&__libcap_mutex); |
| if (_cap_max_bits) { |
| _cap_mu_unlock(&__libcap_mutex); |
| return; |
| } |
| cap_set_syscall(NULL, NULL); |
| _binary_search(_cap_max_bits, cap_get_bound, 0, __CAP_MAXBITS, __CAP_BITS); |
| _cap_mu_unlock(&__libcap_mutex); |
| } |
| |
| cap_value_t cap_max_bits(void) |
| { |
| return _cap_max_bits; |
| } |
| |
| /* |
| * capability allocation is all done in terms of this structure. |
| */ |
| struct _cap_alloc_s { |
| __u32 magic; |
| __u32 size; |
| union { |
| struct _cap_struct set; |
| struct cap_iab_s iab; |
| struct cap_launch_s launcher; |
| } u; |
| }; |
| |
| /* |
| * Obtain a blank set of capabilities |
| */ |
| cap_t cap_init(void) |
| { |
| struct _cap_alloc_s *raw_data; |
| cap_t result; |
| |
| raw_data = calloc(1, sizeof(struct _cap_alloc_s)); |
| if (raw_data == NULL) { |
| _cap_debug("out of memory"); |
| errno = ENOMEM; |
| return NULL; |
| } |
| raw_data->magic = CAP_T_MAGIC; |
| raw_data->size = sizeof(struct _cap_alloc_s); |
| |
| result = &raw_data->u.set; |
| result->head.version = _LIBCAP_CAPABILITY_VERSION; |
| capget(&result->head, NULL); /* load the kernel-capability version */ |
| |
| switch (result->head.version) { |
| #ifdef _LINUX_CAPABILITY_VERSION_1 |
| case _LINUX_CAPABILITY_VERSION_1: |
| break; |
| #endif |
| #ifdef _LINUX_CAPABILITY_VERSION_2 |
| case _LINUX_CAPABILITY_VERSION_2: |
| break; |
| #endif |
| #ifdef _LINUX_CAPABILITY_VERSION_3 |
| case _LINUX_CAPABILITY_VERSION_3: |
| break; |
| #endif |
| default: /* No idea what to do */ |
| cap_free(result); |
| result = NULL; |
| break; |
| } |
| |
| return result; |
| } |
| |
| /* |
| * This is an internal library function to duplicate a string and |
| * tag the result as something cap_free can handle. |
| */ |
| char *_libcap_strdup(const char *old) |
| { |
| struct _cap_alloc_s *header; |
| char *raw_data; |
| size_t len; |
| |
| if (old == NULL) { |
| errno = EINVAL; |
| return NULL; |
| } |
| len = strlen(old) + 1 + 2*sizeof(__u32); |
| if (len < sizeof(struct _cap_alloc_s)) { |
| len = sizeof(struct _cap_alloc_s); |
| } |
| if ((len & 0xffffffff) != len) { |
| _cap_debug("len is too long for libcap to manage"); |
| errno = EINVAL; |
| return NULL; |
| } |
| |
| raw_data = calloc(1, len); |
| if (raw_data == NULL) { |
| errno = ENOMEM; |
| return NULL; |
| } |
| header = (void *) raw_data; |
| header->magic = CAP_S_MAGIC; |
| header->size = (__u32) len; |
| |
| raw_data += 2*sizeof(__u32); |
| strcpy(raw_data, old); |
| return raw_data; |
| } |
| |
| /* |
| * This function duplicates an internal capability set with |
| * calloc()'d memory. It is the responsibility of the user to call |
| * cap_free() to liberate it. |
| */ |
| cap_t cap_dup(cap_t cap_d) |
| { |
| cap_t result; |
| |
| if (!good_cap_t(cap_d)) { |
| _cap_debug("bad argument"); |
| errno = EINVAL; |
| return NULL; |
| } |
| |
| result = cap_init(); |
| if (result == NULL) { |
| _cap_debug("out of memory"); |
| return NULL; |
| } |
| |
| _cap_mu_lock(&cap_d->mutex); |
| memcpy(result, cap_d, sizeof(*cap_d)); |
| _cap_mu_unlock(&cap_d->mutex); |
| _cap_mu_unlock(&result->mutex); |
| |
| return result; |
| } |
| |
| cap_iab_t cap_iab_init(void) |
| { |
| struct _cap_alloc_s *base = calloc(1, sizeof(struct _cap_alloc_s)); |
| if (base == NULL) { |
| _cap_debug("out of memory"); |
| return NULL; |
| } |
| base->magic = CAP_IAB_MAGIC; |
| base->size = sizeof(struct _cap_alloc_s); |
| return &base->u.iab; |
| } |
| |
| /* |
| * This function duplicates an internal iab tuple with calloc()'d |
| * memory. It is the responsibility of the user to call cap_free() to |
| * liberate it. |
| */ |
| cap_iab_t cap_iab_dup(cap_iab_t iab) |
| { |
| cap_iab_t result; |
| |
| if (!good_cap_iab_t(iab)) { |
| _cap_debug("bad argument"); |
| errno = EINVAL; |
| return NULL; |
| } |
| |
| result = cap_iab_init(); |
| if (result == NULL) { |
| _cap_debug("out of memory"); |
| return NULL; |
| } |
| |
| _cap_mu_lock(&iab->mutex); |
| memcpy(result, iab, sizeof(*iab)); |
| _cap_mu_unlock(&iab->mutex); |
| _cap_mu_unlock(&result->mutex); |
| |
| return result; |
| } |
| |
| /* |
| * cap_new_launcher allocates some memory for a launcher and |
| * initializes it. To actually launch a program with this launcher, |
| * use cap_launch(). By default, the launcher is a no-op from a |
| * security perspective and will act just as fork()/execve() |
| * would. Use cap_launcher_setuid() etc to override this. |
| */ |
| cap_launch_t cap_new_launcher(const char *arg0, const char * const *argv, |
| const char * const *envp) |
| { |
| struct _cap_alloc_s *data = calloc(1, sizeof(struct _cap_alloc_s)); |
| if (data == NULL) { |
| _cap_debug("out of memory"); |
| return NULL; |
| } |
| data->magic = CAP_LAUNCH_MAGIC; |
| data->size = sizeof(struct _cap_alloc_s); |
| |
| struct cap_launch_s *attr = &data->u.launcher; |
| attr->arg0 = arg0; |
| attr->argv = argv; |
| attr->envp = envp; |
| return attr; |
| } |
| |
| /* |
| * cap_func_launcher allocates some memory for a launcher and |
| * initializes it. The purpose of this launcher, unlike one created |
| * with cap_new_launcher(), is to execute some function code from a |
| * forked copy of the program. The forked process will exit when the |
| * callback function, func, returns. |
| */ |
| cap_launch_t cap_func_launcher(int (callback_fn)(void *detail)) |
| { |
| struct _cap_alloc_s *data = calloc(1, sizeof(struct _cap_alloc_s)); |
| if (data == NULL) { |
| _cap_debug("out of memory"); |
| return NULL; |
| } |
| data->magic = CAP_LAUNCH_MAGIC; |
| data->size = sizeof(struct _cap_alloc_s); |
| |
| struct cap_launch_s *attr = &data->u.launcher; |
| attr->custom_setup_fn = callback_fn; |
| return attr; |
| } |
| |
| /* |
| * Scrub and then liberate the recognized allocated object. |
| */ |
| int cap_free(void *data_p) |
| { |
| if (!data_p) { |
| return 0; |
| } |
| |
| /* confirm alignment */ |
| if ((sizeof(uintptr_t)-1) & (uintptr_t) data_p) { |
| _cap_debug("whatever we're cap_free()ing it isn't aligned right: %p", |
| data_p); |
| errno = EINVAL; |
| return -1; |
| } |
| |
| void *base = (void *) (-2 + (__u32 *) data_p); |
| struct _cap_alloc_s *data = base; |
| switch (data->magic) { |
| case CAP_T_MAGIC: |
| _cap_mu_lock(&data->u.set.mutex); |
| break; |
| case CAP_S_MAGIC: |
| case CAP_IAB_MAGIC: |
| break; |
| case CAP_LAUNCH_MAGIC: |
| if (data->u.launcher.iab != NULL) { |
| _cap_mu_unlock(&data->u.launcher.iab->mutex); |
| if (cap_free(data->u.launcher.iab) != 0) { |
| return -1; |
| } |
| } |
| data->u.launcher.iab = NULL; |
| if (cap_free(data->u.launcher.chroot) != 0) { |
| return -1; |
| } |
| data->u.launcher.chroot = NULL; |
| break; |
| default: |
| _cap_debug("don't recognize what we're supposed to liberate"); |
| errno = EINVAL; |
| return -1; |
| } |
| |
| /* |
| * operate here with respect to base, to avoid tangling with the |
| * automated buffer overflow detection. |
| */ |
| memset(base, 0, data->size); |
| free(base); |
| data_p = NULL; |
| data = NULL; |
| base = NULL; |
| return 0; |
| } |