aboutsummaryrefslogtreecommitdiff
path: root/libcap/cap_alloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcap/cap_alloc.c')
-rw-r--r--libcap/cap_alloc.c238
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;
}