From quis@quis.cx  Mon Feb 25 09:48:05 2008
Return-Path: <quis@quis.cx>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 1277716A405
	for <FreeBSD-gnats-submit@freebsd.org>; Mon, 25 Feb 2008 09:48:05 +0000 (UTC)
	(envelope-from quis@quis.cx)
Received: from smtp4.versatel.nl (smtp4.versatel.nl [62.58.50.91])
	by mx1.freebsd.org (Postfix) with ESMTP id 7362013C4F0
	for <FreeBSD-gnats-submit@freebsd.org>; Mon, 25 Feb 2008 09:48:04 +0000 (UTC)
	(envelope-from quis@quis.cx)
Received: (qmail 3252 invoked by uid 0); 25 Feb 2008 09:21:17 -0000
Received: from ip83-113-174-82.adsl2.versatel.nl (HELO istud.quis.cx) ([82.174.113.83])
          (envelope-sender <quis@quis.cx>)
          by smtp4.versatel.nl (qmail-ldap-1.03) with SMTP
          for < >; 25 Feb 2008 09:21:17 -0000
Received: by istud.quis.cx (Postfix, from userid 1001)
	id 4DF633981F; Mon, 25 Feb 2008 10:21:17 +0100 (CET)
Message-Id: <20080225092117.4DF633981F@istud.quis.cx>
Date: Mon, 25 Feb 2008 10:21:17 +0100 (CET)
From: Jille Timmermans <jille@quis.cx>
Reply-To: Jille Timmermans <jille@quis.cx>
To: FreeBSD-gnats-submit@freebsd.org
Cc: Ed Schouten <ed@freebsd.org>
Subject: Patch to run chroot as an unprivileged user
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         121073
>Category:       kern
>Synopsis:       [kernel] [patch] run chroot as an unprivileged user
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Feb 25 09:50:03 UTC 2008
>Closed-Date:    
>Last-Modified:  Fri May 30 23:10:00 UTC 2014
>Originator:     Jille Timmermans
>Release:        FreeBSD 8.0-CURRENT i386
>Organization:
>Environment:
System: FreeBSD stagemachine 8.0-CURRENT FreeBSD 8.0-CURRENT #0: Mon Feb 25 09:24:13 CET 2008 ed@stagemachine:/usr/obj/store/home/ed/p4/mpsafetty/sys/STAGEMACHINE  i386



	
>Description:
	We (Ed and I) thought it should be possible to chroot as non-root,
	This should (hopefully) increase the security, because no setuid-root and privilege dropping after the chroot(2) call is longer needed.
	This is tested on 8.0-CURRENT, should work on 7.0-RC2 and I will backport it later to 6.3-RELEASE.

>How-To-Repeat:
	sysctl kern.chroot_allow_unprivileged=1
	su nobody
	chroot / sh
	ping 127.0.0.1 # Should fail

>Fix:
--- lib/libc/sys/chroot.2
+++ lib/libc/sys/chroot.2
@@ -61,7 +61,13 @@
 .Fn chroot
 has no effect on the process's current directory.
 .Pp
-This call is restricted to the super-user.
+By default, this call is restricted to the super-user. If
+.Ql kern.chroot_allow_unprivileged
+is set to a non-zero value, all users are capable of performing the
+.Fn chroot
+call. When called by an unprivileged user, the process and its children
+won't honor the setuid and setgid bits when performing an
+.Xr execve 2 .
 .Pp
 Depending on the setting of the
 .Ql kern.chroot_allow_open_directories
@@ -140,3 +146,8 @@
 open directories, or a MAC check), it is possible that this system
 call may return an error, with the working directory of the process
 left changed.
