git: db8d680ebe9b - main - procctl(2): add PROC_NO_NEW_PRIVS_CTL, PROC_NO_NEW_PRIVS_STATUS

Edward Tomasz Napierala trasz at FreeBSD.org
Thu Jul 1 08:42:24 UTC 2021


The branch main has been updated by trasz:

URL: https://cgit.FreeBSD.org/src/commit/?id=db8d680ebe9b12c7d9e0eb8bf9940fcef709f5ec

commit db8d680ebe9b12c7d9e0eb8bf9940fcef709f5ec
Author:     Edward Tomasz Napierala <trasz at FreeBSD.org>
AuthorDate: 2021-07-01 08:11:11 +0000
Commit:     Edward Tomasz Napierala <trasz at FreeBSD.org>
CommitDate: 2021-07-01 08:42:07 +0000

    procctl(2): add PROC_NO_NEW_PRIVS_CTL, PROC_NO_NEW_PRIVS_STATUS
    
    This introduces a new, per-process flag, "NO_NEW_PRIVS", which
    is inherited, preserved on exec, and cannot be cleared.  The flag,
    when set, makes subsequent execs ignore any SUID and SGID bits,
    instead executing those binaries as if they not set.
    
    The main purpose of the flag is implementation of Linux
    PROC_SET_NO_NEW_PRIVS prctl(2), and possibly also unpriviledged
    chroot.
    
    Reviewed By:    kib
    Sponsored By:   EPSRC
    Differential Revision:  https://reviews.freebsd.org/D30939
---
 lib/libc/sys/procctl.2                | 27 ++++++++++++++++++++++++++-
 sys/compat/freebsd32/freebsd32_misc.c |  3 +++
 sys/kern/kern_exec.c                  |  5 +++--
 sys/kern/kern_fork.c                  |  2 +-
 sys/kern/kern_procctl.c               | 32 ++++++++++++++++++++++++++++++++
 sys/sys/proc.h                        |  1 +
 sys/sys/procctl.h                     |  5 +++++
 7 files changed, 71 insertions(+), 4 deletions(-)

diff --git a/lib/libc/sys/procctl.2 b/lib/libc/sys/procctl.2
index f85825d8cc6f..432ed5919a81 100644
--- a/lib/libc/sys/procctl.2
+++ b/lib/libc/sys/procctl.2
@@ -29,7 +29,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd June 13, 2020
+.Dd July 1, 2021
 .Dt PROCCTL 2
 .Os
 .Sh NAME
@@ -564,6 +564,31 @@ Stack gaps are enabled in the process after
 Stack gaps are disabled in the process after
 .Xr execve 2 .
 .El
+.It Dv PROC_NO_NEW_PRIVS_CTL
+Allows one to ignore the SUID and SGID bits on the program
+images activated by
+.Xr execve 2
+in the specified process and its future descendants.
+The
+.Fa data
+parameter must point to the integer variable holding the following
+value:
+.Bl -tag -width PROC_NO_NEW_PRIVS_ENABLE
+.It Dv PROC_NO_NEW_PRIVS_ENABLE
+Request SUID and SGID bits to be ignored.
+.El
+.Pp
+It is not possible to disable it once it has been enabled.
+.It Dv PROC_NO_NEW_PRIVS_STATUS
+Returns the current status of SUID/SGID enablement for the target process.
+The
+.Fa data
+parameter must point to the integer variable, where one of the
+following values is written:
+.Bl -tag -width PROC_NO_NEW_PRIVS_DISABLE
+.It Dv PROC_NO_NEW_PRIVS_ENABLE
+.It Dv PROC_NO_NEW_PRIVS_DISABLE
+.El
 .El
 .Sh x86 MACHINE-SPECIFIC REQUESTS
 .Bl -tag -width PROC_KPTI_STATUS
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index f221397e91dc..950631352f12 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -3643,6 +3643,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
 	case PROC_STACKGAP_CTL:
 	case PROC_TRACE_CTL:
 	case PROC_TRAPCAP_CTL:
+	case PROC_NO_NEW_PRIVS_CTL:
 		error = copyin(PTRIN(uap->data), &flags, sizeof(flags));
 		if (error != 0)
 			return (error);
