aboutsummaryrefslogtreecommitdiff
path: root/libcap
diff options
context:
space:
mode:
Diffstat (limited to 'libcap')
-rw-r--r--libcap/Makefile53
-rw-r--r--libcap/_makenames.c8
-rw-r--r--libcap/cap_alloc.c238
-rw-r--r--libcap/cap_extint.c72
-rw-r--r--libcap/cap_file.c40
-rw-r--r--libcap/cap_flag.c124
-rw-r--r--libcap/cap_names.header5
-rw-r--r--libcap/cap_proc.c244
-rw-r--r--libcap/cap_test.c248
-rw-r--r--libcap/cap_text.c192
-rw-r--r--libcap/execable.c51
-rw-r--r--libcap/execable.h49
-rw-r--r--libcap/include/sys/capability.h50
-rw-r--r--libcap/include/uapi/linux/capability.h5
-rw-r--r--libcap/libcap.h46
-rw-r--r--libcap/psx_exec.c15
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);
+}