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
This commit is contained in:
Edward Tomasz Napierala 2021-07-01 09:11:11 +01:00
parent 93c3453f11
commit db8d680ebe
7 changed files with 71 additions and 4 deletions

View File

@ -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

View File

@ -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;

View File

@ -781,8 +781,9 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p,
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);
/*

View File

@ -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);

View File

@ -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:

View File

@ -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 */

View File

@ -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 *);