@@ -3676,6 +3677,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
 	case PROC_STACKGAP_STATUS:
 	case PROC_TRACE_STATUS:
 	case PROC_TRAPCAP_STATUS:
+	case PROC_NO_NEW_PRIVS_STATUS:
 		data = &flags;
 		break;
 	case PROC_PDEATHSIG_CTL:
@@ -3707,6 +3709,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
 	case PROC_STACKGAP_STATUS:
 	case PROC_TRACE_STATUS:
 	case PROC_TRAPCAP_STATUS:
+	case PROC_NO_NEW_PRIVS_STATUS:
 		if (error == 0)
 			error = copyout(&flags, uap->data, sizeof(flags));
 		break;
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c
index aff030cd432e..c3c23f44189e 100644
--- a/sys/kern/kern_exec.c
+++ b/sys/kern/kern_exec.c
@@ -781,8 +781,9 @@ interpret:
 		signotify(td);
 	}
 
-	if (imgp->sysent->sv_setid_allowed != NULL &&
-	    !(*imgp->sysent->sv_setid_allowed)(td, imgp))
+	if ((imgp->sysent->sv_setid_allowed != NULL &&
+	    !(*imgp->sysent->sv_setid_allowed)(td, imgp)) ||
+	    (p->p_flag2 & P2_NO_NEW_PRIVS) != 0)
 		execve_nosetid(imgp);
 
 	/*
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index 0d0659b432fe..7a80f7de85d8 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -492,7 +492,7 @@ do_fork(struct thread *td, struct fork_req *fr, struct proc *p2, struct thread *
 	p2->p_flag2 = p1->p_flag2 & (P2_ASLR_DISABLE | P2_ASLR_ENABLE |
 	    P2_ASLR_IGNSTART | P2_NOTRACE | P2_NOTRACE_EXEC |
 	    P2_PROTMAX_ENABLE | P2_PROTMAX_DISABLE | P2_TRAPCAP |
-	    P2_STKGAP_DISABLE | P2_STKGAP_DISABLE_EXEC);
+	    P2_STKGAP_DISABLE | P2_STKGAP_DISABLE_EXEC | P2_NO_NEW_PRIVS);
 	p2->p_swtick = ticks;
 	if (p1->p_flag & P_PROFIL)
 		startprofclock(p2);
diff --git a/sys/kern/kern_procctl.c b/sys/kern/kern_procctl.c
index b6f6f1b772b2..4eb226c6b1b3 100644
--- a/sys/kern/kern_procctl.c
+++ b/sys/kern/kern_procctl.c
@@ -419,6 +419,27 @@ trapcap_status(struct thread *td, struct proc *p, int *data)
 	return (0);
 }
 
+static int
+no_new_privs_ctl(struct thread *td, struct proc *p, int state)
+{
+
+	PROC_LOCK_ASSERT(p, MA_OWNED);
+
+	if (state != PROC_NO_NEW_PRIVS_ENABLE)
+		return (EINVAL);
+	p->p_flag2 |= P2_NO_NEW_PRIVS;
+	return (0);
+}
+
+static int
+no_new_privs_status(struct thread *td, struct proc *p, int *data)
+{
+
+	*data = (p->p_flag2 & P2_NO_NEW_PRIVS) != 0 ?
+	    PROC_NO_NEW_PRIVS_ENABLE : PROC_NO_NEW_PRIVS_DISABLE;
+	return (0);
+}
+
 static int
 protmax_ctl(struct thread *td, struct proc *p, int state)
 {
@@ -600,6 +621,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
 	case PROC_STACKGAP_CTL:
 	case PROC_TRACE_CTL:
 	case PROC_TRAPCAP_CTL:
+	case PROC_NO_NEW_PRIVS_CTL:
 		error = copyin(uap->data, &flags, sizeof(flags));
 		if (error != 0)
 			return (error);
@@ -631,6 +653,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
 	case PROC_STACKGAP_STATUS:
 	case PROC_TRACE_STATUS:
 	case PROC_TRAPCAP_STATUS:
+	case PROC_NO_NEW_PRIVS_STATUS:
 		data = &flags;
 		break;
 	case PROC_PDEATHSIG_CTL:
@@ -661,6 +684,7 @@ sys_procctl(struct thread *td, struct procctl_args *uap)
 	case PROC_STACKGAP_STATUS:
 	case PROC_TRACE_STATUS:
 	case PROC_TRAPCAP_STATUS:
+	case PROC_NO_NEW_PRIVS_STATUS:
 		if (error == 0)
 			error = copyout(&flags, uap->data, sizeof(flags));
 		break;
@@ -710,6 +734,10 @@ kern_procctl_single(struct thread *td, struct proc *p, int com, void *data)
 		return (trapcap_ctl(td, p, *(int *)data));
 	case PROC_TRAPCAP_STATUS:
 		return (trapcap_status(td, p, data));
+	case PROC_NO_NEW_PRIVS_CTL:
+		return (no_new_privs_ctl(td, p, *(int *)data));
+	case PROC_NO_NEW_PRIVS_STATUS:
+		return (no_new_privs_status(td, p, data));
 	default:
 		return (EINVAL);
 	}
@@ -740,6 +768,8 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
 	case PROC_TRAPCAP_STATUS:
 	case PROC_PDEATHSIG_CTL:
 	case PROC_PDEATHSIG_STATUS:
+	case PROC_NO_NEW_PRIVS_CTL:
+	case PROC_NO_NEW_PRIVS_STATUS:
 		if (idtype != P_PID)
 			return (EINVAL);
 	}
@@ -772,6 +802,7 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
 	case PROC_REAP_KILL:
 	case PROC_TRACE_CTL:
 	case PROC_TRAPCAP_CTL:
+	case PROC_NO_NEW_PRIVS_CTL:
 		sx_slock(&proctree_lock);
 		tree_locked = true;
 		break;
@@ -788,6 +819,7 @@ kern_procctl(struct thread *td, idtype_t idtype, id_t id, int com, void *data)
 	case PROC_STACKGAP_STATUS:
 	case PROC_TRACE_STATUS:
 	case PROC_TRAPCAP_STATUS:
+	case PROC_NO_NEW_PRIVS_STATUS:
 		tree_locked = false;
 		break;
 	default:
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index 19e8d76c6f99..9813324bfa69 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -835,6 +835,7 @@ struct proc {
 						   after exec */
 #define	P2_ITSTOPPED		0x00002000
 #define	P2_PTRACEREQ		0x00004000	/* Active ptrace req */
