diff options
Diffstat (limited to 'libcap')
-rw-r--r-- | libcap/Makefile | 53 | ||||
-rw-r--r-- | libcap/_makenames.c | 8 | ||||
-rw-r--r-- | libcap/cap_alloc.c | 238 | ||||
-rw-r--r-- | libcap/cap_extint.c | 72 | ||||
-rw-r--r-- | libcap/cap_file.c | 40 | ||||
-rw-r--r-- | libcap/cap_flag.c | 124 | ||||
-rw-r--r-- | libcap/cap_names.header | 5 | ||||
-rw-r--r-- | libcap/cap_proc.c | 244 | ||||
-rw-r--r-- | libcap/cap_test.c | 248 | ||||
-rw-r--r-- | libcap/cap_text.c | 192 | ||||
-rw-r--r-- | libcap/execable.c | 51 | ||||
-rw-r--r-- | libcap/execable.h | 49 | ||||
-rw-r--r-- | libcap/include/sys/capability.h | 50 | ||||
-rw-r--r-- | libcap/include/uapi/linux/capability.h | 5 | ||||
-rw-r--r-- | libcap/libcap.h | 46 | ||||
-rw-r--r-- | libcap/psx_exec.c | 15 |
16 files changed, 1145 insertions, 295 deletions
diff --git a/libcap/Makefile b/libcap/Makefile index b5689d2..f5dde3e 100644 --- a/libcap/Makefile +++ b/libcap/Makefile @@ -18,6 +18,9 @@ CAPMAGICOBJ=cap_magic.o PSXFILES=../psx/psx PSXMAGICOBJ=psx_magic.o +# Always build libcap sources this way: +CFLAGS += -fPIC + # The linker magic needed to build a dynamic library as independently # executable MAGIC=-Wl,-e,__so_start @@ -49,13 +52,13 @@ ifeq ($(PTHREADS),yes) $(MAKE) $(PSXTITLE).pc endif -ifeq ($(BUILD_GPERF),yes) +ifeq ($(USE_GPERF),yes) USE_GPERF_OUTPUT = $(GPERF_OUTPUT) INCLUDE_GPERF_OUTPUT = -DINCLUDE_GPERF_OUTPUT='"$(GPERF_OUTPUT)"' endif $(LIBTITLE).pc: $(LIBTITLE).pc.in - sed -e 's,@prefix@,$(prefix),' \ + $(BUILD_SED) -e 's,@prefix@,$(prefix),' \ -e 's,@exec_prefix@,$(exec_prefix),' \ -e 's,@libdir@,$(LIBDIR),' \ -e 's,@includedir@,$(inc_prefix)/include,' \ @@ -64,7 +67,7 @@ $(LIBTITLE).pc: $(LIBTITLE).pc.in $< >$@ $(PSXTITLE).pc: $(PSXTITLE).pc.in - sed -e 's,@prefix@,$(prefix),' \ + $(BUILD_SED) -e 's,@prefix@,$(prefix),' \ -e 's,@exec_prefix@,$(exec_prefix),' \ -e 's,@libdir@,$(LIBDIR),' \ -e 's,@includedir@,$(inc_prefix)/include,' \ @@ -73,23 +76,27 @@ $(PSXTITLE).pc: $(PSXTITLE).pc.in $< >$@ _makenames: _makenames.c cap_names.list.h - $(BUILD_CC) $(BUILD_CFLAGS) $< -o $@ + $(BUILD_CC) $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $< -o $@ cap_names.h: _makenames ./_makenames > cap_names.h -$(GPERF_OUTPUT): cap_names.list.h - perl -e 'print "struct __cap_token_s { const char *name; int index; };\n%{\nconst struct __cap_token_s *__cap_lookup_name(const char *, size_t);\n%}\n%%\n"; while ($$l = <>) { $$l =~ s/[\{\"]//g; $$l =~ s/\}.*// ; print $$l; }' < $< | gperf --ignore-case --language=ANSI-C --readonly --null-strings --global-table --hash-function-name=__cap_hash_name --lookup-function-name="__cap_lookup_name" -c -t -m20 $(INDENT) > $@ - sed -e 's/unsigned int len/size_t len/' -i $@ +$(GPERF_OUTPUT): cap_names.list.h cap_names.header Makefile + (cat cap_names.header ; $(BUILD_SED) -e 's/[\{\}"]//g' -e 's/,$$//' cap_names.list.h) | gperf --ignore-case --language=ANSI-C --readonly --null-strings --global-table --hash-function-name=__cap_hash_name --lookup-function-name="__cap_lookup_name" -c -t -m20 $(INDENT) > $@ + $(BUILD_SED) -e 's/unsigned int len/size_t len/' -i $@ # Intention is that libcap keeps up with torvalds' tree, as reflected # by this maintained version of the kernel header. libcap dynamically # trims the meaning of "all" capabilities down to that of the running -# kernel as of 2.30. +# kernel as of 2.30. That is, all production kernels should be equal +# to or behind libcap. +# +# Note "./libcap.so --summary" should explain how the built libcap.so +# compares to the running kernel. UAPI_HEADER := $(topdir)/libcap/include/uapi/linux/capability.h cap_names.list.h: Makefile $(UAPI_HEADER) @echo "=> making $@ from $(UAPI_HEADER)" - perl -e 'while ($$l=<>) { if ($$l =~ /^\#define[ \t](CAP[_A-Z]+)[ \t]+([0-9]+)\s+$$/) { $$tok=$$1; $$val=$$2; $$tok =~ tr/A-Z/a-z/; print "{\"$$tok\",$$val},\n"; } }' $(UAPI_HEADER) | fgrep -v 0x > $@ + $(BUILD_EGREP) '^#define\s+CAP_([^\s]+)\s+[0-9]+\s*$$' include/uapi/linux/capability.h | $(BUILD_SED) -e 's/^#define\s\+/{"/' -e 's/\s*$$/},/' -e 's/\s\+/",/' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' > $@ $(STACAPLIBNAME): $(CAPOBJS) $(AR) rcs $@ $^ @@ -105,18 +112,18 @@ empty: empty.c $(CC) -o $@ $< loader.txt: empty - $(OBJCOPY) --dump-section .interp=$@ $< + $(OBJCOPY) --dump-section .interp=$@ $< /dev/null -cap_magic.o: execable.h execable.c loader.txt - $(CC) $(CFLAGS) $(IPATH) -DLIBRARY_VERSION=\"$(LIBTITLE)-$(VERSION).$(MINOR)\" -DSHARED_LOADER=\"$(shell cat loader.txt)\" -c execable.c -o $@ +cap_magic.o: execable.h execable.c loader.txt libcap.h + $(CC) $(CFLAGS) $(CPPFLAGS) -DLIBRARY_VERSION=\"$(LIBTITLE)-$(VERSION).$(MINOR)\" -DSHARED_LOADER=\"$(shell cat loader.txt)\" -include ./libcap.h -c execable.c -o $@ $(CAPLIBNAME) $(MAJCAPLIBNAME) $(MINCAPLIBNAME): $(CAPOBJS) $(CAPMAGICOBJ) $(LD) $(CFLAGS) $(LDFLAGS) -Wl,-soname,$(MAJCAPLIBNAME) -o $(MINCAPLIBNAME) $^ $(MAGIC) ln -sf $(MINCAPLIBNAME) $(MAJCAPLIBNAME) ln -sf $(MAJCAPLIBNAME) $(CAPLIBNAME) -psx_magic.o: execable.h execable.c loader.txt - $(CC) $(CFLAGS) $(IPATH) -DLIBRARY_VERSION=\"$(PSXTITLE)-$(VERSION).$(MINOR)\" -DSHARED_LOADER=\"$(shell cat loader.txt)\" -c execable.c -o $@ +psx_magic.o: execable.h psx_exec.c loader.txt + $(CC) $(CFLAGS) $(CPPFLAGS) -DLIBRARY_VERSION=\"$(PSXTITLE)-$(VERSION).$(MINOR)\" -DSHARED_LOADER=\"$(shell cat loader.txt)\" -c psx_exec.c -o $@ $(PSXLIBNAME) $(MAJPSXLIBNAME) $(MINPSXLIBNAME): $(PSXOBJS) include/sys/psx_syscall.h $(PSXMAGICOBJ) $(LD) $(CFLAGS) $(LDFLAGS) -Wl,-soname,$(MAJPSXLIBNAME) -o $(MINPSXLIBNAME) $(PSXOBJS) $(PSXMAGICOBJ) $(MAGIC) $(PSXLINKFLAGS) @@ -125,16 +132,19 @@ $(PSXLIBNAME) $(MAJPSXLIBNAME) $(MINPSXLIBNAME): $(PSXOBJS) include/sys/psx_sysc endif %.o: %.c $(INCLS) - $(CC) $(CFLAGS) $(IPATH) -c $< -o $@ + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ cap_text.o: cap_text.c $(USE_GPERF_OUTPUT) $(INCLS) - $(CC) $(CFLAGS) $(IPATH) $(INCLUDE_GPERF_OUTPUT) -c $< -o $@ + $(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDE_GPERF_OUTPUT) -c $< -o $@ -cap_test: cap_test.c libcap.h $(CAPOBJS) - $(CC) $(CFLAGS) $(IPATH) $< $(CAPOBJS) -o $@ +cap_test: cap_test.c $(INCLS) $(CAPOBJS) + $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< $(CAPOBJS) -o $@ libcapsotest: $(CAPLIBNAME) ./$(CAPLIBNAME) + ./$(CAPLIBNAME) --usage + ./$(CAPLIBNAME) --help + ./$(CAPLIBNAME) --summary libpsxsotest: $(PSXLIBNAME) ./$(PSXLIBNAME) @@ -148,6 +158,9 @@ ifeq ($(PTHREADS),yes) endif endif +sudotest: + @echo no sudotests for libcap + install: install-static ifeq ($(SHARED),yes) $(MAKE) install-shared @@ -177,7 +190,7 @@ install-static-cap: install-common-cap $(STACAPLIBNAME) install -m 0644 $(STACAPLIBNAME) $(FAKEROOT)$(LIBDIR)/$(STACAPLIBNAME) install-shared-cap: install-common-cap $(MINCAPLIBNAME) - install -m 0644 $(MINCAPLIBNAME) $(FAKEROOT)$(LIBDIR)/$(MINCAPLIBNAME) + install -m 0755 $(MINCAPLIBNAME) $(FAKEROOT)$(LIBDIR)/$(MINCAPLIBNAME) ln -sf $(MINCAPLIBNAME) $(FAKEROOT)$(LIBDIR)/$(MAJCAPLIBNAME) ln -sf $(MAJCAPLIBNAME) $(FAKEROOT)$(LIBDIR)/$(CAPLIBNAME) ifeq ($(FAKEROOT),) @@ -188,7 +201,7 @@ install-static-psx: install-common-psx $(STAPSXLIBNAME) install -m 0644 $(STAPSXLIBNAME) $(FAKEROOT)$(LIBDIR)/$(STAPSXLIBNAME) install-shared-psx: install-common-psx $(MINPSXLIBNAME) - install -m 0644 $(MINPSXLIBNAME) $(FAKEROOT)$(LIBDIR)/$(MINPSXLIBNAME) + install -m 0755 $(MINPSXLIBNAME) $(FAKEROOT)$(LIBDIR)/$(MINPSXLIBNAME) ln -sf $(MINPSXLIBNAME) $(FAKEROOT)$(LIBDIR)/$(MAJPSXLIBNAME) ln -sf $(MAJPSXLIBNAME) $(FAKEROOT)$(LIBDIR)/$(PSXLIBNAME) ifeq ($(FAKEROOT),) diff --git a/libcap/_makenames.c b/libcap/_makenames.c index 46ab0c9..30eb080 100644 --- a/libcap/_makenames.c +++ b/libcap/_makenames.c @@ -26,7 +26,7 @@ struct { * indicated extended empty space. */ static void *recalloc(void *p, int was, int is) { - void *n = realloc(p, is); + char *n = realloc(p, is); if (!n) { fputs("out of memory", stderr); exit(1); @@ -45,10 +45,14 @@ int main(void) if (maxcaps <= list[i].index) { maxcaps = list[i].index + 1; } - if (list[i].index >= pointers_avail) { + if (pointers == NULL || list[i].index >= pointers_avail) { int was = pointers_avail * sizeof(char *); pointers_avail = 2 * list[i].index + 1; pointers = recalloc(pointers, was, pointers_avail * sizeof(char *)); + if (pointers == NULL) { + perror("unable to continue"); + exit(1); + } } pointers[list[i].index] = list[i].name; int n = strlen(list[i].name); 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; } diff --git a/libcap/cap_extint.c b/libcap/cap_extint.c index bf0967b..462cc65 100644 --- a/libcap/cap_extint.c +++ b/libcap/cap_extint.c @@ -36,40 +36,49 @@ struct cap_ext_struct { */ static size_t _libcap_min_ext_flag_size = CAP_SET_SIZE < 8 ? CAP_SET_SIZE : 8; +static ssize_t _cap_size_locked(cap_t cap_d) +{ + size_t j, used; + for (j=used=0; j<CAP_SET_SIZE; j+=sizeof(__u32)) { + int i; + __u32 val = 0; + for (i=0; i<NUMBER_OF_CAP_SETS; ++i) { + val |= cap_d->u[j/sizeof(__u32)].flat[i]; + } + if (val == 0) { + continue; + } + if (val > 0x0000ffff) { + if (val > 0x00ffffff) { + used = j+4; + } else { + used = j+3; + } + } else if (val > 0x000000ff) { + used = j+2; + } else { + used = j+1; + } + } + if (used < _libcap_min_ext_flag_size) { + used = _libcap_min_ext_flag_size; + } + return (ssize_t)(CAP_EXT_MAGIC_SIZE + 1+ NUMBER_OF_CAP_SETS * used); +} + /* * return size of external capability set */ ssize_t cap_size(cap_t cap_d) { - if (good_cap_t(cap_d)) { - size_t j, used; - for (j=used=0; j<CAP_SET_SIZE; j+=sizeof(__u32)) { - int i; - __u32 val = 0; - for (i=0; i<NUMBER_OF_CAP_SETS; ++i) { - val |= cap_d->u[j/sizeof(__u32)].flat[i]; - } - if (val == 0) { - continue; - } - if (val > 0x0000ffff) { - if (val > 0x00ffffff) { - used = j+4; - } else { - used = j+3; - } - } else if (val > 0x000000ff) { - used = j+2; - } else { - used = j+1; - } - } - if (used < _libcap_min_ext_flag_size) { - used = _libcap_min_ext_flag_size; - } - return (ssize_t)(CAP_EXT_MAGIC_SIZE + 1+ NUMBER_OF_CAP_SETS * used); + size_t used; + if (!good_cap_t(cap_d)) { + return ssizeof(struct cap_ext_struct); } - return ssizeof(struct cap_ext_struct); + _cap_mu_lock(&cap_d->mutex); + used = _cap_size_locked(cap_d); + _cap_mu_unlock(&cap_d->mutex); + return used; } /* @@ -90,10 +99,11 @@ ssize_t cap_copy_ext(void *cap_ext, cap_t cap_d, ssize_t length) return -1; } - csz = cap_size(cap_d); + _cap_mu_lock(&cap_d->mutex); + csz = _cap_size_locked(cap_d); if (csz > length) { errno = EINVAL; - return -1; + _cap_mu_unlock_return(&cap_d->mutex, -1); } len_set = (csz - (CAP_EXT_MAGIC_SIZE+1))/NUMBER_OF_CAP_SETS; @@ -122,7 +132,7 @@ ssize_t cap_copy_ext(void *cap_ext, cap_t cap_d, ssize_t length) } /* All done: return length of external representation */ - return csz; + _cap_mu_unlock_return(&cap_d->mutex, csz); } /* diff --git a/libcap/cap_file.c b/libcap/cap_file.c index 84ae3e1..0bc07f7 100644 --- a/libcap/cap_file.c +++ b/libcap/cap_file.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1997,2007,2016 Andrew G Morgan <morgan@kernel.org> * - * This file deals with setting capabilities on files. + * This file deals with getting/setting capabilities from/on files. */ #ifndef _DEFAULT_SOURCE @@ -12,7 +12,6 @@ #include <byteswap.h> #include <sys/stat.h> #include <unistd.h> -#include <linux/xattr.h> /* * We hardcode the prototypes for the Linux system calls here since @@ -127,6 +126,7 @@ static int _fcaps_save(struct vfs_ns_cap_data *rawvfscap, cap_t cap_d, errno = EINVAL; return -1; } + _cap_mu_lock(&cap_d->mutex); switch (cap_d->head.version) { case _LINUX_CAPABILITY_VERSION_1: @@ -144,14 +144,14 @@ static int _fcaps_save(struct vfs_ns_cap_data *rawvfscap, cap_t cap_d, default: errno = EINVAL; - return -1; + _cap_mu_unlock_return(&cap_d->mutex, -1); } if (cap_d->rootid != 0) { if (cap_d->head.version < _LINUX_CAPABILITY_VERSION_3) { _cap_debug("namespaces with non-0 rootid unsupported by kernel"); errno = EINVAL; - return -1; + _cap_mu_unlock_return(&cap_d->mutex, -1); } magic = VFS_CAP_REVISION_3; tocopy = VFS_CAP_U32_3; @@ -172,7 +172,7 @@ static int _fcaps_save(struct vfs_ns_cap_data *rawvfscap, cap_t cap_d, * System does not support these capabilities */ errno = EINVAL; - return -1; + _cap_mu_unlock_return(&cap_d->mutex, -1); } i++; } @@ -188,7 +188,7 @@ static int _fcaps_save(struct vfs_ns_cap_data *rawvfscap, cap_t cap_d, & (cap_d->u[i].flat[CAP_PERMITTED] | cap_d->u[i].flat[CAP_INHERITABLE]))) { errno = EINVAL; - return -1; + _cap_mu_unlock_return(&cap_d->mutex, -1); } } @@ -198,7 +198,7 @@ static int _fcaps_save(struct vfs_ns_cap_data *rawvfscap, cap_t cap_d, rawvfscap->magic_etc = FIXUP_32BITS(magic|VFS_CAP_FLAGS_EFFECTIVE); } - return 0; /* success */ + _cap_mu_unlock_return(&cap_d->mutex, 0); /* success */ } /* @@ -269,7 +269,15 @@ cap_t cap_get_file(const char *filename) uid_t cap_get_nsowner(cap_t cap_d) { - return cap_d->rootid; + uid_t nsowner; + if (!good_cap_t(cap_d)) { + errno = EINVAL; + return -1; + } + _cap_mu_lock(&cap_d->mutex); + nsowner = cap_d->rootid; + _cap_mu_unlock(&cap_d->mutex); + return nsowner; } /* @@ -337,13 +345,17 @@ int cap_set_file(const char *filename, cap_t cap_d) } /* - * Set rootid for the file capability sets. + * Set nsowner for the file capability set. */ - int cap_set_nsowner(cap_t cap_d, uid_t rootuid) { - cap_d->rootid = rootuid; - return 0; + if (!good_cap_t(cap_d)) { + errno = EINVAL; + return -1; + } + _cap_mu_lock(&cap_d->mutex); + cap_d->rootid = rootuid; + _cap_mu_unlock_return(&cap_d->mutex, 0); } #else /* ie. ndef VFS_CAP_U32 */ @@ -380,8 +392,8 @@ int cap_set_file(const char *filename, cap_t cap_d) int cap_set_nsowner(cap_t cap_d, uid_t rootuid) { - errno = EINVAL; - return -1; + errno = EINVAL; + return -1; } #endif /* def VFS_CAP_U32 */ diff --git a/libcap/cap_flag.c b/libcap/cap_flag.c index 51799b0..94afd1e 100644 --- a/libcap/cap_flag.c +++ b/libcap/cap_flag.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-8,2008,20 Andrew G. Morgan <morgan@kernel.org> + * Copyright (c) 1997-8,2008,20-21 Andrew G. Morgan <morgan@kernel.org> * * This file deals with flipping of capabilities on internal * capability sets as specified by POSIX.1e (formerlly, POSIX 6). @@ -14,7 +14,6 @@ * returned as the contents of *raised. The capability is from one of * the sets stored in cap_d as specified by set and value */ - int cap_get_flag(cap_t cap_d, cap_value_t value, cap_flag_t set, cap_flag_value_t *raised) { @@ -25,7 +24,9 @@ int cap_get_flag(cap_t cap_d, cap_value_t value, cap_flag_t set, if (raised && good_cap_t(cap_d) && value >= 0 && value < __CAP_MAXBITS && set >= 0 && set < NUMBER_OF_CAP_SETS) { + _cap_mu_lock(&cap_d->mutex); *raised = isset_cap(cap_d,value,set) ? CAP_SET:CAP_CLEAR; + _cap_mu_unlock(&cap_d->mutex); return 0; } else { _cap_debug("invalid arguments"); @@ -51,6 +52,7 @@ int cap_set_flag(cap_t cap_d, cap_flag_t set, && (set >= 0) && (set < NUMBER_OF_CAP_SETS) && (raise == CAP_SET || raise == CAP_CLEAR) ) { int i; + _cap_mu_lock(&cap_d->mutex); for (i=0; i<no_values; ++i) { if (array_values[i] < 0 || array_values[i] >= __CAP_MAXBITS) { _cap_debug("weird capability (%d) - skipped", array_values[i]); @@ -64,14 +66,12 @@ int cap_set_flag(cap_t cap_d, cap_flag_t set, } } } + _cap_mu_unlock(&cap_d->mutex); return 0; - } else { - _cap_debug("invalid arguments"); errno = EINVAL; return -1; - } } @@ -82,7 +82,9 @@ int cap_set_flag(cap_t cap_d, cap_flag_t set, int cap_clear(cap_t cap_d) { if (good_cap_t(cap_d)) { + _cap_mu_lock(&cap_d->mutex); memset(&(cap_d->u), 0, sizeof(cap_d->u)); + _cap_mu_unlock(&cap_d->mutex); return 0; } else { _cap_debug("invalid pointer"); @@ -104,9 +106,11 @@ int cap_clear_flag(cap_t cap_d, cap_flag_t flag) if (good_cap_t(cap_d)) { unsigned i; + _cap_mu_lock(&cap_d->mutex); for (i=0; i<_LIBCAP_CAPABILITY_U32S; i++) { cap_d->u[i].flat[flag] = 0; } + _cap_mu_unlock(&cap_d->mutex); return 0; } /* @@ -134,6 +138,15 @@ int cap_compare(cap_t a, cap_t b) return -1; } + /* + * To avoid a deadlock corner case, we operate on an unlocked + * private copy of b + */ + b = cap_dup(b); + if (b == NULL) { + return -1; + } + _cap_mu_lock(&a->mutex); for (i=0, result=0; i<_LIBCAP_CAPABILITY_U32S; i++) { result |= ((a->u[i].flat[CAP_EFFECTIVE] != b->u[i].flat[CAP_EFFECTIVE]) @@ -143,16 +156,21 @@ int cap_compare(cap_t a, cap_t b) | ((a->u[i].flat[CAP_PERMITTED] != b->u[i].flat[CAP_PERMITTED]) ? LIBCAP_PER : 0); } + _cap_mu_unlock(&a->mutex); + cap_free(b); return result; } /* - * cap_fill copies a bit-vector of capability state in a cap_t from - * one flag to another. + * cap_fill_flag copies a bit-vector of capability state in one cap_t from one + * flag to another flag of another cap_t. */ -int cap_fill(cap_t cap_d, cap_flag_t to, cap_flag_t from) +int cap_fill_flag(cap_t cap_d, cap_flag_t to, cap_t ref, cap_flag_t from) { - if (!good_cap_t(cap_d)) { + int i; + cap_t orig; + + if (!good_cap_t(cap_d) || !good_cap_t(ref)) { errno = EINVAL; return -1; } @@ -163,15 +181,31 @@ int cap_fill(cap_t cap_d, cap_flag_t to, cap_flag_t from) return -1; } - int i; + orig = cap_dup(ref); + if (orig == NULL) { + return -1; + } + + _cap_mu_lock(&cap_d->mutex); for (i = 0; i < _LIBCAP_CAPABILITY_U32S; i++) { - cap_d->u[i].flat[to] = cap_d->u[i].flat[from]; + cap_d->u[i].flat[to] = orig->u[i].flat[from]; } + _cap_mu_unlock(&cap_d->mutex); + cap_free(orig); return 0; } /* + * cap_fill copies a bit-vector of capability state in a cap_t from + * one flag to another. + */ +int cap_fill(cap_t cap_d, cap_flag_t to, cap_flag_t from) +{ + return cap_fill_flag(cap_d, to, cap_d, from); +} + +/* * cap_iab_get_vector reads the single bit value from an IAB vector set. */ cap_flag_value_t cap_iab_get_vector(cap_iab_t iab, cap_iab_vector_t vec, @@ -183,20 +217,25 @@ cap_flag_value_t cap_iab_get_vector(cap_iab_t iab, cap_iab_vector_t vec, unsigned o = (bit >> 5); __u32 mask = 1u << (bit & 31); + cap_flag_value_t ret; + _cap_mu_lock(&iab->mutex); switch (vec) { case CAP_IAB_INH: - return !!(iab->i[o] & mask); + ret = !!(iab->i[o] & mask); break; case CAP_IAB_AMB: - return !!(iab->a[o] & mask); + ret = !!(iab->a[o] & mask); break; case CAP_IAB_BOUND: - return !!(iab->nb[o] & mask); + ret = !!(iab->nb[o] & mask); break; default: - return 0; + ret = 0; } + _cap_mu_unlock(&iab->mutex); + + return ret; } /* @@ -216,6 +255,7 @@ int cap_iab_set_vector(cap_iab_t iab, cap_iab_vector_t vec, cap_value_t bit, __u32 on = 1u << (bit & 31); __u32 mask = ~on; + _cap_mu_lock(&iab->mutex); switch (vec) { case CAP_IAB_INH: iab->i[o] = (iab->i[o] & mask) | (raised ? on : 0); @@ -230,9 +270,10 @@ int cap_iab_set_vector(cap_iab_t iab, cap_iab_vector_t vec, cap_value_t bit, break; default: errno = EINVAL; - return -1; + _cap_mu_unlock_return(&iab->mutex, -1); } + _cap_mu_unlock(&iab->mutex); return 0; } @@ -246,6 +287,8 @@ int cap_iab_set_vector(cap_iab_t iab, cap_iab_vector_t vec, cap_value_t bit, int cap_iab_fill(cap_iab_t iab, cap_iab_vector_t vec, cap_t cap_d, cap_flag_t flag) { + int i, ret = 0; + if (!good_cap_t(cap_d) || !good_cap_iab_t(iab)) { errno = EINVAL; return -1; @@ -261,8 +304,17 @@ int cap_iab_fill(cap_iab_t iab, cap_iab_vector_t vec, return -1; } - int i; - for (i = 0; i < _LIBCAP_CAPABILITY_U32S; i++) { + /* + * Make a private copy so we don't need to hold two locks at once + * avoiding a recipe for a deadlock. + */ + cap_d = cap_dup(cap_d); + if (cap_d == NULL) { + return -1; + } + + _cap_mu_lock(&iab->mutex); + for (i = 0; !ret && i < _LIBCAP_CAPABILITY_U32S; i++) { switch (vec) { case CAP_IAB_INH: iab->i[i] = cap_d->u[i].flat[flag]; @@ -277,9 +329,41 @@ int cap_iab_fill(cap_iab_t iab, cap_iab_vector_t vec, break; default: errno = EINVAL; - return -1; + ret = -1; + break; } } + _cap_mu_unlock(&iab->mutex); - return 0; + cap_free(cap_d); + return ret; +} + +/* + * cap_iab_compare compares two iab tuples. + */ +int cap_iab_compare(cap_iab_t a, cap_iab_t b) +{ + int j, result; + if (!(good_cap_iab_t(a) && good_cap_iab_t(b))) { + _cap_debug("invalid arguments"); + errno = EINVAL; + return -1; + } + b = cap_iab_dup(b); + if (b == NULL) { + return -1; + } + + _cap_mu_lock(&a->mutex); + for (j=0, result=0; j<_LIBCAP_CAPABILITY_U32S; j++) { + result |= + (a->i[j] == b->i[j] ? 0 : (1 << CAP_IAB_INH)) | + (a->a[j] == b->a[j] ? 0 : (1 << CAP_IAB_AMB)) | + (a->nb[j] == b->nb[j] ? 0 : (1 << CAP_IAB_BOUND)); + } + _cap_mu_unlock(&a->mutex); + cap_free(b); + + return result; } diff --git a/libcap/cap_names.header b/libcap/cap_names.header new file mode 100644 index 0000000..8d64f64 --- /dev/null +++ b/libcap/cap_names.header @@ -0,0 +1,5 @@ +struct __cap_token_s { const char *name; int index; }; +%{ +const struct __cap_token_s *__cap_lookup_name(const char *, size_t); +%} +%% diff --git a/libcap/cap_proc.c b/libcap/cap_proc.c index e12c8e6..24bc274 100644 --- a/libcap/cap_proc.c +++ b/libcap/cap_proc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-8,2007,11,19,20 Andrew G Morgan <morgan@kernel.org> + * Copyright (c) 1997-8,2007,11,19-21 Andrew G Morgan <morgan@kernel.org> * * This file deals with getting and setting capabilities on processes. */ @@ -18,8 +18,6 @@ #include <sys/types.h> #include <sys/wait.h> -#include <linux/limits.h> - #include "libcap.h" /* @@ -137,7 +135,13 @@ static int _libcap_wprctl3(struct syscaller_s *sc, long int pr_cmd, long int arg1, long int arg2) { if (_libcap_overrode_syscalls) { - return sc->three(SYS_prctl, pr_cmd, arg1, arg2); + int result; + result = sc->three(SYS_prctl, pr_cmd, arg1, arg2); + if (result >= 0) { + return result; + } + errno = -result; + return -1; } return prctl(pr_cmd, arg1, arg2, 0, 0, 0); } @@ -147,7 +151,13 @@ static int _libcap_wprctl6(struct syscaller_s *sc, long int arg3, long int arg4, long int arg5) { if (_libcap_overrode_syscalls) { - return sc->six(SYS_prctl, pr_cmd, arg1, arg2, arg3, arg4, arg5); + int result; + result = sc->six(SYS_prctl, pr_cmd, arg1, arg2, arg3, arg4, arg5); + if (result >= 0) { + return result; + } + errno = -result; + return -1; } return prctl(pr_cmd, arg1, arg2, arg3, arg4, arg5); } @@ -183,7 +193,9 @@ static int _cap_set_proc(struct syscaller_s *sc, cap_t cap_d) { } _cap_debug("setting process capabilities"); + _cap_mu_lock(&cap_d->mutex); retval = _libcap_capset(sc, &cap_d->head, &cap_d->u[0].set); + _cap_mu_unlock(&cap_d->mutex); return retval; } @@ -208,9 +220,11 @@ int capgetp(pid_t pid, cap_t cap_d) _cap_debug("getting process capabilities for proc %d", pid); + _cap_mu_lock(&cap_d->mutex); cap_d->head.pid = pid; error = capget(&cap_d->head, &cap_d->u[0].set); cap_d->head.pid = 0; + _cap_mu_unlock(&cap_d->mutex); return error; } @@ -252,10 +266,12 @@ int capsetp(pid_t pid, cap_t cap_d) } _cap_debug("setting process capabilities for proc %d", pid); + _cap_mu_lock(&cap_d->mutex); cap_d->head.pid = pid; error = capset(&cap_d->head, &cap_d->u[0].set); cap_d->head.version = _LIBCAP_CAPABILITY_VERSION; cap_d->head.pid = 0; + _cap_mu_unlock(&cap_d->mutex); return error; } @@ -267,26 +283,12 @@ int capsetp(pid_t pid, cap_t cap_d) int cap_get_bound(cap_value_t cap) { - int result; - - result = prctl(PR_CAPBSET_READ, pr_arg(cap), pr_arg(0)); - if (result < 0) { - errno = -result; - return -1; - } - return result; + return prctl(PR_CAPBSET_READ, pr_arg(cap), pr_arg(0)); } static int _cap_drop_bound(struct syscaller_s *sc, cap_value_t cap) { - int result; - - result = _libcap_wprctl3(sc, PR_CAPBSET_DROP, pr_arg(cap), pr_arg(0)); - if (result < 0) { - errno = -result; - return -1; - } - return result; + return _libcap_wprctl3(sc, PR_CAPBSET_DROP, pr_arg(cap), pr_arg(0)); } /* drop a capability from the bounding set */ @@ -312,7 +314,7 @@ int cap_get_ambient(cap_value_t cap) static int _cap_set_ambient(struct syscaller_s *sc, cap_value_t cap, cap_flag_value_t set) { - int result, val; + int val; switch (set) { case CAP_SET: val = PR_CAP_AMBIENT_RAISE; @@ -324,13 +326,8 @@ static int _cap_set_ambient(struct syscaller_s *sc, errno = EINVAL; return -1; } - result = _libcap_wprctl6(sc, PR_CAP_AMBIENT, pr_arg(val), pr_arg(cap), - pr_arg(0), pr_arg(0), pr_arg(0)); - if (result < 0) { - errno = -result; - return -1; - } - return result; + return _libcap_wprctl6(sc, PR_CAP_AMBIENT, pr_arg(val), pr_arg(cap), + pr_arg(0), pr_arg(0), pr_arg(0)); } /* @@ -355,14 +352,9 @@ static int _cap_reset_ambient(struct syscaller_s *sc) } } - result = _libcap_wprctl6(sc, PR_CAP_AMBIENT, - pr_arg(PR_CAP_AMBIENT_CLEAR_ALL), - pr_arg(0), pr_arg(0), pr_arg(0), pr_arg(0)); - if (result < 0) { - errno = -result; - return -1; - } - return result; + return _libcap_wprctl6(sc, PR_CAP_AMBIENT, + pr_arg(PR_CAP_AMBIENT_CLEAR_ALL), + pr_arg(0), pr_arg(0), pr_arg(0), pr_arg(0)); } /* @@ -371,7 +363,7 @@ static int _cap_reset_ambient(struct syscaller_s *sc) * case where the set is empty already but the ambient cap API is * locked. */ -int cap_reset_ambient() +int cap_reset_ambient(void) { return _cap_reset_ambient(&multithread); } @@ -443,13 +435,17 @@ static cap_value_t raise_cap_setpcap[] = {CAP_SETPCAP}; static int _cap_set_mode(struct syscaller_s *sc, cap_mode_t flavor) { - cap_t working = cap_get_proc(); + int ret; unsigned secbits = CAP_SECURED_BITS_AMBIENT; + cap_t working = cap_get_proc(); - int ret = cap_set_flag(working, CAP_EFFECTIVE, - 1, raise_cap_setpcap, CAP_SET); - ret = ret | _cap_set_proc(sc, working); + if (working == NULL) { + _cap_debug("getting current process' capabilities failed"); + return -1; + } + ret = cap_set_flag(working, CAP_EFFECTIVE, 1, raise_cap_setpcap, CAP_SET) | + _cap_set_proc(sc, working); if (ret == 0) { cap_flag_t c; @@ -483,7 +479,9 @@ static int _cap_set_mode(struct syscaller_s *sc, cap_mode_t flavor) /* for good measure */ _cap_set_no_new_privs(sc); break; - + case CAP_MODE_HYBRID: + ret = _cap_set_secbits(sc, 0); + break; default: errno = EINVAL; ret = -1; @@ -519,13 +517,16 @@ cap_mode_t cap_get_mode(void) { unsigned secbits = cap_get_secbits(); + if (secbits == 0) { + return CAP_MODE_HYBRID; + } if ((secbits & CAP_SECURED_BITS_BASIC) != CAP_SECURED_BITS_BASIC) { return CAP_MODE_UNCERTAIN; } /* validate ambient is not set */ int olderrno = errno; - int ret = 0; + int ret = 0, cf; cap_value_t c; for (c = 0; !ret; c++) { ret = cap_get_ambient(c); @@ -534,6 +535,7 @@ cap_mode_t cap_get_mode(void) if (c && secbits != CAP_SECURED_BITS_AMBIENT) { return CAP_MODE_UNCERTAIN; } + ret = 0; break; } if (ret) { @@ -541,11 +543,22 @@ cap_mode_t cap_get_mode(void) } } + /* + * Explore how capabilities differ from empty. + */ cap_t working = cap_get_proc(); cap_t empty = cap_init(); - int cf = cap_compare(empty, working); + if (working == NULL || empty == NULL) { + _cap_debug("working=%p, empty=%p - need both non-NULL", working, empty); + ret = -1; + } else { + cf = cap_compare(empty, working); + } cap_free(empty); cap_free(working); + if (ret != 0) { + return CAP_MODE_UNCERTAIN; + } if (CAP_DIFFERS(cf, CAP_INHERITABLE)) { return CAP_MODE_PURE1E; @@ -571,6 +584,10 @@ static int _cap_setuid(struct syscaller_s *sc, uid_t uid) { const cap_value_t raise_cap_setuid[] = {CAP_SETUID}; cap_t working = cap_get_proc(); + if (working == NULL) { + return -1; + } + (void) cap_set_flag(working, CAP_EFFECTIVE, 1, raise_cap_setuid, CAP_SET); /* @@ -626,6 +643,10 @@ static int _cap_setgroups(struct syscaller_s *sc, { const cap_value_t raise_cap_setgid[] = {CAP_SETGID}; cap_t working = cap_get_proc(); + if (working == NULL) { + return -1; + } + (void) cap_set_flag(working, CAP_EFFECTIVE, 1, raise_cap_setgid, CAP_SET); /* @@ -684,9 +705,25 @@ int cap_setgroups(gid_t gid, size_t ngroups, const gid_t groups[]) */ cap_iab_t cap_iab_get_proc(void) { - cap_iab_t iab = cap_iab_init(); - cap_t current = cap_get_proc(); + cap_iab_t iab; + cap_t current; + + iab = cap_iab_init(); + if (iab == NULL) { + _cap_debug("no memory for IAB tuple"); + return NULL; + } + + current = cap_get_proc(); + if (current == NULL) { + _cap_debug("no memory for cap_t"); + cap_free(iab); + return NULL; + } + cap_iab_fill(iab, CAP_IAB_INH, current, CAP_INHERITABLE); + cap_free(current); + cap_value_t c; for (c = cap_max_bits(); c; ) { --c; @@ -704,13 +741,17 @@ cap_iab_t cap_iab_get_proc(void) /* * _cap_iab_set_proc sets the iab collection using the requested syscaller. + * The iab value is locked by the caller. */ static int _cap_iab_set_proc(struct syscaller_s *sc, cap_iab_t iab) { - int ret, i; - cap_t working, temp = cap_get_proc(); + int ret, i, raising = 0; cap_value_t c; - int raising = 0; + cap_t working, temp = cap_get_proc(); + + if (temp == NULL) { + return -1; + } for (i = 0; i < _LIBCAP_CAPABILITY_U32S; i++) { __u32 newI = iab->i[i]; @@ -722,6 +763,10 @@ static int _cap_iab_set_proc(struct syscaller_s *sc, cap_iab_t iab) } working = cap_dup(temp); + if (working == NULL) { + ret = -1; + goto defer; + } if (raising) { ret = cap_set_flag(working, CAP_EFFECTIVE, 1, raise_cap_setpcap, CAP_SET); @@ -770,7 +815,15 @@ defer: */ int cap_iab_set_proc(cap_iab_t iab) { - return _cap_iab_set_proc(&multithread, iab); + int retval; + if (!good_cap_iab_t(iab)) { + errno = EINVAL; + return -1; + } + _cap_mu_lock(&iab->mutex); + retval = _cap_iab_set_proc(&multithread, iab); + _cap_mu_unlock(&iab->mutex); + return retval; } /* @@ -785,51 +838,94 @@ int cap_iab_set_proc(cap_iab_t iab) * considered to have failed and the launch will be aborted - further, * errno will be communicated to the parent. */ -void cap_launcher_callback(cap_launch_t attr, int (callback_fn)(void *detail)) +int cap_launcher_callback(cap_launch_t attr, int (callback_fn)(void *detail)) { + if (!good_cap_launch_t(attr)) { + errno = EINVAL; + return -1; + } + _cap_mu_lock(&attr->mutex); attr->custom_setup_fn = callback_fn; + _cap_mu_unlock(&attr->mutex); + return 0; } /* * cap_launcher_setuid primes the launcher to attempt a change of uid. */ -void cap_launcher_setuid(cap_launch_t attr, uid_t uid) +int cap_launcher_setuid(cap_launch_t attr, uid_t uid) { + if (!good_cap_launch_t(attr)) { + errno = EINVAL; + return -1; + } + _cap_mu_lock(&attr->mutex); attr->uid = uid; attr->change_uids = 1; + _cap_mu_unlock(&attr->mutex); + return 0; } /* * cap_launcher_setgroups primes the launcher to attempt a change of * gid and groups. */ -void cap_launcher_setgroups(cap_launch_t attr, gid_t gid, - int ngroups, const gid_t *groups) +int cap_launcher_setgroups(cap_launch_t attr, gid_t gid, + int ngroups, const gid_t *groups) { + if (!good_cap_launch_t(attr)) { + errno = EINVAL; + return -1; + } + _cap_mu_lock(&attr->mutex); attr->gid = gid; attr->ngroups = ngroups; attr->groups = groups; attr->change_gids = 1; + _cap_mu_unlock(&attr->mutex); + return 0; } /* * cap_launcher_set_mode primes the launcher to attempt a change of * mode. */ -void cap_launcher_set_mode(cap_launch_t attr, cap_mode_t flavor) +int cap_launcher_set_mode(cap_launch_t attr, cap_mode_t flavor) { + if (!good_cap_launch_t(attr)) { + errno = EINVAL; + return -1; + } + _cap_mu_lock(&attr->mutex); attr->mode = flavor; attr->change_mode = 1; + _cap_mu_unlock(&attr->mutex); + return 0; } /* - * cap_launcher_set_iab primes the launcher to attempt to change the iab bits of - * the launched child. + * cap_launcher_set_iab primes the launcher to attempt to change the + * IAB values of the launched child. The launcher locks iab while it + * is owned by the launcher: this prevents the user from + * asynchronously changing its value while it is associated with the + * launcher. */ cap_iab_t cap_launcher_set_iab(cap_launch_t attr, cap_iab_t iab) { + if (!good_cap_launch_t(attr)) { + errno = EINVAL; + return NULL; + } + _cap_mu_lock(&attr->mutex); cap_iab_t old = attr->iab; attr->iab = iab; + if (old != NULL) { + _cap_mu_unlock(&old->mutex); + } + if (iab != NULL) { + _cap_mu_lock(&iab->mutex); + } + _cap_mu_unlock(&attr->mutex); return old; } @@ -837,15 +933,26 @@ cap_iab_t cap_launcher_set_iab(cap_launch_t attr, cap_iab_t iab) * cap_launcher_set_chroot sets the intended chroot for the launched * child. */ -void cap_launcher_set_chroot(cap_launch_t attr, const char *chroot) +int cap_launcher_set_chroot(cap_launch_t attr, const char *chroot) { + if (!good_cap_launch_t(attr)) { + errno = EINVAL; + return -1; + } + _cap_mu_lock(&attr->mutex); attr->chroot = _libcap_strdup(chroot); + _cap_mu_unlock(&attr->mutex); + return 0; } static int _cap_chroot(struct syscaller_s *sc, const char *root) { const cap_value_t raise_cap_sys_chroot[] = {CAP_SYS_CHROOT}; cap_t working = cap_get_proc(); + if (working == NULL) { + return -1; + } + (void) cap_set_flag(working, CAP_EFFECTIVE, 1, raise_cap_sys_chroot, CAP_SET); int ret = _cap_set_proc(sc, working); @@ -859,6 +966,9 @@ static int _cap_chroot(struct syscaller_s *sc, const char *root) } else { ret = chroot(root); } + if (ret == 0) { + ret = chdir("/"); + } } int olderrno = errno; (void) cap_clear_flag(working, CAP_EFFECTIVE); @@ -952,15 +1062,21 @@ pid_t cap_launch(cap_launch_t attr, void *detail) { int ps[2]; pid_t child; + if (!good_cap_launch_t(attr)) { + errno = EINVAL; + return -1; + } + _cap_mu_lock(&attr->mutex); + /* The launch must have a purpose */ if (attr->custom_setup_fn == NULL && (attr->arg0 == NULL || attr->argv == NULL)) { errno = EINVAL; - return -1; + _cap_mu_unlock_return(&attr->mutex, -1); } if (pipe2(ps, O_CLOEXEC) != 0) { - return -1; + _cap_mu_unlock_return(&attr->mutex, -1); } child = fork(); @@ -970,9 +1086,11 @@ pid_t cap_launch(cap_launch_t attr, void *detail) { close(ps[0]); prctl(PR_SET_NAME, "cap-launcher", 0, 0, 0); _cap_launch(ps[1], attr, detail); - /* no return from this function */ - _exit(1); + /* no return from above function */ } + + /* child has its own copy, and parent no longer needs it locked. */ + _cap_mu_unlock(&attr->mutex); close(ps[1]); if (child < 0) { goto defer; diff --git a/libcap/cap_test.c b/libcap/cap_test.c index a717217..68b6a13 100644 --- a/libcap/cap_test.c +++ b/libcap/cap_test.c @@ -1,12 +1,17 @@ +#define _GNU_SOURCE +#include <stdio.h> + #include "libcap.h" static cap_value_t top; -static int cf(cap_value_t x) { +static int cf(cap_value_t x) +{ return top - x - 1; } -static int test_cap_bits(void) { +static int test_cap_bits(void) +{ static cap_value_t vs[] = { 5, 6, 11, 12, 15, 16, 17, 38, 41, 63, 64, __CAP_MAXBITS+3, 0, -1 }; @@ -15,35 +20,49 @@ static int test_cap_bits(void) { for (i = 0; vs[i] >= 0; i++) { cap_value_t ans; - top = i; - _binary_search(ans, cf, 0, __CAP_MAXBITS, 0); + top = vs[i]; + _binary_search(ans, cf, 0, __CAP_MAXBITS, -1); if (ans != top) { - if (top > __CAP_MAXBITS && ans == __CAP_MAXBITS) { - } else { - printf("test_cap_bits miscompared [%d] top=%d - got=%d\n", - i, top, ans); - failed = -1; + if (top == 0 && ans == -1) { + continue; + } + if (top > __CAP_MAXBITS && ans == -1) { + continue; } + printf("test_cap_bits miscompared [%d] top=%d - got=%d\n", + i, top, ans); + failed = -1; } } return failed; } -static int test_cap_flags(void) { +static int test_cap_flags(void) +{ cap_t c, d; cap_flag_t f = CAP_INHERITABLE, t; cap_value_t v; + int retval = 0; c = cap_init(); if (c == NULL) { printf("test_flags failed to allocate a set\n"); return -1; } + if (cap_compare(c, NULL) != -1) { + printf("compare to NULL should give invalid\n"); + return -1; + } + if (cap_compare(NULL, c) != -1) { + printf("compare with NULL should give invalid\n"); + return -1; + } for (v = 0; v < __CAP_MAXBITS; v += 3) { if (cap_set_flag(c, CAP_INHERITABLE, 1, &v, CAP_SET)) { printf("unable to set inheritable bit %d\n", v); - return -1; + retval = -1; + goto drop_c; } } @@ -51,28 +70,225 @@ static int test_cap_flags(void) { for (t = CAP_EFFECTIVE; t <= CAP_INHERITABLE; t++) { if (cap_fill(c, t, f)) { printf("cap_fill failed %d -> %d\n", f, t); - return -1; + retval = -1; + goto drop_d; } if (cap_clear_flag(c, f)) { printf("cap_fill unable to clear flag %d\n", f); - return -1; + retval = -1; + goto drop_d; } f = t; } if (cap_compare(c, d)) { printf("permuted cap_fill()ing failed to perform net no-op\n"); + retval = -1; + } + if (cap_fill_flag(NULL, CAP_EFFECTIVE, c, CAP_INHERITABLE) == 0) { + printf("filling NULL flag should fail\n"); + retval = -1; + } + if (cap_fill_flag(d, CAP_PERMITTED, c, CAP_INHERITABLE) != 0) { + perror("filling PERMITEED flag should work"); + retval = -1; + } + if (cap_fill_flag(c, CAP_PERMITTED, d, CAP_PERMITTED) != 0) { + perror("filling PERMITTED flag from another cap_t should work"); + retval = -1; + } + if (cap_compare(c, d)) { + printf("permuted cap_fill()ing failed to perform net no-op\n"); + retval = -1; + } + +drop_d: + if (cap_free(d) != 0) { + perror("failed to free d"); + retval = -1; + } +drop_c: + if (cap_free(c) != 0) { + perror("failed to free c"); + retval = -1; + } + return retval; +} + +static int test_short_bits(void) +{ + int result = 0; + char *tmp; + int n = asprintf(&tmp, "%d", __CAP_MAXBITS); + if (n <= 0) { + return -1; + } + if (strlen(tmp) > __CAP_NAME_SIZE) { + printf("cap_to_text buffer size reservation needs fixing (%ld > %d)\n", + (long int)strlen(tmp), __CAP_NAME_SIZE); + result = -1; + } + free(tmp); + return result; +} + +static int noop(void *data) +{ + return -1; +} + +static int test_alloc(void) +{ + int retval = 0; + cap_t c; + cap_iab_t iab; + cap_launch_t launcher; + char *old_root; + + printf("test_alloc\n"); + fflush(stdout); + + c = cap_init(); + if (c == NULL) { + perror("failed to allocate a cap_t"); + fflush(stderr); return -1; } - cap_free(d); - cap_free(c); - return 0; + + iab = cap_iab_init(); + if (iab == NULL) { + perror("failed to allocate a cap_iab_t"); + fflush(stderr); + retval = -1; + goto drop_c; + } + + launcher = cap_func_launcher(noop); + if (launcher == NULL) { + perror("failde to allocate a launcher"); + fflush(stderr); + retval = -1; + goto drop_iab; + } + + cap_launcher_set_chroot(launcher, "/tmp"); + if (cap_launcher_set_iab(launcher, iab) != NULL) { + printf("unable to replace iab in launcher\n"); + fflush(stdout); + retval = -1; + goto drop_iab; + } + + iab = cap_launcher_set_iab(launcher, cap_iab_init()); + if (iab == NULL) { + printf("unable to recover iab in launcher\n"); + fflush(stdout); + retval = -1; + goto drop_launcher; + } + + old_root = cap_proc_root("blah"); + if (old_root != NULL) { + printf("bad initial proc_root [%s]\n", old_root); + fflush(stdout); + retval = -1; + } + if (cap_free(old_root)) { + perror("unable to free old proc root"); + fflush(stderr); + retval = -1; + } + if (retval) { + goto drop_launcher; + } + old_root = cap_proc_root("/proc"); + if (strcmp(old_root, "blah") != 0) { + printf("bad proc_root value [%s]\n", old_root); + fflush(stdout); + retval = -1; + } + if (cap_free(old_root)) { + perror("unable to free replacement proc root"); + fflush(stderr); + retval = -1; + } + if (retval) { + goto drop_launcher; + } + +drop_launcher: + printf("test_alloc: drop_launcher\n"); + fflush(stdout); + if (cap_free(launcher)) { + perror("failed to free launcher"); + fflush(stderr); + retval = -1; + } + +drop_iab: + printf("test_alloc: drop_iab\n"); + fflush(stdout); + if (!cap_free(2+(__u32 *) iab)) { + printf("unable to recognize bad cap_iab_t pointer\n"); + fflush(stdout); + retval = -1; + } + if (cap_free(iab)) { + perror("failed to free iab"); + fflush(stderr); + retval = -1; + } + +drop_c: + printf("test_alloc: drop_cap\n"); + fflush(stdout); + if (!cap_free(1+(__u32 *) c)) { + printf("unable to recognize bad cap_t pointer\n"); + fflush(stdout); + retval = -1; + } + if (cap_free(c)) { + perror("failed to free c"); + fflush(stderr); + retval = -1; + } + return retval; +} + +static int test_prctl(void) +{ + int ret, retval=0; + errno = 0; + ret = cap_get_bound((cap_value_t) -1); + if (ret != -1) { + printf("cap_get_bound(-1) did not return error: %d\n", ret); + retval = -1; + } else if (errno != EINVAL) { + perror("cap_get_bound(-1) errno != EINVAL"); + retval = -1; + } + return retval; } int main(int argc, char **argv) { int result = 0; + printf("test_cap_bits: being called\n"); + fflush(stdout); result = test_cap_bits() | result; + printf("test_cap_flags: being called\n"); + fflush(stdout); result = test_cap_flags() | result; + printf("test_short_bits: being called\n"); + fflush(stdout); + result = test_short_bits() | result; + printf("test_alloc: being called\n"); + fflush(stdout); + result = test_alloc() | result; + printf("test_prctl: being called\n"); + fflush(stdout); + result = test_prctl() | result; + printf("tested\n"); + fflush(stdout); if (result) { printf("cap_test FAILED\n"); diff --git a/libcap/cap_text.c b/libcap/cap_text.c index 87e0838..7566bd8 100644 --- a/libcap/cap_text.c +++ b/libcap/cap_text.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-8,2007-8,2019 Andrew G Morgan <morgan@kernel.org> + * Copyright (c) 1997-8,2007-8,2019,2021 Andrew G Morgan <morgan@kernel.org> * Copyright (c) 1997 Andrew Main <zefram@dcs.warwick.ac.uk> * * This file deals with exchanging internal and textual @@ -100,7 +100,7 @@ static int lookupname(char const **strp) return n; } else { int c; - unsigned len; + size_t len; for (len=0; (c = str.constp[len]); ++len) { if (!(isalpha(c) || (c == '_'))) { @@ -160,6 +160,7 @@ cap_t cap_from_text(const char *str) cap_blks = _LINUX_CAPABILITY_U32S_3; break; default: + cap_free(res); errno = EINVAL; return NULL; } @@ -219,7 +220,7 @@ cap_t cap_from_text(const char *str) /* cycle through list of actions */ do { - _cap_debug("next char = `%c'", *str); + _cap_debug("next char = '%c'", *str); if (*str && !isspace(*str)) { switch (*str++) { /* Effective, Inheritable, Permitted */ case 'e': @@ -309,20 +310,19 @@ int cap_from_name(const char *name, cap_value_t *value_p) */ char *cap_to_name(cap_value_t cap) { - if ((cap < 0) || (cap >= __CAP_BITS)) { -#if UINT_MAX != 4294967295U -# error Recompile with correctly sized numeric array -#endif - char *tmp, *result; - - (void) asprintf(&tmp, "%u", cap); - result = _libcap_strdup(tmp); - free(tmp); + char *tmp, *result; - return result; - } else { + if ((cap >= 0) && (cap < __CAP_BITS)) { return _libcap_strdup(_cap_names[cap]); } + if (asprintf(&tmp, "%u", cap) <= 0) { + _cap_debug("asprintf filed"); + return NULL; + } + + result = _libcap_strdup(tmp); + free(tmp); + return result; } /* @@ -348,6 +348,12 @@ static int getstateflags(cap_t caps, int capno) return f; } +/* + * This code assumes that the longest named capability is longer than + * the decimal text representation of __CAP_MAXBITS. This is very true + * at the time of writing and likely to remain so. However, we have + * a test in cap_text to validate it at build time. + */ #define CAP_TEXT_BUFFER_ZONE 100 char *cap_to_text(cap_t caps, ssize_t *length_p) @@ -398,6 +404,9 @@ char *cap_to_text(cap_t caps, ssize_t *length_p) for (n = 0; n < cmb; n++) { if (getstateflags(caps, n) == t) { char *this_cap_name = cap_to_name(n); + if (this_cap_name == NULL) { + return NULL; + } if ((strlen(this_cap_name) + (p - buf)) > CAP_TEXT_SIZE) { cap_free(this_cap_name); errno = ERANGE; @@ -450,6 +459,9 @@ char *cap_to_text(cap_t caps, ssize_t *length_p) for (n = cmb; n < __CAP_MAXBITS; n++) { if (getstateflags(caps, n) == t) { char *this_cap_name = cap_to_name(n); + if (this_cap_name == NULL) { + return NULL; + } if ((strlen(this_cap_name) + (p - buf)) > CAP_TEXT_SIZE) { cap_free(this_cap_name); errno = ERANGE; @@ -491,6 +503,8 @@ const char *cap_mode_name(cap_mode_t flavor) { return "PURE1E"; case CAP_MODE_UNCERTAIN: return "UNCERTAIN"; + case CAP_MODE_HYBRID: + return "HYBRID"; default: return "UNKNOWN"; } @@ -508,6 +522,7 @@ char *cap_iab_to_text(cap_iab_t iab) int first = 1; if (good_cap_iab_t(iab)) { + _cap_mu_lock(&iab->mutex); for (c = 0; c < cmb; c++) { int keep = 0; int o = c >> 5; @@ -541,6 +556,7 @@ char *cap_iab_to_text(cap_iab_t iab) first = 0; } } + _cap_mu_unlock(&iab->mutex); } *p = '\0'; return _libcap_strdup(buf); @@ -549,6 +565,9 @@ char *cap_iab_to_text(cap_iab_t iab) cap_iab_t cap_iab_from_text(const char *text) { cap_iab_t iab = cap_iab_init(); + if (iab == NULL) { + return iab; + } if (text != NULL) { unsigned flags; for (flags = 0; *text; text++) { @@ -604,3 +623,148 @@ cleanup: errno = EINVAL; return NULL; } + +static __u32 _parse_hex32(const char *c) +{ + int i; + __u32 v = 0; + for (i=0; i < 8; i++, c++) { + v <<= 4; + if (*c == 0 || *c < '0') { + return 0; + } else if (*c <= '9') { + v += *c - '0'; + } else if (*c > 'f') { + return 0; + } else if (*c >= 'a') { + v += *c + 10 - 'a'; + } else if (*c < 'A') { + return 0; + } else if (*c <= 'F') { + v += *c + 10 - 'A'; + } else { + return 0; + } + } + return v; +} + +/* + * _parse_vec_string converts the hex dumps in /proc/<pid>/current into + * an array of u32s - masked as per the forceall() mask. + */ +static __u32 _parse_vec_string(__u32 *vals, const char *c, int invert) +{ + int i; + int words = strlen(c)/8; + if (words > _LIBCAP_CAPABILITY_U32S) { + return 0; + } + forceall(vals, ~0, words); + for (i = 0; i < words; i++) { + __u32 val = _parse_hex32(c+8*(words-1-i)); + if (invert) { + val = ~val; + } + vals[i] &= val; + } + return ~0; +} + +/* + * libcap believes this is the root of the mounted "/proc" + * filesystem. (NULL == "/proc".) + */ +static char *_cap_proc_dir; + +/* + * If the constructor is called (see cap_alloc.c) then we'll need the + * corresponding destructor. + */ +__attribute__((destructor (300))) static void _cleanup_libcap(void) +{ + if (_cap_proc_dir == NULL) { + return; + } + cap_free(_cap_proc_dir); + _cap_proc_dir = NULL; +} + +/* + * cap_proc_root reads and (optionally: when root != NULL) changes + * libcap's notion of where the "/proc" filesystem is mounted. It + * defaults to the value "/proc". Note, this is a global value and not + * considered thread safe to write - so the client should take + * suitable care when changing it. Further, libcap will allocate + * memory for storing the replacement root, and it is this memory that + * is returned. So, when changing the value, the caller should + * cap_free(the-return-value) when done with it. + * + * A return value of NULL implies the default is in effect "/proc". + */ +char *cap_proc_root(const char *root) +{ + char *old = _cap_proc_dir; + if (root != NULL) { + _cap_proc_dir = _libcap_strdup(root); + } + return old; +} + +#define PROC_LINE_MAX (8 + 8*_LIBCAP_CAPABILITY_U32S + 100) +/* + * cap_iab_get_pid fills an IAB tuple from the content of + * /proc/<pid>/status. Linux doesn't support syscall access to the + * needed information, so we parse it out of that file. + */ +cap_iab_t cap_iab_get_pid(pid_t pid) +{ + cap_iab_t iab; + char *path; + FILE *file; + char line[PROC_LINE_MAX]; + const char *proc_root = _cap_proc_dir; + + if (proc_root == NULL) { + proc_root = "/proc"; + } + if (asprintf(&path, "%s/%d/status", proc_root, pid) <= 0) { + return NULL; + } + file = fopen(path, "r"); + free(path); + if (file == NULL) { + return NULL; + } + + iab = cap_iab_init(); + uint ok = 0; + if (iab != NULL) { + while (fgets(line, PROC_LINE_MAX-1, file) != NULL) { + if (strncmp("Cap", line, 3) != 0) { + continue; + } + if (strncmp("Inh:\t", line+3, 5) == 0) { + ok = (_parse_vec_string(iab->i, line+8, 0) & + LIBCAP_IAB_I_FLAG) | ok; + continue; + } + if (strncmp("Bnd:\t", line+3, 5) == 0) { + ok = (_parse_vec_string(iab->nb, line+8, 1) & + LIBCAP_IAB_NB_FLAG) | ok; + continue; + } + if (strncmp("Amb:\t", line+3, 5) == 0) { + ok = (_parse_vec_string(iab->a, line+8, 0) & + LIBCAP_IAB_A_FLAG) | ok; + continue; + } + } + } + if (ok != (LIBCAP_IAB_IA_FLAG | LIBCAP_IAB_NB_FLAG)) { + cap_free(iab); + iab = NULL; + } + fclose(file); + return iab; +} diff --git a/libcap/execable.c b/libcap/execable.c index be18914..9f7062e 100644 --- a/libcap/execable.c +++ b/libcap/execable.c @@ -1,10 +1,48 @@ #include <stdio.h> +#include <stdlib.h> +#include <sys/capability.h> + #include "execable.h" +static void usage(int status) +{ + printf("\nusage: libcap.so [--help|--usage|--summary]\n"); + exit(status); +} + +static void summary(void) +{ + cap_value_t bits = cap_max_bits(), c; + cap_mode_t mode = cap_get_mode(); + + printf("\nCurrent mode: %s\n", cap_mode_name(mode)); + printf("Number of cap values known to: this libcap=%d, running kernel=%d\n", + CAP_LAST_CAP+1, bits); + + if (bits > CAP_LAST_CAP+1) { + printf("=> Consider upgrading libcap to name:"); + for (c = CAP_LAST_CAP+1; c < bits; c++) { + printf(" %d", c); + } + } else if (bits < CAP_LAST_CAP+1) { + printf("=> Newer kernels also provide support for:"); + for (c = bits; c <= CAP_LAST_CAP; c++) { + char *name = cap_to_name(c); + printf(" %s", name); + cap_free(name); + } + } else { + return; + } + printf("\n"); +} + SO_MAIN(int argc, char **argv) { + int i; const char *cmd = "This library"; - if (argv != NULL) { + + if (argv != NULL && argv[0] != NULL) { cmd = argv[0]; } printf("%s is the shared library version: " LIBRARY_VERSION ".\n" @@ -12,4 +50,15 @@ SO_MAIN(int argc, char **argv) "More information on this library is available from:\n" "\n" " https://sites.google.com/site/fullycapable/\n", cmd); + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "--usage") || !strcmp(argv[i], "--help")) { + usage(0); + } + if (!strcmp(argv[i], "--summary")) { + summary(); + continue; + } + usage(1); + } } diff --git a/libcap/execable.h b/libcap/execable.h index 0bcc5d4..7a2d247 100644 --- a/libcap/execable.h +++ b/libcap/execable.h @@ -71,23 +71,42 @@ static void __execable_parse_args(int *argc_p, char ***argv_p) } /* + * Linux x86 ABI requires the stack be 16 byte aligned. Keep things + * simple and just force it. + */ +#if defined(__i386__) || defined(__x86_64__) +#define __SO_FORCE_ARG_ALIGNMENT __attribute__((force_align_arg_pointer)) +#else +#define __SO_FORCE_ARG_ALIGNMENT +#endif /* def some x86 */ + +/* + * Permit the compiler to override this one. + */ +#ifndef EXECABLE_INITIALIZE +#define EXECABLE_INITIALIZE do { } while(0) +#endif /* ndef EXECABLE_INITIALIZE */ + +/* * Note, to avoid any runtime confusion, SO_MAIN is a void static * function. */ - -#define SO_MAIN \ -static void __execable_main(int, char**); \ -extern void __so_start(void); \ -void __so_start(void) \ -{ \ - int argc; \ - char **argv; \ - __execable_parse_args(&argc, &argv); \ +#define SO_MAIN \ +static void __execable_main(int, char**); \ +__attribute__((visibility ("hidden"))) \ +void __so_start(void); \ +__SO_FORCE_ARG_ALIGNMENT \ +void __so_start(void) \ +{ \ + int argc; \ + char **argv; \ + __execable_parse_args(&argc, &argv); \ + EXECABLE_INITIALIZE; \ __execable_main(argc, argv); \ - if (argc != 0) { \ - free(argv[0]); \ - free(argv); \ - } \ - exit(0); \ -} \ + if (argc != 0) { \ + free(argv[0]); \ + free(argv); \ + } \ + exit(0); \ +} \ static void __execable_main diff --git a/libcap/include/sys/capability.h b/libcap/include/sys/capability.h index d172ddc..2db9972 100644 --- a/libcap/include/sys/capability.h +++ b/libcap/include/sys/capability.h @@ -2,7 +2,7 @@ * <sys/capability.h> * * Copyright (C) 1997 Aleph One - * Copyright (C) 1997,8, 2008,19,20 Andrew G. Morgan <morgan@kernel.org> + * Copyright (C) 1997,8, 2008,19-22 Andrew G. Morgan <morgan@kernel.org> * * defunct POSIX.1e Standard: 25.2 Capabilities <sys/capability.h> */ @@ -15,13 +15,18 @@ extern "C" { #endif /* + * Provide a programmatic way to #ifdef around features. + */ +#define LIBCAP_MAJOR 2 +#define LIBCAP_MINOR 69 + +/* * This file complements the kernel file by providing prototype * information for the user library. */ #include <sys/types.h> #include <stdint.h> -#include <linux/types.h> #ifndef __user #define __user @@ -54,6 +59,26 @@ typedef int cap_value_t; extern cap_value_t cap_max_bits(void); /* + * cap_proc_root reads and (optionally: when root != NULL) changes + * libcap's notion of where the "/proc" filesystem is mounted. When + * the return value is NULL, it should be interpreted as the + * value "/proc". + * + * Note, this is a global value and not considered thread safe to + * write - so the client should take suitable care when changing + * it. + * + * Further, libcap will allocate a memory copy for storing the + * replacement root, and it is this kind of memory that is returned. + * So, when changing the value, the caller should + * cap_free(the-return-value) else cause a memory leak. + * + * Note, the library uses a destructor to clean up the live allocated + * value of the working setting. + */ +extern char *cap_proc_root(const char *root); + +/* * Set identifiers */ typedef enum { @@ -102,11 +127,13 @@ typedef unsigned cap_mode_t; #define CAP_MODE_NOPRIV ((cap_mode_t) 1) #define CAP_MODE_PURE1E_INIT ((cap_mode_t) 2) #define CAP_MODE_PURE1E ((cap_mode_t) 3) +#define CAP_MODE_HYBRID ((cap_mode_t) 4) /* libcap/cap_alloc.c */ extern cap_t cap_dup(cap_t); extern int cap_free(void *); extern cap_t cap_init(void); +extern cap_iab_t cap_iab_dup(cap_iab_t); extern cap_iab_t cap_iab_init(void); /* libcap/cap_flag.c */ @@ -115,10 +142,14 @@ extern int cap_set_flag(cap_t, cap_flag_t, int, const cap_value_t *, cap_flag_value_t); extern int cap_clear(cap_t); extern int cap_clear_flag(cap_t, cap_flag_t); +extern int cap_fill_flag(cap_t cap_d, cap_flag_t to, + cap_t ref, cap_flag_t from); extern int cap_fill(cap_t, cap_flag_t, cap_flag_t); #define CAP_DIFFERS(result, flag) (((result) & (1 << (flag))) != 0) extern int cap_compare(cap_t, cap_t); +#define CAP_IAB_DIFFERS(result, vector) (((result) & (1 << (vector))) != 0) +extern int cap_iab_compare(cap_iab_t, cap_iab_t); extern cap_flag_value_t cap_iab_get_vector(cap_iab_t, cap_iab_vector_t, cap_value_t); @@ -185,6 +216,7 @@ extern int cap_setuid(uid_t uid); extern int cap_setgroups(gid_t gid, size_t ngroups, const gid_t groups[]); extern cap_iab_t cap_iab_get_proc(void); +extern cap_iab_t cap_iab_get_pid(pid_t); extern int cap_iab_set_proc(cap_iab_t iab); typedef struct cap_launch_s *cap_launch_t; @@ -192,14 +224,14 @@ typedef struct cap_launch_s *cap_launch_t; extern cap_launch_t cap_new_launcher(const char *arg0, const char * const *argv, const char * const *envp); extern cap_launch_t cap_func_launcher(int (callback_fn)(void *detail)); -extern void cap_launcher_callback(cap_launch_t attr, - int (callback_fn)(void *detail)); -extern void cap_launcher_setuid(cap_launch_t attr, uid_t uid); -extern void cap_launcher_setgroups(cap_launch_t attr, gid_t gid, - int ngroups, const gid_t *groups); -extern void cap_launcher_set_mode(cap_launch_t attr, cap_mode_t flavor); +extern int cap_launcher_callback(cap_launch_t attr, + int (callback_fn)(void *detail)); +extern int cap_launcher_setuid(cap_launch_t attr, uid_t uid); +extern int cap_launcher_setgroups(cap_launch_t attr, gid_t gid, + int ngroups, const gid_t *groups); +extern int cap_launcher_set_mode(cap_launch_t attr, cap_mode_t flavor); extern cap_iab_t cap_launcher_set_iab(cap_launch_t attr, cap_iab_t iab); -extern void cap_launcher_set_chroot(cap_launch_t attr, const char *chroot); +extern int cap_launcher_set_chroot(cap_launch_t attr, const char *chroot); extern pid_t cap_launch(cap_launch_t attr, void *detail); /* diff --git a/libcap/include/uapi/linux/capability.h b/libcap/include/uapi/linux/capability.h index 09b5563..56c9180 100644 --- a/libcap/include/uapi/linux/capability.h +++ b/libcap/include/uapi/linux/capability.h @@ -14,7 +14,9 @@ #ifndef _UAPI_LINUX_CAPABILITY_H #define _UAPI_LINUX_CAPABILITY_H -#include <linux/types.h> +#include <stdint.h> +#define __u32 uint32_t +#define __le32 __u32 /* User-level do most of the mapping between kernel and user capabilities based on the version tag given by the kernel. The @@ -422,5 +424,4 @@ struct vfs_ns_cap_data { #define CAP_TO_INDEX(x) ((x) >> 5) /* 1 << 5 == bits in __u32 */ #define CAP_TO_MASK(x) (1u << ((x) & 31)) /* mask for indexed __u32 */ - #endif /* _UAPI_LINUX_CAPABILITY_H */ diff --git a/libcap/libcap.h b/libcap/libcap.h index 67fa0d0..f4a72fe 100644 --- a/libcap/libcap.h +++ b/libcap/libcap.h @@ -9,6 +9,7 @@ #define LIBCAP_H #include <errno.h> +#include <sched.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -113,6 +114,7 @@ struct _cap_vfs_cap_data { #define CAP_T_MAGIC 0xCA90D0 struct _cap_struct { + __u8 mutex; struct __user_cap_header_struct head; union { struct __user_cap_data_struct set; @@ -121,6 +123,24 @@ struct _cap_struct { uid_t rootid; }; +/* + * Elementary exclusive locking primatives for situations where + * linking with pthreads needs it, but such linking is not common. + * + * _cap_mu_blocked(x) attempts to lock x but if already locked, returns true + * _cap_mu_lock(x) attempts to lock and waits until the lock is granted + * _cap_mu_unlock(x) unconditionally unlocks the lock + * _cap_mu_unlock_return(x, y) unlock lock x and return value y + */ +#define _cap_mu_blocked(x) \ + __atomic_test_and_set((void *)(x), __ATOMIC_SEQ_CST) +#define _cap_mu_lock(x) \ + while (_cap_mu_blocked(x)) sched_yield() +#define _cap_mu_unlock(x) \ + __atomic_clear((void *) (x), __ATOMIC_SEQ_CST) +#define _cap_mu_unlock_return(x, y) \ + do { _cap_mu_unlock(x); return (y); } while (0) + /* the maximum bits supportable */ #define __CAP_MAXBITS (__CAP_BLKS * 32) @@ -128,10 +148,15 @@ struct _cap_struct { #define CAP_S_MAGIC 0xCA95D0 /* iab set magic for cap_free */ -#define CAP_IAB_MAGIC 0xCA9AB +#define CAP_IAB_MAGIC 0xCA91AB /* launcher magic for cap_free */ -#define CAP_LAUNCH_MAGIC 0xCA91A +#define CAP_LAUNCH_MAGIC 0xCA91AC + +#define magic_of(x) ((x) ? *(-2 + (const __u32 *) x) : 0) +#define good_cap_t(x) (CAP_T_MAGIC == magic_of(x)) +#define good_cap_iab_t(x) (CAP_IAB_MAGIC == magic_of(x)) +#define good_cap_launch_t(x) (CAP_LAUNCH_MAGIC == magic_of(x)) /* * kernel API cap set abstraction @@ -142,16 +167,6 @@ struct _cap_struct { #define isset_cap(y, x, set) ((y)->u[(x) >> 5].flat[set] & (1u << ((x)&31))) /* - * Private definitions for internal use by the library. - */ - -#define __libcap_check_magic(c,magic) ((c) && *(-1+(__u32 *)(c)) == (magic)) -#define good_cap_t(c) __libcap_check_magic(c, CAP_T_MAGIC) -#define good_cap_string(c) __libcap_check_magic(c, CAP_S_MAGIC) -#define good_cap_iab_t(c) __libcap_check_magic(c, CAP_IAB_MAGIC) -#define good_cap_launch_t(c) __libcap_check_magic(c, CAP_LAUNCH_MAGIC) - -/* * These match CAP_DIFFERS() expectations */ #define LIBCAP_EFF (1 << CAP_EFFECTIVE) @@ -187,6 +202,9 @@ struct _cap_struct { #endif /* DEBUG */ extern char *_libcap_strdup(const char *text); +extern void _libcap_initialize(void); + +#define EXECABLE_INITIALIZE _libcap_initialize() /* * These are semi-public prototypes, they will only be defined in @@ -227,7 +245,7 @@ extern int capsetp(pid_t pid, cap_t cap_d); min = mid + 1; \ } \ } \ - val = min ? min : fallback; \ + val = min ? (min <= high ? min : fallback) : fallback; \ } while(0) /* @@ -238,6 +256,7 @@ extern int capsetp(pid_t pid, cap_t cap_d); * applied. */ struct cap_iab_s { + __u8 mutex; __u32 i[_LIBCAP_CAPABILITY_U32S]; __u32 a[_LIBCAP_CAPABILITY_U32S]; __u32 nb[_LIBCAP_CAPABILITY_U32S]; @@ -254,6 +273,7 @@ struct cap_iab_s { * multithreaded applications. */ struct cap_launch_s { + __u8 mutex; /* * Once forked but before active privilege is changed, this * function (if non-NULL) is called. diff --git a/libcap/psx_exec.c b/libcap/psx_exec.c new file mode 100644 index 0000000..5e7a88f --- /dev/null +++ b/libcap/psx_exec.c @@ -0,0 +1,15 @@ +#include <stdio.h> +#include "execable.h" + +SO_MAIN(int argc, char **argv) +{ + const char *cmd = "This library"; + if (argv != NULL && argv[0] != NULL) { + cmd = argv[0]; + } + printf("%s is the shared library version: " LIBRARY_VERSION ".\n" + "See the License file for distribution information.\n" + "More information on this library is available from:\n" + "\n" + " https://sites.google.com/site/fullycapable/\n", cmd); +} |