This makes it possible for a root-task to pass capabilities to
nonroot-task across execve. The root-task needs to change it's
cap_inheritable mask and set prctl(PR_SET_KEEPCAPS, 1) to pass on
capabilities. 
At execve time the capabilities will be passed on to the new
nonroot-task and any non-inheritable effective and permitted
capabilities will be masked out.
The effective capability of the new nonroot-task will be set to the
maximum permitted.

>From here on the inheritable mask will be passed on unchanged to the new
tasks children unless told otherwise (effectively the complete
capability state is passed on).

With a small insert of prctl(PR_SET_KEEPCAPS, 1) into pam_cap.c at the
correct place this makes pam_cap work as expected. I'm also attaching a
test-case that tests capabilities across setuid => execve (makes the new
task inherit CAP_CHOWN).


Signed-off-by: Alexander Nyberg <[EMAIL PROTECTED]>

===== security/commoncap.c 1.15 vs edited =====
--- 1.15/security/commoncap.c   2005-01-11 02:29:23 +01:00
+++ edited/security/commoncap.c 2005-03-12 12:04:34 +01:00
@@ -111,13 +111,19 @@ void cap_capset_set (struct task_struct 
 
 int cap_bprm_set_security (struct linux_binprm *bprm)
 {
-       /* Copied from fs/exec.c:prepare_binprm. */
-
-       /* We don't have VFS support for capabilities yet */
-       cap_clear (bprm->cap_inheritable);
-       cap_clear (bprm->cap_permitted);
-       cap_clear (bprm->cap_effective);
+       struct task_struct *p = current;
 
+       /*
+        * Mask out the non-inheritable capabilities.
+        * Note: init has a zero mask of cap_inheritable, so a root-task will 
not
+        * pass on any capabilities unless explicitly told to do so. If a 
non-zero
+        * inheritable mask is passed to a positive uid task it will then pass 
on 
+        * its inheritable mask to all children unless told otherwise.
+        */
+       bprm->cap_permitted = cap_intersect(p->cap_permitted, 
p->cap_inheritable);
+       bprm->cap_effective = cap_intersect(p->cap_effective, 
p->cap_inheritable);
+       bprm->cap_inheritable = p->cap_inheritable;
+       
        /*  To support inheritance of root-permissions and suid-root
         *  executables under compatibility mode, we raise all three
         *  capability sets for the file.
@@ -127,7 +133,7 @@ int cap_bprm_set_security (struct linux_
         */
 
        if (!issecure (SECURE_NOROOT)) {
-               if (bprm->e_uid == 0 || current->uid == 0) {
+               if (bprm->e_uid == 0 || p->uid == 0) {
                        cap_set_full (bprm->cap_inheritable);
                        cap_set_full (bprm->cap_permitted);
                }
@@ -139,13 +145,9 @@ int cap_bprm_set_security (struct linux_
 
 void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
 {
-       /* Derived from fs/exec.c:compute_creds. */
-       kernel_cap_t new_permitted, working;
+       kernel_cap_t new_permitted;
 
        new_permitted = cap_intersect (bprm->cap_permitted, cap_bset);
-       working = cap_intersect (bprm->cap_inheritable,
-                                current->cap_inheritable);
-       new_permitted = cap_combine (new_permitted, working);
 
        if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
            !cap_issubset (new_permitted, current->cap_permitted)) {
@@ -166,14 +168,9 @@ void cap_bprm_apply_creds (struct linux_
        current->suid = current->euid = current->fsuid = bprm->e_uid;
        current->sgid = current->egid = current->fsgid = bprm->e_gid;
 
-       /* For init, we want to retain the capabilities set
-        * in the init_task struct. Thus we skip the usual
-        * capability rules */
-       if (current->pid != 1) {
-               current->cap_permitted = new_permitted;
-               current->cap_effective =
-                   cap_intersect (new_permitted, bprm->cap_effective);
-       }
+       current->cap_permitted = new_permitted;
+       current->cap_effective =
+                       cap_intersect (new_permitted, bprm->cap_effective);
 
        /* AUD: Audit candidate if current->cap_effective is set */
 
@@ -249,9 +246,9 @@ static inline void cap_emulate_setxuid (
                cap_clear (current->cap_permitted);
                cap_clear (current->cap_effective);
        }
-       if (old_euid == 0 && current->euid != 0) {
+       if (old_euid == 0 && current->euid != 0 && !current->keep_capabilities)
                cap_clear (current->cap_effective);
-       }
+
        if (old_euid != 0 && current->euid == 0) {
                current->cap_effective = current->cap_permitted;
        }

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/capability.h>
#include <sys/prctl.h>

struct caph {
	int version;
	int pid;
};

struct capd {
	__u32 effective;
	__u32 permitted;
	__u32 inheritable;
};

gid_t gid = 1000;
uid_t uid = 1000;


void printcap(char *str)
{
	int ret;
	struct caph caph;
	struct capd capd;
	
	memset(&capd, 0, sizeof(capd));	
	
	caph.version = 0x19980330;
	caph.pid = getpid();

	ret = capget(&caph, &capd);
	if (ret < 0)
		perror("capget");
	printf("%s: ", str);
	printf("eff:%u perm:%u inh:%u\n", capd.effective, capd.permitted, capd.inheritable);
}

void lower_uid()
{
	int ret;
	gid_t gidlist[1];
	
	gidlist[0] = gid;
	ret = setgroups(1, gidlist);
	if (ret < 0)
		perror("setgroups");
	if (setgid(gid))
		perror("setgid");
	if (setuid(uid))
		perror("setuid");
}

const char *bash[] = { "bash", NULL };

int main(int argc, char **argv)
{
	int ret;
	struct caph caph;
	struct capd capd;

	if (argc == 3) {
		uid = strtoul(argv[1], NULL, 0);
		gid = strtoul(argv[2], NULL, 0);
	} else {
		printf("Usage: \"./program uid gid\" where uid & gid\n");
		printf("are the ids you want to change to (then run bash)\n");
		exit(1);
	}

	printcap("caps when started");

	memset(&capd, 0, sizeof(capd));	

	caph.version = 0x19980330;
	caph.pid = getpid();

	ret = capget(&caph, &capd);
	if (ret < 0)
		perror("capget");
	
	capd.inheritable |= (1 << CAP_CHOWN);
	
	ret = capset(&caph, &capd);
	if (ret < 0)
		perror("capset");

	printf("new inheritable mask: %u\n", capd.inheritable);
	
	ret = prctl(PR_SET_KEEPCAPS, 1);
	if (ret)
		perror("prctl");
	
	lower_uid();

	printcap("after setuid");
        
	ret = execv("/bin/bash", bash);
	if (ret < 0)
		perror("execve");
		
	return 0;
}

Reply via email to