+#define	P2_NO_NEW_PRIVS		0x00008000	/* Ignore setuid */
 
 /* Flags protected by proctree_lock, kept in p_treeflags. */
 #define	P_TREE_ORPHANED		0x00000001	/* Reparented, on orphan list */
diff --git a/sys/sys/procctl.h b/sys/sys/procctl.h
index 90fb149830dc..cc0279fb0d08 100644
--- a/sys/sys/procctl.h
+++ b/sys/sys/procctl.h
@@ -63,6 +63,8 @@
 #define	PROC_PROTMAX_STATUS	16	/* query implicit PROT_MAX status */
 #define	PROC_STACKGAP_CTL	17	/* en/dis stack gap on MAP_STACK */
 #define	PROC_STACKGAP_STATUS	18	/* query stack gap */
+#define	PROC_NO_NEW_PRIVS_CTL	19	/* disable setuid/setgid */
+#define	PROC_NO_NEW_PRIVS_STATUS 20	/* query suid/sgid disabled status */
 
 /* Operations for PROC_SPROTECT (passed in integer arg). */
 #define	PPROT_OP(x)	((x) & 0xf)
@@ -141,6 +143,9 @@ struct procctl_reaper_kill {
 #define	PROC_STACKGAP_ENABLE_EXEC	0x0004
 #define	PROC_STACKGAP_DISABLE_EXEC	0x0008
 
+#define	PROC_NO_NEW_PRIVS_ENABLE	1
+#define	PROC_NO_NEW_PRIVS_DISABLE	2
+
 #ifndef _KERNEL
 __BEGIN_DECLS
 int	procctl(idtype_t, id_t, int, void *);


More information about the dev-commits-src-all mailing list