diff options
Diffstat (limited to 'libcap/cap_alloc.c')
-rw-r--r-- | libcap/cap_alloc.c | 238 |
1 files changed, 163 insertions, 75 deletions
diff --git a/libcap/cap_alloc.c b/libcap/cap_alloc.c index 88ba6da..504abd2 100644 --- a/libcap/cap_alloc.c +++ b/libcap/cap_alloc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-8,2019 Andrew G Morgan <morgan@kernel.org> + * 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). @@ -8,41 +8,65 @@ #include "libcap.h" /* - * This gets set via the pre-main() executed constructor function below it. + * 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))) static void _initialize_libcap(void) { - if (_cap_max_bits) { - return; +__attribute__((visibility ("hidden"))) +__attribute__((constructor (300))) void _libcap_initialize(void) +{ + int errno_saved = errno; + _cap_mu_lock(&__libcap_mutex); + if (!_cap_max_bits) { + cap_set_syscall(NULL, NULL); + _binary_search(_cap_max_bits, cap_get_bound, 0, __CAP_MAXBITS, + __CAP_BITS); } - cap_set_syscall(NULL, NULL); - _binary_search(_cap_max_bits, cap_get_bound, 0, __CAP_MAXBITS, __CAP_BITS); + _cap_mu_unlock(&__libcap_mutex); + errno = errno_saved; } -cap_value_t cap_max_bits(void) { +cap_value_t cap_max_bits(void) +{ return _cap_max_bits; } /* - * Obtain a blank set of capabilities + * 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) { - __u32 *raw_data; + struct _cap_alloc_s *raw_data; cap_t result; - raw_data = calloc(1, sizeof(__u32) + sizeof(*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); - *raw_data = CAP_T_MAGIC; - result = (cap_t) (raw_data + 1); - + result = &raw_data->u.set; result->head.version = _LIBCAP_CAPABILITY_VERSION; capget(&result->head, NULL); /* load the kernel-capability version */ @@ -72,34 +96,47 @@ cap_t cap_init(void) * 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) +__attribute__((visibility ("hidden"))) char *_libcap_strdup(const char *old) { - __u32 *raw_data; + struct _cap_alloc_s *header; + char *raw_data; + size_t len; if (old == NULL) { errno = EINVAL; return NULL; } - raw_data = malloc( sizeof(__u32) + strlen(old) + 1 ); + len = strlen(old); + if ((len & 0x3fffffff) != len) { + _cap_debug("len is too long for libcap to manage"); + errno = EINVAL; + return NULL; + } + len += 1 + 2*sizeof(__u32); + if (len < sizeof(struct _cap_alloc_s)) { + len = sizeof(struct _cap_alloc_s); + } + + 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++) = CAP_S_MAGIC; - strcpy((char *) raw_data, old); - - return ((char *) raw_data); + raw_data += 2*sizeof(__u32); + strcpy(raw_data, old); + return raw_data; } /* * This function duplicates an internal capability set with - * malloc()'d memory. It is the responsibility of the user to call + * 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; @@ -116,15 +153,53 @@ cap_t cap_dup(cap_t cap_d) 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) { - __u32 *base = calloc(1, sizeof(__u32) + sizeof(struct cap_iab_s)); - *(base++) = CAP_IAB_MAGIC; - return (cap_iab_t) base; +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; } /* @@ -137,9 +212,15 @@ cap_iab_t cap_iab_init(void) { cap_launch_t cap_new_launcher(const char *arg0, const char * const *argv, const char * const *envp) { - __u32 *data = calloc(1, sizeof(__u32) + sizeof(struct cap_launch_s)); - *(data++) = CAP_LAUNCH_MAGIC; - struct cap_launch_s *attr = (struct cap_launch_s *) data; + 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; @@ -155,65 +236,72 @@ cap_launch_t cap_new_launcher(const char *arg0, const char * const *argv, */ cap_launch_t cap_func_launcher(int (callback_fn)(void *detail)) { - __u32 *data = calloc(1, sizeof(__u32) + sizeof(struct cap_launch_s)); - *(data++) = CAP_LAUNCH_MAGIC; - struct cap_launch_s *attr = (struct cap_launch_s *) data; + 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 an internal capability set. + * Scrub and then liberate the recognized allocated object. */ - int cap_free(void *data_p) { - if (!data_p) + if (!data_p) { return 0; - - if (good_cap_t(data_p)) { - data_p = -1 + (__u32 *) data_p; - memset(data_p, 0, sizeof(__u32) + sizeof(struct _cap_struct)); - free(data_p); - data_p = NULL; - return 0; - } - - if (good_cap_string(data_p)) { - size_t length = strlen(data_p) + sizeof(__u32); - data_p = -1 + (__u32 *) data_p; - memset(data_p, 0, length); - free(data_p); - data_p = NULL; - return 0; } - if (good_cap_iab_t(data_p)) { - size_t length = sizeof(struct cap_iab_s) + sizeof(__u32); - data_p = -1 + (__u32 *) data_p; - memset(data_p, 0, length); - free(data_p); - data_p = NULL; - 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; } - if (good_cap_launch_t(data_p)) { - cap_launch_t launcher = data_p; - if (launcher->iab) { - cap_free(launcher->iab); + 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; + } } - if (launcher->chroot) { - cap_free(launcher->chroot); + data->u.launcher.iab = NULL; + if (cap_free(data->u.launcher.chroot) != 0) { + return -1; } - size_t length = sizeof(struct cap_iab_s) + sizeof(__u32); - data_p = -1 + (__u32 *) data_p; - memset(data_p, 0, length); - free(data_p); - data_p = NULL; - return 0; + data->u.launcher.chroot = NULL; + break; + default: + _cap_debug("don't recognize what we're supposed to liberate"); + errno = EINVAL; + return -1; } - _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; } |