Skip to contentSkip to conversation

rlimit_nproc check

kernel-hardening Vasiliy Kulikov 2 participants 5 messages Jun 9, 2011 – Jun 23, 2011

lore
Share & tools
lore
#0 lore raw

Solar, all -

I found 8-years old patch that enables RLIMIT_NPROC check at setuid (and similar) calls:

http://lkml.org/lkml/2003/7/13/226

So, checking it on execve() is a bit redundant. But it means that setuid() may fail if it follows setrlimit() call and the target user has already reached the limit (asserted on the test C program). If the limit is defined in pam_limit, the attack becomes real.

Thanks,

Signature · 1 line
Vasiliy
#1 lore raw

Vasiliy -

Quoted 9 lines · Vasiliy Kulikov

I found 8-years old patch that enables RLIMIT_NPROC check at setuid (and similar) calls:

http://lkml.org/lkml/2003/7/13/226

So, checking it on execve() is a bit redundant. But it means that setuid() may fail if it follows setrlimit() call and the target user has already reached the limit (asserted on the test C program). If the limit is defined in pam_limit, the attack becomes real.

Right. Dealing with setuid() failing to drop privs yet returning, which many apps don't expect, is definitely something we (you) need to do under this project. In Linux 2.4.x-ow, I simply do:

kernel/sys.c +3 -1
@@ -514,8 +514,10 @@ static int set_user(uid_t new_ruid, int 
 	struct user_struct *new_user;
 
 	new_user = alloc_uid(new_ruid);
-	if (!new_user)
+	if (!new_user) {
+		force_sig(SIGSEGV, current);
 		return -EAGAIN;
+	}
 	switch_uid(new_user);
 
 	if(dumpclear)

As an option, you could propose to revert that 8-year old change and introduce the check on execve(). Unrealistic?

The requirements are:

1. setuid(2) must not fail and return, when it was invoked with "appropriate privileges". If it fails, it must not return.

This suggests that it should not fail very often, so maybe the RLIMIT_NPROC check does not belong there.

2. The setuid-execve sequence should not successfully start the new program when that would exceed RLIMIT_NPROC for the target UID.

Oh, by the way, here's what I found:

Subject: [PATCH] sched: Don't allow setuid to succeed if the user does not have rt bandwidth
http://lists.openwall.net/linux-kernel/2009/02/27/177

Thanks,

Alexander

#2 lore raw

Solar,

Quoted 2 lines · Solar Designer

As an option, you could propose to revert that 8-year old change and introduce the check on execve(). Unrealistic?

I've started a separate thread on LKML for it, we'll see ;)

Quoted 4 lines

Oh, by the way, here's what I found:

Subject: [PATCH] sched: Don't allow setuid to succeed if the user does not have rt bandwidth
http://lists.openwall.net/linux-kernel/2009/02/27/177

Yeah, but it was removed in 7c9414385ebfdd87cc542d4e7e3bb0dbb2d3ce25 as a "2.6.34 scheduled cleanup".

Thanks,

Signature · 1 line
Vasiliy
#3 lore raw
In reply to Solar Designer
Quoted 19 lines · Solar Designer

Right. Dealing with setuid() failing to drop privs yet returning, which many apps don't expect, is definitely something we (you) need to do under this project. In Linux 2.4.x-ow, I simply do:

kernel/sys.c +3 -1
@@ -514,8 +514,10 @@ static int set_user(uid_t new_ruid, int 
 	struct user_struct *new_user;
 
 	new_user = alloc_uid(new_ruid);
-	if (!new_user)
+	if (!new_user) {
+		force_sig(SIGSEGV, current);
 		return -EAGAIN;
+	}
 	switch_uid(new_user);
 
 	if(dumpclear)
Quoted 2 lines

As an option, you could propose to revert that 8-year old change and introduce the check on execve(). Unrealistic?

I have slightly another idea. Introduce mechanism (e.g. sysctl variable) to make all capabilities dropping function cause SIGSEGV if actual dropping process fails for any of several reasons.

The initial list should look like this:

    set*{u,g}id
    {set,init}groups
    unshare
    prctl(PR_CAPBSET_DROP, ...)
    prctl(PR_SET_SECCOMP, ...)
    capset

But any such list looks a bit not complete because there are many different functions that might drop some capabilities in some situations, like close(), *chdir(), rlimit(), nice(), fcntl(), ioctl(), chroot(), which are, well, might fail in very common situations without any actual secuity risk.

So, IMO this solution might not be enough consistent for upstream inclusion.

Thanks,

Signature · 1 line
Vasiliy
#4 lore raw
Quoted 6 lines · Vasiliy Kulikov

I have slightly another idea. Introduce mechanism (e.g. sysctl variable) to make all capabilities dropping function cause SIGSEGV if actual dropping process fails for any of several reasons.

The initial list should look like this:

Quoted 7 lines
    set*{u,g}id
    {set,init}groups
    unshare
    prctl(PR_CAPBSET_DROP, ...)
    prctl(PR_SET_SECCOMP, ...)
    capset

[...]

The scheme actually is harmfull in some situations. E.g. nfs daemon with one process architecture switches to another user via setfsuid() or similar, handles the request, switches back to the root. If setfsuid() fails (rlimit or some other reason) nfsd is DoS'ed by the signal. Any other daemon with similar architecture will be DoS'ed too.

So, the list of SIGKILL'able syscalls should be selected _very_ carefully (I don't know whether the safe list is nonempty).

Thanks,

Signature · 2 lines
Vasiliy Kulikov
http://www.openwall.com - bringing security into open computing environments