+.Pp
+When a call to
+.Fn chroot
+fails when invoked by an unprivileged user, the process is not properly
+capable of executing setuid or setgid applications anymore.
--- sys/kern/kern_exec.c
+++ sys/kern/kern_exec.c
@@ -560,7 +560,7 @@
 
 	if (credential_changing &&
 	    (imgp->vp->v_mount->mnt_flag & MNT_NOSUID) == 0 &&
-	    (p->p_flag & P_TRACED) == 0) {
+	    (p->p_flag & (P_NOSUGID|P_TRACED)) == 0) {
 		/*
 		 * Turn off syscall tracing for set-id programs, except for
 		 * root.  Record any set-id flags first to make sure that
--- sys/kern/kern_fork.c
+++ sys/kern/kern_fork.c
@@ -584,7 +584,7 @@
 	 * Preserve some more flags in subprocess.  P_PROFIL has already
 	 * been preserved.
 	 */
-	p2->p_flag |= p1->p_flag & P_SUGID;
+	p2->p_flag |= p1->p_flag & P_INHERITED;
 	td2->td_pflags |= td->td_pflags & TDP_ALTSTACK;
 	SESS_LOCK(p1->p_session);
 	if (p1->p_session->s_ttyvp != NULL && p1->p_flag & P_CONTROLT)
--- sys/kern/vfs_syscalls.c
+++ sys/kern/vfs_syscalls.c
@@ -860,9 +860,12 @@
  */
 
 static int chroot_allow_open_directories = 1;
+static int chroot_allow_unprivileged = 0;
 
 SYSCTL_INT(_kern, OID_AUTO, chroot_allow_open_directories, CTLFLAG_RW,
      &chroot_allow_open_directories, 0, "");
+SYSCTL_INT(_kern, OID_AUTO, chroot_allow_unprivileged, CTLFLAG_RW,
+     &chroot_allow_unprivileged, 0, "");
 
 /*
  * Change notion of root (``/'') directory.
@@ -880,12 +883,27 @@
 	} */ *uap;
 {
 	int error;
+	struct proc *p;
 	struct nameidata nd;
 	int vfslocked;
 
 	error = priv_check(td, PRIV_VFS_CHROOT);
-	if (error)
-		return (error);
+	if (error) {
+		if (!chroot_allow_unprivileged)
+			return (error);
+
+		/*
+		 * Disallow this process and its children to use setuid
+		 * bits. Users could hardlink setuid applications into a
+		 * chroot which contains a fake C library to obtain
+		 * super-user privileges.
+		 */
+		p = td->td_proc;
+		PROC_LOCK(p);
+		p->p_flag |= P_NOSUGID;
+		PROC_UNLOCK(p);
+	}
+
 	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1,
 	    UIO_USERSPACE, uap->path, td);
 	error = namei(&nd);
--- sys/sys/proc.h
+++ sys/sys/proc.h
@@ -643,7 +643,9 @@
 #define	P_INMEM		0x10000000 /* Loaded into memory. */
 #define	P_SWAPPINGOUT	0x20000000 /* Process is being swapped out. */
 #define	P_SWAPPINGIN	0x40000000 /* Process is being swapped in. */
+#define	P_NOSUGID	0x80000000 /* Ignore set[ug]id on exec. */
 
+#define	P_INHERITED	(P_SUGID|P_NOSUGID)
 #define	P_STOPPED	(P_STOPPED_SIG|P_STOPPED_SINGLE|P_STOPPED_TRACE)
 #define	P_SHOULDSTOP(p)	((p)->p_flag & P_STOPPED)
 
>Release-Note:
>Audit-Trail:

From: Ed Schouten <ed@fxq.nl>
To: bug-followup@FreeBSD.org, jille@quis.cx
Cc:  
Subject: Re: kern/121073: Patch to run chroot as an unprivileged user
Date: Mon, 25 Feb 2008 16:21:46 +0100

 --P+33d92oIH25kiaB
 Content-Type: text/plain; charset=us-ascii
 Content-Disposition: inline
 Content-Transfer-Encoding: quoted-printable
 
 Hello,
 
 Just wanted to add some info about what this patch does:
 
 As far as I know, the only unsafe thing about chroot(2) is the fact that
 you can trick set[ug]id applications to do unwanted things when
 hardlinked into a new root directory, for example:
 
 - The user could store a different C library inside the chroot to
   perform an execl("/bin/sh", ...).
 - The user could just store his own passwd files, including database
   files, to make applications like su(8) work, without the proper
   privileges.
 
 This patch adds a new flag called P_NOSUGID. When enabled, this process
 will not honor the setuid and setgid flags anymore, just like MNT_NOSUID
 and P_TRACED.
 
 I have great confidence that this patch does not add any security holes,
 but just to be sure, this patch adds a sysctl to disable this behaviour
 by default.
 
 --=20
  Ed Schouten <ed@fxq.nl>
  WWW: http://g-rave.nl/
 
 --P+33d92oIH25kiaB
 Content-Type: application/pgp-signature
 Content-Disposition: inline
 
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.8 (FreeBSD)
 
 iEYEARECAAYFAkfC3QoACgkQ52SDGA2eCwUDeQCfSYgrLLapQGsNZOfAZXU7jNqR
 7c0AnREYpYIa4OojqVR7GoO8mT9MRrsi
 =Jir3
 -----END PGP SIGNATURE-----
 
 --P+33d92oIH25kiaB--

From: FreeBSD Security Officer <cperciva@freebsd.org>
To: bug-followup@FreeBSD.org, jille@quis.cx
Cc:  
Subject: Re: kern/121073: [kernel] [patch] run chroot as an unprivileged user
Date: Fri, 29 Feb 2008 22:36:24 -0800

 This is an interesting approach, and at first glance I can't see anything wrong
 with it.  That said, it needs very careful consideration; so to avoid confusion
 and make sure that this doesn't get committed before the right people have a
 chance to consider it sufficiently:
 
          +----------------------------------------------------------+
          |UNDER NO CONDITIONS SHOULD THIS PATCH BE COMMITTED WITHOUT|
          |EXPLICIT APPROVAL FROM THE FREEBSD SECURITY OFFICER.      |
          +----------------------------------------------------------+
 
 Colin Percival

From: Jille Timmermans <jille@quis.cx>
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/121073: [kernel] [patch] run chroot as an unprivileged user
Date: Fri, 07 Mar 2008 14:32:49 +0100

 Hello,
 
 This is the way I think about this; correct me when/where I'm wrong:
 
 - When an user chroot()'s himself, there is no possibility to change 
 user, because setuid is disabled, and this is the only way to switch user.
 - This means no local exploits will be possible.
 - Something that is possible, is programs use eg /etc/passwd, and think 
 it is reliable. It is the chroot()'ing user's job to make sure this is 
 reliable (no empty passwords, weak passwords, etc)
 Example 'remote exploit':
 userx chroot()'s an ftpd, because root doesn't want to run one, and he 
 wants to ftp.
 the ftpd checks the /etc/passwd in the chroot, and checks whether 
 passwords match that file.
 So if userx added users, or 'changed' passwords in the /etc/passwd the 
 ftpd will think that are his own passwords.
 
 This way a remote attacker can get unwanted access to the system.
 I think this is userx' responsibility, he should know what chroot()'ing 
 for (side)effects has.
 
 - What if some setuid program does a chroot() ? (euid!=ruid)
 1) userx runs a setuid-usery: usery might have coded an chroot() that 
 chroots, and userx doesn't know that
   - this is userx' fault, he shouldn't run binaries owned by others
 2) userx runs a setuid-usery binary: There is no way userx can 'insert' 
 a chroot() (I assume), or modify the memory etc; otherwise even su is an 
 enourmous security risk.
 
 Am I making mistakes ? Missing things ?
 
 -- Jille Timmermans

From: Nathan Whitehorn <nwhitehorn@freebsd.org>
To: bug-followup@freebsd.org, jille@quis.cx
Cc:  
Subject: Re: kern/121073: [kernel] [patch] run chroot as an unprivileged user
Date: Fri, 30 May 2014 14:54:07 -0700

 This is a multi-part message in MIME format.
 --------------010409080200010001080602
 Content-Type: text/plain; charset=ISO-8859-1; format=flowed
 Content-Transfer-Encoding: 7bit
 
 This patch would be extremely useful for package building, as I think it 
 would allow all operations to be done without root. It has gone stale 
 since the bug was filed, so I've updated it and attached a new version.
 -Nathan
 
 --------------010409080200010001080602
 Content-Type: text/plain; charset=us-ascii;
  name="chroot.patch"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="chroot.patch"
 
 Index: lib/libc/sys/chroot.2
 ===================================================================
 --- lib/libc/sys/chroot.2	(revision 266749)
 +++ lib/libc/sys/chroot.2	(working copy)
 @@ -61,7 +61,13 @@
  .Fn chroot
  has no effect on the process's current directory.
  .Pp
 -This call is restricted to the super-user.
 +By default, this call is restricted to the super-user. If
 +.Ql kern.chroot_allow_unprivileged
 +is set to a non-zero value, all users are capable of performing the
 +.Fn chroot
 +call. When called by an unprivileged user, the process and its children
 +won't honor the setuid and setgid bits when performing an
 +.Xr execve 2 .
  .Pp
  Depending on the setting of the
  .Ql kern.chroot_allow_open_directories
 @@ -141,6 +147,11 @@
  open directories, or a MAC check), it is possible that this system
  call may return an error, with the working directory of the process
  left changed.
 +.Pp
 +When a call to
 +.Fn chroot
 +fails when invoked by an unprivileged user, the process is not properly
 +capable of executing setuid or setgid applications anymore.
  .Sh SECURITY CONSIDERATIONS
  The system have many hardcoded paths to files where it may load after
  the process starts.
 Index: sys/kern/kern_exec.c
 ===================================================================
 --- sys/kern/kern_exec.c	(revision 266749)
 +++ sys/kern/kern_exec.c	(working copy)
 @@ -695,7 +695,7 @@
  	    ((oldcred->cr_flags & CRED_FLAG_CAPMODE) == 0) &&
  #endif
  	    (imgp->vp->v_mount->mnt_flag & MNT_NOSUID) == 0 &&
 -	    (p->p_flag & P_TRACED) == 0) {
 +	    (p->p_flag & P_TRACED) == 0 && (p->p_flag2 & P2_NOSUGID) == 0) {
  		/*
  		 * Turn off syscall tracing for set-id programs, except for
  		 * root.  Record any set-id flags first to make sure that
 Index: sys/kern/kern_fork.c
 ===================================================================
 --- sys/kern/kern_fork.c	(revision 266749)
 +++ sys/kern/kern_fork.c	(working copy)
 @@ -571,6 +571,7 @@
  	 * been preserved.
  	 */
  	p2->p_flag |= p1->p_flag & P_SUGID;
 +	p2->p_flag2 |= p1->p_flag2 & P2_NOSUGID;
  	td2->td_pflags |= td->td_pflags & TDP_ALTSTACK;
  	SESS_LOCK(p1->p_session);
  	if (p1->p_session->s_ttyvp != NULL && p1->p_flag & P_CONTROLT)
 Index: sys/kern/vfs_syscalls.c
 ===================================================================
 --- sys/kern/vfs_syscalls.c	(revision 266749)
 +++ sys/kern/vfs_syscalls.c	(working copy)
 @@ -855,6 +855,11 @@
       &chroot_allow_open_directories, 0,
       "Allow a process to chroot(2) if it has a directory open");
  
 +/* This one allows unprivileged users to run chroot */
 +static int chroot_allow_unprivileged = 0;
 +SYSCTL_INT(_kern, OID_AUTO, chroot_allow_unprivileged, CTLFLAG_RW,
 +     &chroot_allow_unprivileged, 0, "");
 +
  /*
   * Change notion of root (``/'') directory.
   */
 @@ -871,11 +876,26 @@
  	} */ *uap;
  {
  	struct nameidata nd;
 +	struct proc *p;
  	int error;
  
  	error = priv_check(td, PRIV_VFS_CHROOT);
 -	if (error != 0)
 -		return (error);
 +	if (error) {
 +		if (!chroot_allow_unprivileged)
 +			return (error);
 +
 +		/*
 +		 * Disallow this process and its children to use setuid
 +		 * bits. Users could hardlink setuid applications into a
 +		 * chroot which contains a fake C library to obtain
 +		 * super-user privileges.
 +		 */
 +		p = td->td_proc;
 +		PROC_LOCK(p);
 +		p->p_flag2 |= P2_NOSUGID;
 +		PROC_UNLOCK(p);
 +	}
 +
  	NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1,
  	    UIO_USERSPACE, uap->path, td);
  	error = namei(&nd);
 Index: sys/sys/proc.h
 ===================================================================
 --- sys/sys/proc.h	(revision 266749)
 +++ sys/sys/proc.h	(working copy)
 @@ -646,6 +646,7 @@
  
  /* These flags are kept in p_flag2. */
  #define	P2_INHERIT_PROTECTED 0x00000001 /* New children get P_PROTECTED. */
 +#define	P2_NOSUGID	 0x00000002 /* Ignore set[ug]id on exec. */
  
  /*
   * These were process status values (p_stat), now they are only used in
 
 --------------010409080200010001080602--

From: Jilles Tjoelker <jilles@stack.nl>
To: bug-followup@FreeBSD.org, jille@quis.cx
Cc:  
Subject: Re: kern/121073: [kernel] [patch] run chroot as an unprivileged user
Date: Sat, 31 May 2014 01:02:46 +0200

 In FreeBSD PR kern/121073, you wrote:
 > We (Ed and I) thought it should be possible to chroot as non-root,
 > This should (hopefully) increase the security, because no setuid-root
 > and privilege dropping after the chroot(2) call is longer needed.
 
 This change may be useful for package building without root.
 
 The disable setuid/setgid part looks similar to Linux's
 prctl(PR_SET_NO_NEW_PRIVS). In Linux, this is a separate operation that
 is a precondition for certain operations if unprivileged.
 
 I found two possible security risks with this:
 
 Firstly, unprivileged chroot might be used to break out of a chroot. For
 example, a directory file descriptor may be put onto a unix socket (to
 defeat kern.chroot_allow_open_directories), chroot to a subdirectory,
 get the file descriptor back, fchdir and abuse "..". If this is the
 first chroot, fd_jdir will stop it but other chroots (but not nested
 jails) can be escaped from. This can be fixed by only allowing a first
 or second chroot (fdp->fd_jdir == NULL || fdp->fd_jdir == fdp->fd_rdir).
 Due to locking this check must be in change_root().
 
 Secondly, a mac_vnode_execve_will_transition could lower as well as
 increase privilege; it may be better to reject the exec entirely if a
 MAC transition is denied.
 
 -- 
 Jilles Tjoelker
>Unformatted:
