aboutsummaryrefslogtreecommitdiff
path: root/libcap/cap_proc.c
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2019-11-30 18:33:42 -0800
committerAndrew G. Morgan <morgan@kernel.org>2019-12-06 23:06:47 -0800
commitb2b267ef1c83f1f3d3105a4bb84f8bebbc130dec (patch)
treed0be8e0daca097a3911006b9eb85fcf4d2607182 /libcap/cap_proc.c
parente9f55d90e482f680504487be6b3afb80865691d6 (diff)
downloadlibcap-b2b267ef1c83f1f3d3105a4bb84f8bebbc130dec.tar.gz
Add support to libcap for overriding system call functions.
Note, this override only supports the system calls that libcap uses to change kernel state associated with the current process. This is primarily intended to permit the user to use libpsx to force all pthreads to mirror capability and other security relevant state. Use a weak function definition feature of libpsx share_psx_syscall() to transparently arrange for libcap to so force itself to use the psx_syscall() abstraction when linked against -lpsx. This has the effect of using linker magic to make libcap transparently observe POSIX semantics for security state setting operations. That is, when linked as follows: gcc .... -lcap -lpsx -lpthread -Wl,-wrap,pthread_create all pthreads maintain a common security state with respect to the libcap API. This also adds full capability setting support to the Go package libcap/cap via a libcap/psx package which uses cgo+libpsx syscalls that share capabilities over all pthreads including those of the Go runtime. Finally, if Go supports syscall.PosixSyscall() etc. then provide a non-psx mechanism for libcap/cap to "just work" in all Go code. Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
Diffstat (limited to 'libcap/cap_proc.c')
-rw-r--r--libcap/cap_proc.c95
1 files changed, 86 insertions, 9 deletions
diff --git a/libcap/cap_proc.c b/libcap/cap_proc.c
index f70b0e3..3f66d58 100644
--- a/libcap/cap_proc.c
+++ b/libcap/cap_proc.c
@@ -1,13 +1,87 @@
/*
- * Copyright (c) 1997-8,2007,2011 Andrew G Morgan <morgan@kernel.org>
+ * Copyright (c) 1997-8,2007,2011,2019 Andrew G Morgan <morgan@kernel.org>
*
* This file deals with getting and setting capabilities on processes.
*/
+#include <sys/syscall.h>
#include <sys/prctl.h>
+#include <unistd.h>
#include "libcap.h"
+/*
+ * libcap uses this abstraction for all system calls that change
+ * kernel managed capability state. This permits the user to redirect
+ * it for testing and also to better implement posix semantics when
+ * using pthreads.
+ */
+
+static long int _cap_syscall(long int syscall_nr,
+ long int arg1, long int arg2, long int arg3)
+{
+ return syscall(syscall_nr, arg1, arg2, arg3);
+}
+
+static long int _cap_syscall6(long int syscall_nr,
+ long int arg1, long int arg2, long int arg3,
+ long int arg4, long int arg5, long int arg6)
+{
+ return syscall(syscall_nr, arg1, arg2, arg3, arg4, arg5, arg6);
+}
+
+static long int (*_libcap_syscall)(long int, long int, long int, long int)
+ = _cap_syscall;
+static long int (*_libcap_syscall6)(long int, long int, long int, long int,
+ long int, long int, long int) = _cap_syscall6;
+
+void cap_set_syscall(long int (*new_syscall)(long int,
+ long int, long int, long int),
+ long int (*new_syscall6)(long int,
+ long int, long int, long int,
+ long int, long int, long int))
+{
+ _libcap_syscall = new_syscall;
+ _libcap_syscall6 = new_syscall6;
+}
+
+/*
+ * libcap<->libpsx subtle linking trick. If -lpsx is linked, then this
+ * function will get called when psx is initialized. In so doing,
+ * libcap will opt to use POSIX compliant syscalls for all state
+ * changing system calls - via psx_syscall().
+ */
+void share_psx_syscall(long int (*syscall_fn)(long int,
+ long int, long int, long int),
+ long int (*syscall6_fn)(long int,
+ long int, long int, long int,
+ long int, long int, long int));
+
+void share_psx_syscall(long int (*syscall_fn)(long int,
+ long int, long int, long int),
+ long int (*syscall6_fn)(long int,
+ long int, long int, long int,
+ long int, long int, long int))
+{
+ cap_set_syscall(syscall_fn, syscall6_fn);
+}
+
+static int _libcap_capset(cap_user_header_t header, const cap_user_data_t data)
+{
+ return _libcap_syscall(SYS_capset, (long int) header, (long int) data, 0);
+}
+
+static int _libcap_prctl(long int pr_cmd, long int arg1, long int arg2)
+{
+ return _libcap_syscall(SYS_prctl, pr_cmd, arg1, arg2);
+}
+
+static int _libcap_prctl6(long int pr_cmd, long int arg1, long int arg2,
+ long int arg3, long int arg4, long int arg5)
+{
+ return _libcap_syscall6(SYS_prctl, pr_cmd, arg1, arg2, arg3, arg4, arg5);
+}
+
cap_t cap_get_proc(void)
{
cap_t result;
@@ -37,7 +111,7 @@ int cap_set_proc(cap_t cap_d)
}
_cap_debug("setting process capabilities");
- retval = capset(&cap_d->head, &cap_d->u[0].set);
+ retval = _libcap_capset(&cap_d->head, &cap_d->u[0].set);
return retval;
}
@@ -85,7 +159,10 @@ cap_t cap_get_pid(pid_t pid)
return result;
}
-/* set the caps on a specific process/pg etc.. */
+/*
+ * set the caps on a specific process/pg etc.. The kernel has long
+ * since deprecated this asynchronus interface.
+ */
int capsetp(pid_t pid, cap_t cap_d)
{
@@ -114,7 +191,7 @@ int cap_get_bound(cap_value_t cap)
{
int result;
- result = prctl(PR_CAPBSET_READ, pr_arg(cap));
+ result = _libcap_prctl(PR_CAPBSET_READ, pr_arg(cap), pr_arg(0));
if (result < 0) {
errno = -result;
return -1;
@@ -128,7 +205,7 @@ int cap_drop_bound(cap_value_t cap)
{
int result;
- result = prctl(PR_CAPBSET_DROP, pr_arg(cap));
+ result = _libcap_prctl(PR_CAPBSET_DROP, pr_arg(cap), pr_arg(0));
if (result < 0) {
errno = -result;
return -1;
@@ -166,8 +243,8 @@ int cap_set_ambient(cap_value_t cap, cap_flag_value_t set)
errno = EINVAL;
return -1;
}
- result = prctl(PR_CAP_AMBIENT, pr_arg(val), pr_arg(cap),
- pr_arg(0), pr_arg(0));
+ result = _libcap_prctl6(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;
@@ -181,8 +258,8 @@ int cap_reset_ambient()
{
int result;
- result = prctl(PR_CAP_AMBIENT, pr_arg(PR_CAP_AMBIENT_CLEAR_ALL),
- pr_arg(0), pr_arg(0), pr_arg(0));
+ result = _libcap_prctl6(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;