public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Alexander Nyberg <alexn@dsv.su.se>
To: linux-kernel@vger.kernel.org
Cc: akpm@osdl.org
Subject: Capabilities across execve
Date: Sat, 12 Mar 2005 12:42:28 +0100	[thread overview]
Message-ID: <1110627748.2376.6.camel@boxen> (raw)

[-- Attachment #1: Type: text/plain, Size: 4121 bytes --]

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 <alexn@dsv.su.se>

===== 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;
 	}


[-- Attachment #2: cap_test.c --]
[-- Type: text/x-csrc, Size: 1729 bytes --]

#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	other threads:[~2005-03-12 11:42 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-03-12 11:42 Alexander Nyberg [this message]
2005-03-13  3:21 ` Capabilities across execve Chris Wright
2005-03-15 14:46   ` Alexander Nyberg
2005-03-15 21:57   ` Russell King
2005-03-15 22:42     ` Chris Wright
2005-03-15 23:41       ` Alexander Nyberg
2005-03-15 23:58         ` Chris Wright
2005-03-16  0:34           ` Alexander Nyberg
2005-03-19  0:02           ` Olaf Dietsche
2005-03-13 18:32 ` Pavel Machek
  -- strict thread matches above, loose matches on Subject: below --
2005-03-16  0:04 Albert Cahalan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1110627748.2376.6.camel@boxen \
    --to=alexn@dsv.su.se \
    --cc=akpm@osdl.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox