aboutsummaryrefslogtreecommitdiff
path: root/libcap/cap_proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcap/cap_proc.c')
-rw-r--r--libcap/cap_proc.c244
1 files changed, 181 insertions, 63 deletions
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;