public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* Re: Capabilities across execve
@ 2005-03-16  0:04 Albert Cahalan
  0 siblings, 0 replies; 11+ messages in thread
From: Albert Cahalan @ 2005-03-16  0:04 UTC (permalink / raw)
  To: linux-kernel mailing list; +Cc: rmk+lkml, alexn, chrisw, akpm, pavel

Russell King, the latest person to notice defects, writes:

> However, the way the kernel is setup today, this seems
> impossible to achieve, which tends to make the whole
> idea of capabilities completely and utterly useless.
>
> How is this stuff supposed to work?  Are my ideas of
> what's supposed to be achievable completely wrong,
> although they look completely reasonable to me.
>
> Don't get me wrong - the capability system seems great at
> permanently revoking capabilities via /proc/sys/kernel/cap-bound,
> and dropping them within an application provided it remains UID0.
> Apart from that, capabilities seem completely useless.
...
> it seems to be something of a lost cause.
...
> my goal of running the script with minimal capabilities
> was completely *impossible* to achieve.

Uh huh. First, some history.

Capability bits were implemented in DG-UX and IRIX.
The two systems did not agree on operation. The draft
POSIX standard, withdrawn for good reason, greatly
changed between draft 16 and draft 17. Settings that
work for one draft are horribly insecure on the other.
Linux capabilities were partly done by the IRIX crew,
working from draft 16. Everyone else had draft 17 or
even draft 13. (and DG-UX had a better system anyway)

Tytso put things well when he wrote: "A lot of innocent
bits have been deforested  while trying work out the
differences between what Linux is doing (which is basically
following Draft 17), and what Trusted Irix is doing (which 
apparently is following Draft 16)."

Then along comes a sendmail exploit. An emergency fix
was produced, breaking an already-defective capability
design.

Note that, unlike DG-UX, our IRIX-inspired design did
not reserve any capability bits for non-kernel use.
This causes an inconsistent security model, with things
like the X server relying on UID. Inconsistency is bad.

OK, so that's how we got into this mess.

Now, how do we get out?

We will always have to deal with old-style apps. Those
few apps that handle capabilities can handle the bad
system we have now, and can handle a system without the
capability syscalls. (for old kernels) These apps can
not handle a changed setup though; to change things we
must make the old syscalls return failure. ANYTHING ELSE
IS VERY UNSAFE.

There is exactly one capability system in popular use.
That would be the one that comes with Solaris. Moving
toward that, via a kernel config option, appears to be
a sane way to get ourselves unstuck from this big mess.
An added advantage that that the Solaris-style method
instantly becomes the standard, especially if Linux is
strongly compatible. This helps with admin training and
portable software.

See if you can find any holes:
http://docs.sun.com/app/docs/doc/816-5175/6mbba7f39?a=view



^ permalink raw reply	[flat|nested] 11+ messages in thread
* Capabilities across execve
@ 2005-03-12 11:42 Alexander Nyberg
  2005-03-13  3:21 ` Chris Wright
  2005-03-13 18:32 ` Pavel Machek
  0 siblings, 2 replies; 11+ messages in thread
From: Alexander Nyberg @ 2005-03-12 11:42 UTC (permalink / raw)
  To: linux-kernel; +Cc: akpm

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

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2005-03-19  0:13 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-03-16  0:04 Capabilities across execve Albert Cahalan
  -- strict thread matches above, loose matches on Subject: below --
2005-03-12 11:42 Alexander Nyberg
2005-03-13  3:21 ` 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox