diff --git a/lib/libc/sys/procctl.2 b/lib/libc/sys/procctl.2 --- 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 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 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -3643,6 +3643,7 @@ 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 @@ 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 @@ 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 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -781,8 +781,9 @@ 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 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -492,7 +492,7 @@ 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 --- a/sys/kern/kern_procctl.c +++ b/sys/kern/kern_procctl.c @@ -419,6 +419,27 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -835,6 +835,7 @@ 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 --- 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 @@ #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 *);