diff --git a/lib/libc/include/compat.h b/lib/libc/include/compat.h index 70fb8dcd97f3..97f22607ddd7 100644 --- a/lib/libc/include/compat.h +++ b/lib/libc/include/compat.h @@ -1,80 +1,83 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009 Hudson River Trading LLC * Written by: John H. Baldwin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This file defines compatibility symbol versions for old system calls. It * is included in all generated system call files. */ #ifndef __LIBC_COMPAT_H__ #define __LIBC_COMPAT_H__ #define __sym_compat(sym, impl, verid) \ .symver impl, sym@verid #ifndef NO_COMPAT7 __sym_compat(__semctl, freebsd7___semctl, FBSD_1.0); __sym_compat(msgctl, freebsd7_msgctl, FBSD_1.0); __sym_compat(shmctl, freebsd7_shmctl, FBSD_1.0); #endif __sym_compat(nfstat, freebsd11_nfstat, FBSD_1.0); __sym_compat(nlstat, freebsd11_nlstat, FBSD_1.0); __sym_compat(nstat, freebsd11_nstat, FBSD_1.0); __sym_compat(fhstat, freebsd11_fhstat, FBSD_1.0); __sym_compat(fstat, freebsd11_fstat, FBSD_1.0); __sym_compat(fstatat, freebsd11_fstatat, FBSD_1.1); __sym_compat(lstat, freebsd11_lstat, FBSD_1.0); __sym_compat(stat, freebsd11_stat, FBSD_1.0); __sym_compat(getdents, freebsd11_getdents, FBSD_1.0); __sym_compat(getdirentries, freebsd11_getdirentries, FBSD_1.0); __sym_compat(getfsstat, freebsd11_getfsstat, FBSD_1.0); __sym_compat(fhstatfs, freebsd11_fhstatfs, FBSD_1.0); __sym_compat(fstatfs, freebsd11_fstatfs, FBSD_1.0); __sym_compat(statfs, freebsd11_statfs, FBSD_1.0); __sym_compat(mknod, freebsd11_mknod, FBSD_1.0); __sym_compat(mknodat, freebsd11_mknodat, FBSD_1.1); __sym_compat(kevent, freebsd11_kevent, FBSD_1.0); __sym_compat(swapoff, freebsd13_swapoff, FBSD_1.0); +__sym_compat(getgroups, freebsd14_getgroups, FBSD_1.0); +__sym_compat(setgroups, freebsd14_setgroups, FBSD_1.0); + #undef __sym_compat #define __weak_reference(sym,alias) \ .weak alias;.equ alias,sym #undef __weak_reference #endif /* __LIBC_COMPAT_H__ */ diff --git a/lib/libsys/Symbol.sys.map b/lib/libsys/Symbol.sys.map index 45e0160100af..1a297f9df581 100644 --- a/lib/libsys/Symbol.sys.map +++ b/lib/libsys/Symbol.sys.map @@ -1,396 +1,396 @@ /* * It'd be nice to automatically generate the syscall symbols, but we * don't know to what version they will eventually belong to, so for now * it has to be manual. */ FBSD_1.0 { __acl_aclcheck_fd; __acl_aclcheck_file; __acl_aclcheck_link; __acl_delete_fd; __acl_delete_file; __acl_delete_link; __acl_get_fd; __acl_get_file; __acl_get_link; __acl_set_fd; __acl_set_file; __acl_set_link; __getcwd; __mac_execve; __mac_get_fd; __mac_get_file; __mac_get_link; __mac_get_pid; __mac_get_proc; __mac_set_fd; __mac_set_file; __mac_set_link; __mac_set_proc; __setugid; __syscall; __sysctl; _exit; _umtx_op; abort2; access; acct; adjtime; aio_cancel; aio_error; aio_fsync; aio_read; aio_return; aio_waitcomplete; aio_write; audit; auditctl; auditon; bind; chdir; chflags; chmod; chown; chroot; clock_getres; clock_gettime; clock_settime; dup; dup2; eaccess; execve; extattr_delete_fd; extattr_delete_file; extattr_delete_link; extattr_get_fd; extattr_get_file; extattr_get_link; extattr_list_fd; extattr_list_file; extattr_list_link; extattr_set_fd; extattr_set_file; extattr_set_link; extattrctl; fchdir; fchflags; fchmod; fchown; fhopen; flock; fpathconf; futimes; getaudit; getaudit_addr; getauid; getcontext; getdtablesize; getegid; geteuid; getfh; getgid; - getgroups; getitimer; getpagesize; getpeername; getpgid; getpgrp; getpid; getppid; getpriority; getresgid; getresuid; getrlimit; getrusage; getsid; getsockname; getsockopt; gettimeofday; getuid; ioctl; issetugid; jail; jail_attach; kenv; kill; kldfind; kldfirstmod; kldload; kldnext; kldstat; kldsym; kldunload; kldunloadf; kqueue; kmq_notify; /* Do we want these to be public interfaces? */ kmq_open; /* librt uses them to provide mq_xxx. */ kmq_setattr; kmq_timedreceive; kmq_timedsend; kmq_unlink; ksem_close; ksem_destroy; ksem_getvalue; ksem_init; ksem_open; ksem_post; ksem_timedwait; ksem_trywait; ksem_unlink; ksem_wait; ktrace; lchflags; lchmod; lchown; lgetfh; link; lio_listio; listen; lutimes; mac_syscall; madvise; mincore; minherit; mkdir; mkfifo; mlock; mlockall; modfind; modfnext; modnext; modstat; mount; mprotect; msgget; msgrcv; msgsnd; msgsys; munlock; munlockall; munmap; nfssvc; nmount; ntp_adjtime; ntp_gettime; pathconf; posix_openpt; preadv; profil; pwritev; quotactl; readlink; reboot; rename; revoke; rfork; rmdir; rtprio; rtprio_thread; sched_get_priority_max; sched_get_priority_min; sched_getparam; sched_getscheduler; sched_rr_get_interval; sched_setparam; sched_setscheduler; sched_yield; semget; semop; semsys; sendfile; setaudit; setaudit_addr; setauid; setegid; seteuid; setgid; - setgroups; setitimer; setlogin; setpgid; setpriority; setregid; setresgid; setresuid; setreuid; setrlimit; setsid; setsockopt; settimeofday; setuid; shm_unlink; shmat; shmdt; shmget; shmsys; shutdown; sigaltstack; sigpending; sigqueue; sigreturn; socket; socketpair; swapon; symlink; sync; sysarch; syscall; thr_create; thr_exit; thr_kill; thr_kill2; thr_new; thr_self; thr_set_name; thr_suspend; thr_wake; ktimer_create; /* Do we want these to be public interfaces? */ ktimer_delete; /* librt uses them to provide timer_xxx. */ ktimer_getoverrun; ktimer_gettime; ktimer_settime; umask; undelete; unlink; unmount; utimes; utrace; uuidgen; vfork; __error; ftruncate; lseek; mmap; pread; pwrite; truncate; }; FBSD_1.1 { __semctl; cpuset; cpuset_getid; cpuset_setid; cpuset_getaffinity; cpuset_setaffinity; faccessat; fchmodat; fchownat; fexecve; futimesat; jail_get; jail_set; jail_remove; linkat; lpathconf; mkdirat; mkfifoat; msgctl; readlinkat; renameat; setfib; shmctl; symlinkat; unlinkat; }; FBSD_1.2 { cap_enter; cap_getmode; getloginclass; getpagesizes; pdgetpid; pdkill; posix_fallocate; rctl_get_racct; rctl_get_rules; rctl_get_limits; rctl_add_rule; rctl_remove_rule; setloginclass; }; FBSD_1.3 { aio_mlock; bindat; cap_fcntls_get; cap_fcntls_limit; cap_ioctls_get; cap_ioctls_limit; __cap_rights_get; cap_rights_limit; chflagsat; clock_getcpuclockid2; connectat; ffclock_getcounter; ffclock_getestimate; ffclock_setestimate; pipe2; posix_fadvise; procctl; }; FBSD_1.4 { futimens; utimensat; }; FBSD_1.5 { elf_aux_info; fhstat; fhstatfs; fstat; fstatat; fstatfs; getdirentries; getfsstat; getrandom; mknodat; statfs; cpuset_getdomain; cpuset_setdomain; }; FBSD_1.6 { __sysctlbyname; aio_readv; aio_writev; close_range; copy_file_range; fhlink; fhlinkat; fhreadlink; getfhat; funlinkat; shm_rename; }; FBSD_1.7 { fspacectl; kqueuex; membarrier; sched_getcpu; swapoff; timerfd_create; timerfd_gettime; timerfd_settime; }; FBSD_1.8 { exterrctl; fchroot; + getgroups; getrlimitusage; inotify_add_watch_at; inotify_rm_watch; kcmp; setcred; + setgroups; }; FBSDprivate_1.0 { /* Add entries in sort(1) order */ __set_error_selector; __sigwait; nlm_syscall; rpctls_syscall; }; diff --git a/lib/libsys/getgroups.2 b/lib/libsys/getgroups.2 index 91cca2748ec2..37c8fbad7215 100644 --- a/lib/libsys/getgroups.2 +++ b/lib/libsys/getgroups.2 @@ -1,104 +1,111 @@ .\" Copyright (c) 1983, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd January 21, 2011 +.Dd August 1, 2025 .Dt GETGROUPS 2 .Os .Sh NAME .Nm getgroups .Nd get group access list .Sh LIBRARY .Lb libc .Sh SYNOPSIS .In unistd.h .Ft int .Fn getgroups "int gidsetlen" "gid_t *gidset" .Sh DESCRIPTION The .Fn getgroups system call -gets the current group access list of the user process -and stores it in the array +gets the current supplementary groups of the user process and stores it in the +array .Fa gidset . The .Fa gidsetlen argument indicates the number of entries that may be placed in .Fa gidset . The .Fn getgroups system call returns the actual number of groups returned in .Fa gidset . -At least one and as many as {NGROUPS_MAX}+1 values may be returned. +As many as {NGROUPS_MAX} values may be returned. If .Fa gidsetlen is zero, .Fn getgroups returns the number of supplementary group IDs associated with the calling process without modifying the array pointed to by .Fa gidset . .Pp The value of .Dv {NGROUPS_MAX} should be obtained using .Xr sysconf 3 to avoid hard-coding it into the executable. .Sh RETURN VALUES A successful call returns the number of groups in the group set. A value of -1 indicates that an error occurred, and the error code is stored in the global variable .Va errno . .Sh ERRORS The possible errors for .Fn getgroups are: .Bl -tag -width Er .It Bq Er EINVAL The argument .Fa gidsetlen is smaller than the number of groups in the group set. .It Bq Er EFAULT The argument .Fa gidset specifies an invalid address. .El .Sh SEE ALSO .Xr setgroups 2 , .Xr initgroups 3 , .Xr sysconf 3 .Sh STANDARDS The .Fn getgroups system call conforms to .St -p1003.1-2008 . .Sh HISTORY The .Fn getgroups system call appeared in .Bx 4.2 . +.Pp +Before +.Fx 15.0 , +the +.Fn getgroups +system call always returned the effective group ID for the process as the first +element of the array, before the supplementary groups. diff --git a/lib/libsys/setgroups.2 b/lib/libsys/setgroups.2 index a226aeafea96..451f63ba1266 100644 --- a/lib/libsys/setgroups.2 +++ b/lib/libsys/setgroups.2 @@ -1,101 +1,95 @@ .\" Copyright (c) 1983, 1991, 1993, 1994 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd January 19, 2018 +.Dd August 1, 2025 .Dt SETGROUPS 2 .Os .Sh NAME .Nm setgroups .Nd set group access list .Sh LIBRARY .Lb libc .Sh SYNOPSIS .In sys/param.h .In unistd.h .Ft int .Fn setgroups "int ngroups" "const gid_t *gidset" .Sh DESCRIPTION The .Fn setgroups system call -sets the group access list of the current user process +sets the supplementary group list of the current user process according to the array .Fa gidset . The .Fa ngroups argument indicates the number of entries in the array and must be no more than -.Dv {NGROUPS_MAX}+1 . -.Pp -Only the super-user may set a new group list. +.Dv {NGROUPS_MAX} . +The +.Fa ngroups +argument may be set to 0 to clear the supplementary group list. .Pp -The first entry of the group array -.Pq Va gidset[0] -is used as the effective group-ID for the process. -This entry is over-written when a setgid program is run. -To avoid losing access to the privileges of the -.Va gidset[0] -entry, it should be duplicated later in the group array. -By convention, -this happens because the group value indicated -in the password file also appears in -.Pa /etc/group . -The group value in the password file is placed in -.Va gidset[0] -and that value then gets added a second time when the -.Pa /etc/group -file is scanned to create the group set. +Only the super-user may set a new supplementary group list. .Sh RETURN VALUES .Rv -std setgroups .Sh ERRORS The .Fn setgroups system call will fail if: .Bl -tag -width Er .It Bq Er EPERM The caller is not the super-user. .It Bq Er EINVAL The number specified in the .Fa ngroups argument is larger than the .Dv {NGROUPS_MAX}+1 limit. .It Bq Er EFAULT The address specified for .Fa gidset is outside the process address space. .El .Sh SEE ALSO .Xr getgroups 2 , .Xr initgroups 3 .Sh HISTORY The .Fn setgroups system call appeared in .Bx 4.2 . +.Pp +Before +.Fx 15.0 , +the +.Fn setgroups +system call would set the effective group ID for the process to the first +element of +.Fa gidset . diff --git a/sbin/hastd/subr.c b/sbin/hastd/subr.c index 2a26482b3727..284fb0d07647 100644 --- a/sbin/hastd/subr.c +++ b/sbin/hastd/subr.c @@ -1,298 +1,295 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2010 The FreeBSD Foundation * Copyright (c) 2011 Pawel Jakub Dawidek * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #ifdef HAVE_CAPSICUM #include #include #endif #include #include #include #include #include #include #include #include #include #include "hast.h" #include "subr.h" int vsnprlcat(char *str, size_t size, const char *fmt, va_list ap) { size_t len; len = strlen(str); return (vsnprintf(str + len, size - len, fmt, ap)); } int snprlcat(char *str, size_t size, const char *fmt, ...) { va_list ap; int result; va_start(ap, fmt); result = vsnprlcat(str, size, fmt, ap); va_end(ap); return (result); } int provinfo(struct hast_resource *res, bool dowrite) { struct stat sb; PJDLOG_ASSERT(res->hr_localpath != NULL && res->hr_localpath[0] != '\0'); if (res->hr_localfd == -1) { res->hr_localfd = open(res->hr_localpath, dowrite ? O_RDWR : O_RDONLY); if (res->hr_localfd == -1) { pjdlog_errno(LOG_ERR, "Unable to open %s", res->hr_localpath); return (-1); } } if (fstat(res->hr_localfd, &sb) == -1) { pjdlog_errno(LOG_ERR, "Unable to stat %s", res->hr_localpath); return (-1); } if (S_ISCHR(sb.st_mode)) { /* * If this is character device, it is most likely GEOM provider. */ if (ioctl(res->hr_localfd, DIOCGMEDIASIZE, &res->hr_local_mediasize) == -1) { pjdlog_errno(LOG_ERR, "Unable obtain provider %s mediasize", res->hr_localpath); return (-1); } if (ioctl(res->hr_localfd, DIOCGSECTORSIZE, &res->hr_local_sectorsize) == -1) { pjdlog_errno(LOG_ERR, "Unable obtain provider %s sectorsize", res->hr_localpath); return (-1); } } else if (S_ISREG(sb.st_mode)) { /* * We also support regular files for which we hardcode * sector size of 512 bytes. */ res->hr_local_mediasize = sb.st_size; res->hr_local_sectorsize = 512; } else { /* * We support no other file types. */ pjdlog_error("%s is neither GEOM provider nor regular file.", res->hr_localpath); errno = EFTYPE; return (-1); } return (0); } const char * role2str(int role) { switch (role) { case HAST_ROLE_INIT: return ("init"); case HAST_ROLE_PRIMARY: return ("primary"); case HAST_ROLE_SECONDARY: return ("secondary"); } return ("unknown"); } int drop_privs(const struct hast_resource *res) { char jailhost[sizeof(res->hr_name) * 2]; struct jail jailst; struct passwd *pw; uid_t ruid, euid, suid; gid_t rgid, egid, sgid; gid_t gidset[1]; bool capsicum, jailed; /* * According to getpwnam(3) we have to clear errno before calling the * function to be able to distinguish between an error and missing * entry (with is not treated as error by getpwnam(3)). */ errno = 0; pw = getpwnam(HAST_USER); if (pw == NULL) { if (errno != 0) { pjdlog_errno(LOG_ERR, "Unable to find info about '%s' user", HAST_USER); return (-1); } else { pjdlog_error("'%s' user doesn't exist.", HAST_USER); errno = ENOENT; return (-1); } } bzero(&jailst, sizeof(jailst)); jailst.version = JAIL_API_VERSION; jailst.path = pw->pw_dir; if (res == NULL) { (void)snprintf(jailhost, sizeof(jailhost), "hastctl"); } else { (void)snprintf(jailhost, sizeof(jailhost), "hastd: %s (%s)", res->hr_name, role2str(res->hr_role)); } jailst.hostname = jailhost; jailst.jailname = NULL; jailst.ip4s = 0; jailst.ip4 = NULL; jailst.ip6s = 0; jailst.ip6 = NULL; if (jail(&jailst) >= 0) { jailed = true; } else { jailed = false; pjdlog_errno(LOG_WARNING, "Unable to jail to directory to %s", pw->pw_dir); if (chroot(pw->pw_dir) == -1) { pjdlog_errno(LOG_ERR, "Unable to change root directory to %s", pw->pw_dir); return (-1); } } PJDLOG_VERIFY(chdir("/") == 0); - gidset[0] = pw->pw_gid; - if (setgroups(1, gidset) == -1) { - pjdlog_errno(LOG_ERR, "Unable to set groups to gid %u", - (unsigned int)pw->pw_gid); + if (setgroups(0, NULL) == -1) { + pjdlog_errno(LOG_ERR, "Unable to drop supplementary groups"); return (-1); } if (setgid(pw->pw_gid) == -1) { pjdlog_errno(LOG_ERR, "Unable to set gid to %u", (unsigned int)pw->pw_gid); return (-1); } if (setuid(pw->pw_uid) == -1) { pjdlog_errno(LOG_ERR, "Unable to set uid to %u", (unsigned int)pw->pw_uid); return (-1); } #ifdef HAVE_CAPSICUM capsicum = (cap_enter() == 0); if (!capsicum) { pjdlog_common(LOG_DEBUG, 1, errno, "Unable to sandbox using capsicum"); } else if (res != NULL) { cap_rights_t rights; static const unsigned long geomcmds[] = { DIOCGDELETE, DIOCGFLUSH }; PJDLOG_ASSERT(res->hr_role == HAST_ROLE_PRIMARY || res->hr_role == HAST_ROLE_SECONDARY); cap_rights_init(&rights, CAP_FLOCK, CAP_IOCTL, CAP_PREAD, CAP_PWRITE); if (cap_rights_limit(res->hr_localfd, &rights) == -1) { pjdlog_errno(LOG_ERR, "Unable to limit capability rights on local descriptor"); } if (cap_ioctls_limit(res->hr_localfd, geomcmds, nitems(geomcmds)) == -1) { pjdlog_errno(LOG_ERR, "Unable to limit allowed GEOM ioctls"); } if (res->hr_role == HAST_ROLE_PRIMARY) { static const unsigned long ggatecmds[] = { G_GATE_CMD_MODIFY, G_GATE_CMD_START, G_GATE_CMD_DONE, G_GATE_CMD_DESTROY }; cap_rights_init(&rights, CAP_IOCTL); if (cap_rights_limit(res->hr_ggatefd, &rights) == -1) { pjdlog_errno(LOG_ERR, "Unable to limit capability rights to CAP_IOCTL on ggate descriptor"); } if (cap_ioctls_limit(res->hr_ggatefd, ggatecmds, nitems(ggatecmds)) == -1) { pjdlog_errno(LOG_ERR, "Unable to limit allowed ggate ioctls"); } } } #else capsicum = false; #endif /* * Better be sure that everything succeeded. */ PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0); PJDLOG_VERIFY(ruid == pw->pw_uid); PJDLOG_VERIFY(euid == pw->pw_uid); PJDLOG_VERIFY(suid == pw->pw_uid); PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0); PJDLOG_VERIFY(rgid == pw->pw_gid); PJDLOG_VERIFY(egid == pw->pw_gid); PJDLOG_VERIFY(sgid == pw->pw_gid); PJDLOG_VERIFY(getgroups(0, NULL) == 1); - PJDLOG_VERIFY(getgroups(1, gidset) == 1); - PJDLOG_VERIFY(gidset[0] == pw->pw_gid); + PJDLOG_VERIFY(getgroups(1, gidset) == 0); pjdlog_debug(1, "Privileges successfully dropped using %s%s+setgid+setuid.", capsicum ? "capsicum+" : "", jailed ? "jail" : "chroot"); return (0); } diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c index 2cd5b7069023..beab30a9d157 100644 --- a/sys/kern/kern_prot.c +++ b/sys/kern/kern_prot.c @@ -1,3170 +1,3200 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1982, 1986, 1989, 1990, 1991, 1993 * The Regents of the University of California. * (c) UNIX System Laboratories, Inc. * Copyright (c) 2000-2001 Robert N. M. Watson. * All rights reserved. * * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * System calls related to processes and protection */ #include #include "opt_inet.h" #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef COMPAT_43 #include #endif #include #include #include #include #include #include #include #include #include #ifdef MAC #include #endif #include #ifdef REGRESSION FEATURE(regression, "Kernel support for interfaces necessary for regression testing (SECURITY RISK!)"); #endif #include #include static MALLOC_DEFINE(M_CRED, "cred", "credentials"); SYSCTL_NODE(_security, OID_AUTO, bsd, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "BSD security policy"); static void crfree_final(struct ucred *cr); static inline void groups_check_positive_len(int ngrp) { MPASS2(ngrp >= 0, "negative number of groups"); } static inline void groups_check_max_len(int ngrp) { MPASS2(ngrp <= ngroups_max, "too many supplementary groups"); } static void groups_normalize(int *ngrp, gid_t *groups); static void crsetgroups_internal(struct ucred *cr, int ngrp, const gid_t *groups); static int cr_canseeotheruids(struct ucred *u1, struct ucred *u2); static int cr_canseeothergids(struct ucred *u1, struct ucred *u2); static int cr_canseejailproc(struct ucred *u1, struct ucred *u2); #ifndef _SYS_SYSPROTO_H_ struct getpid_args { int dummy; }; #endif /* ARGSUSED */ int sys_getpid(struct thread *td, struct getpid_args *uap) { struct proc *p = td->td_proc; td->td_retval[0] = p->p_pid; #if defined(COMPAT_43) if (SV_PROC_FLAG(p, SV_AOUT)) td->td_retval[1] = kern_getppid(td); #endif return (0); } #ifndef _SYS_SYSPROTO_H_ struct getppid_args { int dummy; }; #endif /* ARGSUSED */ int sys_getppid(struct thread *td, struct getppid_args *uap) { td->td_retval[0] = kern_getppid(td); return (0); } int kern_getppid(struct thread *td) { struct proc *p = td->td_proc; return (p->p_oppid); } /* * Get process group ID; note that POSIX getpgrp takes no parameter. */ #ifndef _SYS_SYSPROTO_H_ struct getpgrp_args { int dummy; }; #endif int sys_getpgrp(struct thread *td, struct getpgrp_args *uap) { struct proc *p = td->td_proc; PROC_LOCK(p); td->td_retval[0] = p->p_pgrp->pg_id; PROC_UNLOCK(p); return (0); } /* Get an arbitrary pid's process group id */ #ifndef _SYS_SYSPROTO_H_ struct getpgid_args { pid_t pid; }; #endif int sys_getpgid(struct thread *td, struct getpgid_args *uap) { struct proc *p; int error; if (uap->pid == 0) { p = td->td_proc; PROC_LOCK(p); } else { p = pfind(uap->pid); if (p == NULL) return (ESRCH); error = p_cansee(td, p); if (error) { PROC_UNLOCK(p); return (error); } } td->td_retval[0] = p->p_pgrp->pg_id; PROC_UNLOCK(p); return (0); } /* * Get an arbitrary pid's session id. */ #ifndef _SYS_SYSPROTO_H_ struct getsid_args { pid_t pid; }; #endif int sys_getsid(struct thread *td, struct getsid_args *uap) { return (kern_getsid(td, uap->pid)); } int kern_getsid(struct thread *td, pid_t pid) { struct proc *p; int error; if (pid == 0) { p = td->td_proc; PROC_LOCK(p); } else { p = pfind(pid); if (p == NULL) return (ESRCH); error = p_cansee(td, p); if (error) { PROC_UNLOCK(p); return (error); } } td->td_retval[0] = p->p_session->s_sid; PROC_UNLOCK(p); return (0); } #ifndef _SYS_SYSPROTO_H_ struct getuid_args { int dummy; }; #endif /* ARGSUSED */ int sys_getuid(struct thread *td, struct getuid_args *uap) { td->td_retval[0] = td->td_ucred->cr_ruid; #if defined(COMPAT_43) td->td_retval[1] = td->td_ucred->cr_uid; #endif return (0); } #ifndef _SYS_SYSPROTO_H_ struct geteuid_args { int dummy; }; #endif /* ARGSUSED */ int sys_geteuid(struct thread *td, struct geteuid_args *uap) { td->td_retval[0] = td->td_ucred->cr_uid; return (0); } #ifndef _SYS_SYSPROTO_H_ struct getgid_args { int dummy; }; #endif /* ARGSUSED */ int sys_getgid(struct thread *td, struct getgid_args *uap) { td->td_retval[0] = td->td_ucred->cr_rgid; #if defined(COMPAT_43) td->td_retval[1] = td->td_ucred->cr_gid; #endif return (0); } /* * Get effective group ID. The "egid" is groups[0], and could be obtained * via getgroups. This syscall exists because it is somewhat painful to do * correctly in a library function. */ #ifndef _SYS_SYSPROTO_H_ struct getegid_args { int dummy; }; #endif /* ARGSUSED */ int sys_getegid(struct thread *td, struct getegid_args *uap) { td->td_retval[0] = td->td_ucred->cr_gid; return (0); } +#ifdef COMPAT_FREEBSD14 +int +freebsd14_getgroups(struct thread *td, struct freebsd14_getgroups_args *uap) +{ + struct ucred *cred; + int ngrp, error; + + cred = td->td_ucred; + + /* + * For FreeBSD < 15.0, we account for the egid being placed at the + * beginning of the group list prior to all supplementary groups. + */ + ngrp = cred->cr_ngroups + 1; + if (uap->gidsetsize == 0) { + error = 0; + goto out; + } else if (uap->gidsetsize < ngrp) { + return (EINVAL); + } + + error = copyout(&cred->cr_gid, uap->gidset, sizeof(gid_t)); + if (error != 0) + error = copyout(cred->cr_groups, uap->gidset + 1, + (ngrp - 1) * sizeof(gid_t)); + +out: + td->td_retval[0] = ngrp; + return (error); + +} +#endif /* COMPAT_FREEBSD14 */ + #ifndef _SYS_SYSPROTO_H_ struct getgroups_args { int gidsetsize; gid_t *gidset; }; #endif int sys_getgroups(struct thread *td, struct getgroups_args *uap) { struct ucred *cred; - gid_t *ugidset; int ngrp, error; cred = td->td_ucred; - /* - * cr_gid has been moved out of cr_groups, but we'll continue exporting - * the egid as groups[0] for the time being until we audit userland for - * any surprises. - */ - ngrp = cred->cr_ngroups + 1; - + ngrp = cred->cr_ngroups; if (uap->gidsetsize == 0) { error = 0; goto out; } if (uap->gidsetsize < ngrp) return (EINVAL); - ugidset = uap->gidset; - error = copyout(&cred->cr_gid, ugidset, sizeof(*ugidset)); - if (error != 0) - goto out; - - if (ngrp > 1) - error = copyout(cred->cr_groups, ugidset + 1, - (ngrp - 1) * sizeof(*ugidset)); + error = copyout(cred->cr_groups, uap->gidset, ngrp * sizeof(gid_t)); out: td->td_retval[0] = ngrp; return (error); } #ifndef _SYS_SYSPROTO_H_ struct setsid_args { int dummy; }; #endif /* ARGSUSED */ int sys_setsid(struct thread *td, struct setsid_args *uap) { struct pgrp *pgrp; int error; struct proc *p = td->td_proc; struct pgrp *newpgrp; struct session *newsess; pgrp = NULL; newpgrp = uma_zalloc(pgrp_zone, M_WAITOK); newsess = malloc(sizeof(struct session), M_SESSION, M_WAITOK | M_ZERO); again: error = 0; sx_xlock(&proctree_lock); if (p->p_pgid == p->p_pid || (pgrp = pgfind(p->p_pid)) != NULL) { if (pgrp != NULL) PGRP_UNLOCK(pgrp); error = EPERM; } else { error = enterpgrp(p, p->p_pid, newpgrp, newsess); if (error == ERESTART) goto again; MPASS(error == 0); td->td_retval[0] = p->p_pid; newpgrp = NULL; newsess = NULL; } sx_xunlock(&proctree_lock); uma_zfree(pgrp_zone, newpgrp); free(newsess, M_SESSION); return (error); } /* * set process group (setpgid/old setpgrp) * * caller does setpgid(targpid, targpgid) * * pid must be caller or child of caller (ESRCH) * if a child * pid must be in same session (EPERM) * pid can't have done an exec (EACCES) * if pgid != pid * there must exist some pid in same session having pgid (EPERM) * pid must not be session leader (EPERM) */ #ifndef _SYS_SYSPROTO_H_ struct setpgid_args { int pid; /* target process id */ int pgid; /* target pgrp id */ }; #endif /* ARGSUSED */ int sys_setpgid(struct thread *td, struct setpgid_args *uap) { struct proc *curp = td->td_proc; struct proc *targp; /* target process */ struct pgrp *pgrp; /* target pgrp */ int error; struct pgrp *newpgrp; if (uap->pgid < 0) return (EINVAL); newpgrp = uma_zalloc(pgrp_zone, M_WAITOK); again: error = 0; sx_xlock(&proctree_lock); if (uap->pid != 0 && uap->pid != curp->p_pid) { if ((targp = pfind(uap->pid)) == NULL) { error = ESRCH; goto done; } if (!inferior(targp)) { PROC_UNLOCK(targp); error = ESRCH; goto done; } if ((error = p_cansee(td, targp))) { PROC_UNLOCK(targp); goto done; } if (targp->p_pgrp == NULL || targp->p_session != curp->p_session) { PROC_UNLOCK(targp); error = EPERM; goto done; } if (targp->p_flag & P_EXEC) { PROC_UNLOCK(targp); error = EACCES; goto done; } PROC_UNLOCK(targp); } else targp = curp; if (SESS_LEADER(targp)) { error = EPERM; goto done; } if (uap->pgid == 0) uap->pgid = targp->p_pid; if ((pgrp = pgfind(uap->pgid)) == NULL) { if (uap->pgid == targp->p_pid) { error = enterpgrp(targp, uap->pgid, newpgrp, NULL); if (error == 0) newpgrp = NULL; } else error = EPERM; } else { if (pgrp == targp->p_pgrp) { PGRP_UNLOCK(pgrp); goto done; } if (pgrp->pg_id != targp->p_pid && pgrp->pg_session != curp->p_session) { PGRP_UNLOCK(pgrp); error = EPERM; goto done; } PGRP_UNLOCK(pgrp); error = enterthispgrp(targp, pgrp); } done: KASSERT(error == 0 || newpgrp != NULL, ("setpgid failed and newpgrp is NULL")); if (error == ERESTART) goto again; sx_xunlock(&proctree_lock); uma_zfree(pgrp_zone, newpgrp); return (error); } static int gidp_cmp(const void *p1, const void *p2) { const gid_t g1 = *(const gid_t *)p1; const gid_t g2 = *(const gid_t *)p2; return ((g1 > g2) - (g1 < g2)); } /* * Final storage for supplementary groups will be returned via 'groups'. * '*groups' must be NULL on input, and if not equal to 'smallgroups' * on output, must be freed (M_TEMP) *even if* an error is returned. */ static int kern_setcred_copyin_supp_groups(struct setcred *const wcred, const u_int flags, gid_t *const smallgroups, gid_t **const groups) { MPASS(*groups == NULL); if (flags & SETCREDF_SUPP_GROUPS) { int error; /* * Check for the limit for number of groups right now in order * to limit the amount of bytes to copy. */ if (wcred->sc_supp_groups_nb > ngroups_max) return (EINVAL); /* * Since we are going to be copying the supplementary groups * from userland, make room also for the effective GID right * now, to avoid having to allocate and copy again the * supplementary groups. */ *groups = wcred->sc_supp_groups_nb <= CRED_SMALLGROUPS_NB ? smallgroups : malloc(wcred->sc_supp_groups_nb * sizeof(*groups), M_TEMP, M_WAITOK); error = copyin(wcred->sc_supp_groups, *groups, wcred->sc_supp_groups_nb * sizeof(*groups)); if (error != 0) return (error); wcred->sc_supp_groups = *groups; } else { wcred->sc_supp_groups_nb = 0; wcred->sc_supp_groups = NULL; } return (0); } int user_setcred(struct thread *td, const u_int flags, const void *const uwcred, const size_t size, bool is_32bit) { struct setcred wcred; #ifdef MAC struct mac mac; /* Pointer to 'struct mac' or 'struct mac32'. */ void *umac; #endif gid_t smallgroups[CRED_SMALLGROUPS_NB]; gid_t *groups = NULL; int error; /* * As the only point of this wrapper function is to copyin() from * userland, we only interpret the data pieces we need to perform this * operation and defer further sanity checks to kern_setcred(), except * that we redundantly check here that no unknown flags have been * passed. */ if ((flags & ~SETCREDF_MASK) != 0) return (EINVAL); #ifdef COMPAT_FREEBSD32 if (is_32bit) { struct setcred32 wcred32; if (size != sizeof(wcred32)) return (EINVAL); error = copyin(uwcred, &wcred32, sizeof(wcred32)); if (error != 0) return (error); /* These fields have exactly the same sizes and positions. */ memcpy(&wcred, &wcred32, &wcred32.setcred32_copy_end - &wcred32.setcred32_copy_start); /* Remaining fields are pointers and need PTRIN*(). */ PTRIN_CP(wcred32, wcred, sc_supp_groups); PTRIN_CP(wcred32, wcred, sc_label); } else #endif /* COMPAT_FREEBSD32 */ { if (size != sizeof(wcred)) return (EINVAL); error = copyin(uwcred, &wcred, sizeof(wcred)); if (error != 0) return (error); } #ifdef MAC umac = wcred.sc_label; #endif /* Also done on !MAC as a defensive measure. */ wcred.sc_label = NULL; /* * Copy supplementary groups as needed. There is no specific * alternative for 32-bit compatibility as 'gid_t' has the same size * everywhere. */ error = kern_setcred_copyin_supp_groups(&wcred, flags, smallgroups, &groups); if (error != 0) goto free_groups; #ifdef MAC if ((flags & SETCREDF_MAC_LABEL) != 0) { #ifdef COMPAT_FREEBSD32 if (is_32bit) error = mac_label_copyin32(umac, &mac, NULL); else #endif error = mac_label_copyin(umac, &mac, NULL); if (error != 0) goto free_groups; wcred.sc_label = &mac; } #endif error = kern_setcred(td, flags, &wcred, groups); #ifdef MAC if (wcred.sc_label != NULL) free_copied_label(wcred.sc_label); #endif free_groups: if (groups != smallgroups) free(groups, M_TEMP); return (error); } #ifndef _SYS_SYSPROTO_H_ struct setcred_args { u_int flags; /* Flags. */ const struct setcred *wcred; size_t size; /* Passed 'setcred' structure length. */ }; #endif /* ARGSUSED */ int sys_setcred(struct thread *td, struct setcred_args *uap) { return (user_setcred(td, uap->flags, uap->wcred, uap->size, false)); } /* * CAUTION: This function normalizes groups in 'wcred'. * * If 'preallocated_groups' is non-NULL, it must be an already allocated array * of size 'wcred->sc_supp_groups_nb' containing the supplementary groups, and * 'wcred->sc_supp_groups' then must point to it. */ int kern_setcred(struct thread *const td, const u_int flags, struct setcred *const wcred, gid_t *preallocated_groups) { struct proc *const p = td->td_proc; struct ucred *new_cred, *old_cred, *to_free_cred; struct uidinfo *uip = NULL, *ruip = NULL; #ifdef MAC void *mac_set_proc_data = NULL; bool proc_label_set = false; #endif gid_t *groups = NULL; gid_t smallgroups[CRED_SMALLGROUPS_NB]; int error; bool cred_set; /* Bail out on unrecognized flags. */ if (flags & ~SETCREDF_MASK) return (EINVAL); /* * Part 1: We allocate and perform preparatory operations with no locks. */ if (flags & SETCREDF_SUPP_GROUPS) { if (wcred->sc_supp_groups_nb > ngroups_max) return (EINVAL); if (preallocated_groups != NULL) { groups = preallocated_groups; MPASS(preallocated_groups == wcred->sc_supp_groups); } else { if (wcred->sc_supp_groups_nb <= CRED_SMALLGROUPS_NB) groups = smallgroups; else groups = malloc(wcred->sc_supp_groups_nb * sizeof(*groups), M_TEMP, M_WAITOK); memcpy(groups, wcred->sc_supp_groups, wcred->sc_supp_groups_nb * sizeof(*groups)); } } if (flags & SETCREDF_MAC_LABEL) { #ifdef MAC error = mac_set_proc_prepare(td, wcred->sc_label, &mac_set_proc_data); if (error != 0) goto free_groups; #else error = ENOTSUP; goto free_groups; #endif } if (flags & SETCREDF_UID) { AUDIT_ARG_EUID(wcred->sc_uid); uip = uifind(wcred->sc_uid); } if (flags & SETCREDF_RUID) { AUDIT_ARG_RUID(wcred->sc_ruid); ruip = uifind(wcred->sc_ruid); } if (flags & SETCREDF_SVUID) AUDIT_ARG_SUID(wcred->sc_svuid); if (flags & SETCREDF_GID) AUDIT_ARG_EGID(wcred->sc_gid); if (flags & SETCREDF_RGID) AUDIT_ARG_RGID(wcred->sc_rgid); if (flags & SETCREDF_SVGID) AUDIT_ARG_SGID(wcred->sc_svgid); if (flags & SETCREDF_SUPP_GROUPS) { /* * Output the raw supplementary groups array for better * traceability. */ AUDIT_ARG_GROUPSET(groups, wcred->sc_supp_groups_nb); groups_normalize(&wcred->sc_supp_groups_nb, groups); } /* * We first completely build the new credentials and only then pass them * to MAC along with the old ones so that modules can check whether the * requested transition is allowed. */ new_cred = crget(); to_free_cred = new_cred; if (flags & SETCREDF_SUPP_GROUPS) crextend(new_cred, wcred->sc_supp_groups_nb); #ifdef MAC mac_cred_setcred_enter(); #endif /* * Part 2: We grab the process lock as to have a stable view of its * current credentials, and prepare a copy of them with the requested * changes applied under that lock. */ PROC_LOCK(p); old_cred = crcopysafe(p, new_cred); /* * Change user IDs. */ if (flags & SETCREDF_UID) change_euid(new_cred, uip); if (flags & SETCREDF_RUID) change_ruid(new_cred, ruip); if (flags & SETCREDF_SVUID) change_svuid(new_cred, wcred->sc_svuid); /* * Change groups. */ if (flags & SETCREDF_SUPP_GROUPS) crsetgroups_internal(new_cred, wcred->sc_supp_groups_nb, groups); if (flags & SETCREDF_GID) change_egid(new_cred, wcred->sc_gid); if (flags & SETCREDF_RGID) change_rgid(new_cred, wcred->sc_rgid); if (flags & SETCREDF_SVGID) change_svgid(new_cred, wcred->sc_svgid); #ifdef MAC /* * Change the MAC label. */ if (flags & SETCREDF_MAC_LABEL) { error = mac_set_proc_core(td, new_cred, mac_set_proc_data); if (error != 0) goto unlock_finish; proc_label_set = true; } /* * MAC security modules checks. */ error = mac_cred_check_setcred(flags, old_cred, new_cred); if (error != 0) goto unlock_finish; #endif /* * Privilege check. */ error = priv_check_cred(old_cred, PRIV_CRED_SETCRED); if (error != 0) goto unlock_finish; /* * Set the new credentials, noting that they have changed. */ cred_set = proc_set_cred_enforce_proc_lim(p, new_cred); if (cred_set) { setsugid(p); to_free_cred = old_cred; MPASS(error == 0); } else error = EAGAIN; unlock_finish: PROC_UNLOCK(p); /* * Part 3: After releasing the process lock, we perform cleanups and * finishing operations. */ #ifdef MAC if (mac_set_proc_data != NULL) mac_set_proc_finish(td, proc_label_set, mac_set_proc_data); mac_cred_setcred_exit(); #endif crfree(to_free_cred); if (uip != NULL) uifree(uip); if (ruip != NULL) uifree(ruip); free_groups: if (groups != preallocated_groups && groups != smallgroups) free(groups, M_TEMP); /* Deals with 'groups' being NULL. */ return (error); } /* * Use the clause in B.4.2.2 that allows setuid/setgid to be 4.2/4.3BSD * compatible. It says that setting the uid/gid to euid/egid is a special * case of "appropriate privilege". Once the rules are expanded out, this * basically means that setuid(nnn) sets all three id's, in all permitted * cases unless _POSIX_SAVED_IDS is enabled. In that case, setuid(getuid()) * does not set the saved id - this is dangerous for traditional BSD * programs. For this reason, we *really* do not want to set * _POSIX_SAVED_IDS and do not want to clear POSIX_APPENDIX_B_4_2_2. */ #define POSIX_APPENDIX_B_4_2_2 #ifndef _SYS_SYSPROTO_H_ struct setuid_args { uid_t uid; }; #endif /* ARGSUSED */ int sys_setuid(struct thread *td, struct setuid_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; uid_t uid; struct uidinfo *uip; int error; uid = uap->uid; AUDIT_ARG_UID(uid); newcred = crget(); uip = uifind(uid); PROC_LOCK(p); /* * Copy credentials so other references do not see our changes. */ oldcred = crcopysafe(p, newcred); #ifdef MAC error = mac_cred_check_setuid(oldcred, uid); if (error) goto fail; #endif /* * See if we have "permission" by POSIX 1003.1 rules. * * Note that setuid(geteuid()) is a special case of * "appropriate privileges" in appendix B.4.2.2. We need * to use this clause to be compatible with traditional BSD * semantics. Basically, it means that "setuid(xx)" sets all * three id's (assuming you have privs). * * Notes on the logic. We do things in three steps. * 1: We determine if the euid is going to change, and do EPERM * right away. We unconditionally change the euid later if this * test is satisfied, simplifying that part of the logic. * 2: We determine if the real and/or saved uids are going to * change. Determined by compile options. * 3: Change euid last. (after tests in #2 for "appropriate privs") */ if (uid != oldcred->cr_ruid && /* allow setuid(getuid()) */ #ifdef _POSIX_SAVED_IDS uid != oldcred->cr_svuid && /* allow setuid(saved gid) */ #endif #ifdef POSIX_APPENDIX_B_4_2_2 /* Use BSD-compat clause from B.4.2.2 */ uid != oldcred->cr_uid && /* allow setuid(geteuid()) */ #endif (error = priv_check_cred(oldcred, PRIV_CRED_SETUID)) != 0) goto fail; #ifdef _POSIX_SAVED_IDS /* * Do we have "appropriate privileges" (are we root or uid == euid) * If so, we are changing the real uid and/or saved uid. */ if ( #ifdef POSIX_APPENDIX_B_4_2_2 /* Use the clause from B.4.2.2 */ uid == oldcred->cr_uid || #endif /* We are using privs. */ priv_check_cred(oldcred, PRIV_CRED_SETUID) == 0) #endif { /* * Set the real uid. */ if (uid != oldcred->cr_ruid) { change_ruid(newcred, uip); setsugid(p); } /* * Set saved uid * * XXX always set saved uid even if not _POSIX_SAVED_IDS, as * the security of seteuid() depends on it. B.4.2.2 says it * is important that we should do this. */ if (uid != oldcred->cr_svuid) { change_svuid(newcred, uid); setsugid(p); } } /* * In all permitted cases, we are changing the euid. */ if (uid != oldcred->cr_uid) { change_euid(newcred, uip); setsugid(p); } /* * This also transfers the proc count to the new user. */ proc_set_cred(p, newcred); #ifdef RACCT racct_proc_ucred_changed(p, oldcred, newcred); crhold(newcred); #endif PROC_UNLOCK(p); #ifdef RCTL rctl_proc_ucred_changed(p, newcred); crfree(newcred); #endif uifree(uip); crfree(oldcred); return (0); fail: PROC_UNLOCK(p); uifree(uip); crfree(newcred); return (error); } #ifndef _SYS_SYSPROTO_H_ struct seteuid_args { uid_t euid; }; #endif /* ARGSUSED */ int sys_seteuid(struct thread *td, struct seteuid_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; uid_t euid; struct uidinfo *euip; int error; euid = uap->euid; AUDIT_ARG_EUID(euid); newcred = crget(); euip = uifind(euid); PROC_LOCK(p); /* * Copy credentials so other references do not see our changes. */ oldcred = crcopysafe(p, newcred); #ifdef MAC error = mac_cred_check_seteuid(oldcred, euid); if (error) goto fail; #endif if (euid != oldcred->cr_ruid && /* allow seteuid(getuid()) */ euid != oldcred->cr_svuid && /* allow seteuid(saved uid) */ (error = priv_check_cred(oldcred, PRIV_CRED_SETEUID)) != 0) goto fail; /* * Everything's okay, do it. */ if (oldcred->cr_uid != euid) { change_euid(newcred, euip); setsugid(p); } proc_set_cred(p, newcred); PROC_UNLOCK(p); uifree(euip); crfree(oldcred); return (0); fail: PROC_UNLOCK(p); uifree(euip); crfree(newcred); return (error); } #ifndef _SYS_SYSPROTO_H_ struct setgid_args { gid_t gid; }; #endif /* ARGSUSED */ int sys_setgid(struct thread *td, struct setgid_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; gid_t gid; int error; gid = uap->gid; AUDIT_ARG_GID(gid); newcred = crget(); PROC_LOCK(p); oldcred = crcopysafe(p, newcred); #ifdef MAC error = mac_cred_check_setgid(oldcred, gid); if (error) goto fail; #endif /* * See if we have "permission" by POSIX 1003.1 rules. * * Note that setgid(getegid()) is a special case of * "appropriate privileges" in appendix B.4.2.2. We need * to use this clause to be compatible with traditional BSD * semantics. Basically, it means that "setgid(xx)" sets all * three id's (assuming you have privs). * * For notes on the logic here, see setuid() above. */ if (gid != oldcred->cr_rgid && /* allow setgid(getgid()) */ #ifdef _POSIX_SAVED_IDS gid != oldcred->cr_svgid && /* allow setgid(saved gid) */ #endif #ifdef POSIX_APPENDIX_B_4_2_2 /* Use BSD-compat clause from B.4.2.2 */ gid != oldcred->cr_gid && /* allow setgid(getegid()) */ #endif (error = priv_check_cred(oldcred, PRIV_CRED_SETGID)) != 0) goto fail; #ifdef _POSIX_SAVED_IDS /* * Do we have "appropriate privileges" (are we root or gid == egid) * If so, we are changing the real uid and saved gid. */ if ( #ifdef POSIX_APPENDIX_B_4_2_2 /* use the clause from B.4.2.2 */ gid == oldcred->cr_gid || #endif /* We are using privs. */ priv_check_cred(oldcred, PRIV_CRED_SETGID) == 0) #endif { /* * Set real gid */ if (oldcred->cr_rgid != gid) { change_rgid(newcred, gid); setsugid(p); } /* * Set saved gid * * XXX always set saved gid even if not _POSIX_SAVED_IDS, as * the security of setegid() depends on it. B.4.2.2 says it * is important that we should do this. */ if (oldcred->cr_svgid != gid) { change_svgid(newcred, gid); setsugid(p); } } /* * In all cases permitted cases, we are changing the egid. * Copy credentials so other references do not see our changes. */ if (oldcred->cr_gid != gid) { change_egid(newcred, gid); setsugid(p); } proc_set_cred(p, newcred); PROC_UNLOCK(p); crfree(oldcred); return (0); fail: PROC_UNLOCK(p); crfree(newcred); return (error); } #ifndef _SYS_SYSPROTO_H_ struct setegid_args { gid_t egid; }; #endif /* ARGSUSED */ int sys_setegid(struct thread *td, struct setegid_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; gid_t egid; int error; egid = uap->egid; AUDIT_ARG_EGID(egid); newcred = crget(); PROC_LOCK(p); oldcred = crcopysafe(p, newcred); #ifdef MAC error = mac_cred_check_setegid(oldcred, egid); if (error) goto fail; #endif if (egid != oldcred->cr_rgid && /* allow setegid(getgid()) */ egid != oldcred->cr_svgid && /* allow setegid(saved gid) */ (error = priv_check_cred(oldcred, PRIV_CRED_SETEGID)) != 0) goto fail; if (oldcred->cr_gid != egid) { change_egid(newcred, egid); setsugid(p); } proc_set_cred(p, newcred); PROC_UNLOCK(p); crfree(oldcred); return (0); fail: PROC_UNLOCK(p); crfree(newcred); return (error); } +#ifdef COMPAT_FREEBSD14 +int +freebsd14_setgroups(struct thread *td, struct freebsd14_setgroups_args *uap) +{ + gid_t smallgroups[CRED_SMALLGROUPS_NB]; + gid_t *groups; + int gidsetsize, error; + + /* + * Before FreeBSD 15.0, we allow one more group to be supplied to + * account for the egid appearing before the supplementary groups. This + * may technically allow one more supplementary group for systems that + * did use the default NGROUPS_MAX if we round it back up to 1024. + */ + gidsetsize = uap->gidsetsize; + if (gidsetsize > ngroups_max + 1 || gidsetsize < 0) + return (EINVAL); + + if (gidsetsize > CRED_SMALLGROUPS_NB) + groups = malloc(gidsetsize * sizeof(gid_t), M_TEMP, M_WAITOK); + else + groups = smallgroups; + + error = copyin(uap->gidset, groups, gidsetsize * sizeof(gid_t)); + if (error == 0) { + int ngroups = gidsetsize > 0 ? gidsetsize - 1 /* egid */ : 0; + + error = kern_setgroups(td, &ngroups, groups + 1); + if (error == 0 && gidsetsize > 0) + td->td_proc->p_ucred->cr_gid = groups[0]; + } + + if (groups != smallgroups) + free(groups, M_TEMP); + return (error); +} +#endif /* COMPAT_FREEBSD14 */ + #ifndef _SYS_SYSPROTO_H_ struct setgroups_args { int gidsetsize; gid_t *gidset; }; #endif /* ARGSUSED */ int sys_setgroups(struct thread *td, struct setgroups_args *uap) { gid_t smallgroups[CRED_SMALLGROUPS_NB]; gid_t *groups; int gidsetsize, error; /* * Sanity check size now to avoid passing too big a value to copyin(), * even if kern_setgroups() will do it again. * * Ideally, the 'gidsetsize' argument should have been a 'u_int' (and it * was, in this implementation, for a long time), but POSIX standardized * getgroups() to take an 'int' and it would be quite entrapping to have * setgroups() differ. */ gidsetsize = uap->gidsetsize; - /* XXXKE Limit to ngroups_max when we change the userland interface. */ - if (gidsetsize > ngroups_max + 1 || gidsetsize < 0) + if (gidsetsize > ngroups_max || gidsetsize < 0) return (EINVAL); if (gidsetsize > CRED_SMALLGROUPS_NB) groups = malloc(gidsetsize * sizeof(gid_t), M_TEMP, M_WAITOK); else groups = smallgroups; error = copyin(uap->gidset, groups, gidsetsize * sizeof(gid_t)); if (error == 0) error = kern_setgroups(td, &gidsetsize, groups); if (groups != smallgroups) free(groups, M_TEMP); return (error); } /* * CAUTION: This function normalizes 'groups', possibly also changing the value * of '*ngrpp' as a consequence. */ int kern_setgroups(struct thread *td, int *ngrpp, gid_t *groups) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; int ngrp, error; - gid_t egid; ngrp = *ngrpp; /* Sanity check size. */ - /* XXXKE Limit to ngroups_max when we change the userland interface. */ - if (ngrp < 0 || ngrp > ngroups_max + 1) + if (ngrp < 0 || ngrp > ngroups_max) return (EINVAL); AUDIT_ARG_GROUPSET(groups, ngrp); - /* - * setgroups(0, NULL) is a legitimate way of clearing the groups vector - * on non-BSD systems (which generally do not have the egid in the - * groups[0]). We risk security holes when running non-BSD software if - * we do not do the same. So we allow and treat 0 for 'ngrp' specially - * below (twice). - */ - if (ngrp != 0) { - /* - * To maintain userland compat for now, we use the first group - * as our egid and we'll use the rest as our supplemental - * groups. - */ - egid = groups[0]; - ngrp--; - groups++; - groups_normalize(&ngrp, groups); - *ngrpp = ngrp; - } + groups_normalize(&ngrp, groups); + *ngrpp = ngrp; + newcred = crget(); crextend(newcred, ngrp); PROC_LOCK(p); oldcred = crcopysafe(p, newcred); #ifdef MAC /* * We pass NULL here explicitly if we don't have any supplementary * groups mostly for the sake of normalization, but also to avoid/detect * a situation where a MAC module has some assumption about the layout * of `groups` matching historical behavior. */ error = mac_cred_check_setgroups(oldcred, ngrp, ngrp == 0 ? NULL : groups); if (error) goto fail; #endif error = priv_check_cred(oldcred, PRIV_CRED_SETGROUPS); if (error) goto fail; - /* - * If some groups were passed, the first one is currently the desired - * egid. This code is to be removed (along with some commented block - * above) when setgroups() is changed to take only supplementary groups. - */ - if (ngrp != 0) - newcred->cr_gid = egid; crsetgroups_internal(newcred, ngrp, groups); - setsugid(p); proc_set_cred(p, newcred); PROC_UNLOCK(p); crfree(oldcred); return (0); fail: PROC_UNLOCK(p); crfree(newcred); return (error); } #ifndef _SYS_SYSPROTO_H_ struct setreuid_args { uid_t ruid; uid_t euid; }; #endif /* ARGSUSED */ int sys_setreuid(struct thread *td, struct setreuid_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; uid_t euid, ruid; struct uidinfo *euip, *ruip; int error; euid = uap->euid; ruid = uap->ruid; AUDIT_ARG_EUID(euid); AUDIT_ARG_RUID(ruid); newcred = crget(); euip = uifind(euid); ruip = uifind(ruid); PROC_LOCK(p); oldcred = crcopysafe(p, newcred); #ifdef MAC error = mac_cred_check_setreuid(oldcred, ruid, euid); if (error) goto fail; #endif if (((ruid != (uid_t)-1 && ruid != oldcred->cr_ruid && ruid != oldcred->cr_svuid) || (euid != (uid_t)-1 && euid != oldcred->cr_uid && euid != oldcred->cr_ruid && euid != oldcred->cr_svuid)) && (error = priv_check_cred(oldcred, PRIV_CRED_SETREUID)) != 0) goto fail; if (euid != (uid_t)-1 && oldcred->cr_uid != euid) { change_euid(newcred, euip); setsugid(p); } if (ruid != (uid_t)-1 && oldcred->cr_ruid != ruid) { change_ruid(newcred, ruip); setsugid(p); } if ((ruid != (uid_t)-1 || newcred->cr_uid != newcred->cr_ruid) && newcred->cr_svuid != newcred->cr_uid) { change_svuid(newcred, newcred->cr_uid); setsugid(p); } proc_set_cred(p, newcred); #ifdef RACCT racct_proc_ucred_changed(p, oldcred, newcred); crhold(newcred); #endif PROC_UNLOCK(p); #ifdef RCTL rctl_proc_ucred_changed(p, newcred); crfree(newcred); #endif uifree(ruip); uifree(euip); crfree(oldcred); return (0); fail: PROC_UNLOCK(p); uifree(ruip); uifree(euip); crfree(newcred); return (error); } #ifndef _SYS_SYSPROTO_H_ struct setregid_args { gid_t rgid; gid_t egid; }; #endif /* ARGSUSED */ int sys_setregid(struct thread *td, struct setregid_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; gid_t egid, rgid; int error; egid = uap->egid; rgid = uap->rgid; AUDIT_ARG_EGID(egid); AUDIT_ARG_RGID(rgid); newcred = crget(); PROC_LOCK(p); oldcred = crcopysafe(p, newcred); #ifdef MAC error = mac_cred_check_setregid(oldcred, rgid, egid); if (error) goto fail; #endif if (((rgid != (gid_t)-1 && rgid != oldcred->cr_rgid && rgid != oldcred->cr_svgid) || (egid != (gid_t)-1 && egid != oldcred->cr_gid && egid != oldcred->cr_rgid && egid != oldcred->cr_svgid)) && (error = priv_check_cred(oldcred, PRIV_CRED_SETREGID)) != 0) goto fail; if (egid != (gid_t)-1 && oldcred->cr_gid != egid) { change_egid(newcred, egid); setsugid(p); } if (rgid != (gid_t)-1 && oldcred->cr_rgid != rgid) { change_rgid(newcred, rgid); setsugid(p); } if ((rgid != (gid_t)-1 || newcred->cr_gid != newcred->cr_rgid) && newcred->cr_svgid != newcred->cr_gid) { change_svgid(newcred, newcred->cr_gid); setsugid(p); } proc_set_cred(p, newcred); PROC_UNLOCK(p); crfree(oldcred); return (0); fail: PROC_UNLOCK(p); crfree(newcred); return (error); } /* * setresuid(ruid, euid, suid) is like setreuid except control over the saved * uid is explicit. */ #ifndef _SYS_SYSPROTO_H_ struct setresuid_args { uid_t ruid; uid_t euid; uid_t suid; }; #endif /* ARGSUSED */ int sys_setresuid(struct thread *td, struct setresuid_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; uid_t euid, ruid, suid; struct uidinfo *euip, *ruip; int error; euid = uap->euid; ruid = uap->ruid; suid = uap->suid; AUDIT_ARG_EUID(euid); AUDIT_ARG_RUID(ruid); AUDIT_ARG_SUID(suid); newcred = crget(); euip = uifind(euid); ruip = uifind(ruid); PROC_LOCK(p); oldcred = crcopysafe(p, newcred); #ifdef MAC error = mac_cred_check_setresuid(oldcred, ruid, euid, suid); if (error) goto fail; #endif if (((ruid != (uid_t)-1 && ruid != oldcred->cr_ruid && ruid != oldcred->cr_svuid && ruid != oldcred->cr_uid) || (euid != (uid_t)-1 && euid != oldcred->cr_ruid && euid != oldcred->cr_svuid && euid != oldcred->cr_uid) || (suid != (uid_t)-1 && suid != oldcred->cr_ruid && suid != oldcred->cr_svuid && suid != oldcred->cr_uid)) && (error = priv_check_cred(oldcred, PRIV_CRED_SETRESUID)) != 0) goto fail; if (euid != (uid_t)-1 && oldcred->cr_uid != euid) { change_euid(newcred, euip); setsugid(p); } if (ruid != (uid_t)-1 && oldcred->cr_ruid != ruid) { change_ruid(newcred, ruip); setsugid(p); } if (suid != (uid_t)-1 && oldcred->cr_svuid != suid) { change_svuid(newcred, suid); setsugid(p); } proc_set_cred(p, newcred); #ifdef RACCT racct_proc_ucred_changed(p, oldcred, newcred); crhold(newcred); #endif PROC_UNLOCK(p); #ifdef RCTL rctl_proc_ucred_changed(p, newcred); crfree(newcred); #endif uifree(ruip); uifree(euip); crfree(oldcred); return (0); fail: PROC_UNLOCK(p); uifree(ruip); uifree(euip); crfree(newcred); return (error); } /* * setresgid(rgid, egid, sgid) is like setregid except control over the saved * gid is explicit. */ #ifndef _SYS_SYSPROTO_H_ struct setresgid_args { gid_t rgid; gid_t egid; gid_t sgid; }; #endif /* ARGSUSED */ int sys_setresgid(struct thread *td, struct setresgid_args *uap) { struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; gid_t egid, rgid, sgid; int error; egid = uap->egid; rgid = uap->rgid; sgid = uap->sgid; AUDIT_ARG_EGID(egid); AUDIT_ARG_RGID(rgid); AUDIT_ARG_SGID(sgid); newcred = crget(); PROC_LOCK(p); oldcred = crcopysafe(p, newcred); #ifdef MAC error = mac_cred_check_setresgid(oldcred, rgid, egid, sgid); if (error) goto fail; #endif if (((rgid != (gid_t)-1 && rgid != oldcred->cr_rgid && rgid != oldcred->cr_svgid && rgid != oldcred->cr_gid) || (egid != (gid_t)-1 && egid != oldcred->cr_rgid && egid != oldcred->cr_svgid && egid != oldcred->cr_gid) || (sgid != (gid_t)-1 && sgid != oldcred->cr_rgid && sgid != oldcred->cr_svgid && sgid != oldcred->cr_gid)) && (error = priv_check_cred(oldcred, PRIV_CRED_SETRESGID)) != 0) goto fail; if (egid != (gid_t)-1 && oldcred->cr_gid != egid) { change_egid(newcred, egid); setsugid(p); } if (rgid != (gid_t)-1 && oldcred->cr_rgid != rgid) { change_rgid(newcred, rgid); setsugid(p); } if (sgid != (gid_t)-1 && oldcred->cr_svgid != sgid) { change_svgid(newcred, sgid); setsugid(p); } proc_set_cred(p, newcred); PROC_UNLOCK(p); crfree(oldcred); return (0); fail: PROC_UNLOCK(p); crfree(newcred); return (error); } #ifndef _SYS_SYSPROTO_H_ struct getresuid_args { uid_t *ruid; uid_t *euid; uid_t *suid; }; #endif /* ARGSUSED */ int sys_getresuid(struct thread *td, struct getresuid_args *uap) { struct ucred *cred; int error1 = 0, error2 = 0, error3 = 0; cred = td->td_ucred; if (uap->ruid) error1 = copyout(&cred->cr_ruid, uap->ruid, sizeof(cred->cr_ruid)); if (uap->euid) error2 = copyout(&cred->cr_uid, uap->euid, sizeof(cred->cr_uid)); if (uap->suid) error3 = copyout(&cred->cr_svuid, uap->suid, sizeof(cred->cr_svuid)); return (error1 ? error1 : error2 ? error2 : error3); } #ifndef _SYS_SYSPROTO_H_ struct getresgid_args { gid_t *rgid; gid_t *egid; gid_t *sgid; }; #endif /* ARGSUSED */ int sys_getresgid(struct thread *td, struct getresgid_args *uap) { struct ucred *cred; int error1 = 0, error2 = 0, error3 = 0; cred = td->td_ucred; if (uap->rgid) error1 = copyout(&cred->cr_rgid, uap->rgid, sizeof(cred->cr_rgid)); if (uap->egid) error2 = copyout(&cred->cr_gid, uap->egid, sizeof(cred->cr_gid)); if (uap->sgid) error3 = copyout(&cred->cr_svgid, uap->sgid, sizeof(cred->cr_svgid)); return (error1 ? error1 : error2 ? error2 : error3); } #ifndef _SYS_SYSPROTO_H_ struct issetugid_args { int dummy; }; #endif /* ARGSUSED */ int sys_issetugid(struct thread *td, struct issetugid_args *uap) { struct proc *p = td->td_proc; /* * Note: OpenBSD sets a P_SUGIDEXEC flag set at execve() time, * we use P_SUGID because we consider changing the owners as * "tainting" as well. * This is significant for procs that start as root and "become" * a user without an exec - programs cannot know *everything* * that libc *might* have put in their data segment. */ td->td_retval[0] = (p->p_flag & P_SUGID) ? 1 : 0; return (0); } int sys___setugid(struct thread *td, struct __setugid_args *uap) { #ifdef REGRESSION struct proc *p; p = td->td_proc; switch (uap->flag) { case 0: PROC_LOCK(p); p->p_flag &= ~P_SUGID; PROC_UNLOCK(p); return (0); case 1: PROC_LOCK(p); p->p_flag |= P_SUGID; PROC_UNLOCK(p); return (0); default: return (EINVAL); } #else /* !REGRESSION */ return (ENOSYS); #endif /* REGRESSION */ } #ifdef INVARIANTS static void groups_check_normalized(int ngrp, const gid_t *groups) { gid_t prev_g; groups_check_positive_len(ngrp); groups_check_max_len(ngrp); if (ngrp <= 1) return; prev_g = groups[0]; for (int i = 1; i < ngrp; ++i) { const gid_t g = groups[i]; if (prev_g >= g) panic("%s: groups[%d] (%u) >= groups[%d] (%u)", __func__, i - 1, prev_g, i, g); prev_g = g; } } #else #define groups_check_normalized(...) #endif /* * Returns whether gid designates a supplementary group in cred. */ bool group_is_supplementary(const gid_t gid, const struct ucred *const cred) { groups_check_normalized(cred->cr_ngroups, cred->cr_groups); /* * Perform a binary search of the supplementary groups. This is * possible because we sort the groups in crsetgroups(). */ return (bsearch(&gid, cred->cr_groups, cred->cr_ngroups, sizeof(gid), gidp_cmp) != NULL); } /* * Check if gid is a member of the (effective) group set (i.e., effective and * supplementary groups). */ bool groupmember(gid_t gid, const struct ucred *cred) { groups_check_positive_len(cred->cr_ngroups); if (gid == cred->cr_gid) return (true); return (group_is_supplementary(gid, cred)); } /* * Check if gid is a member of the real group set (i.e., real and supplementary * groups). */ bool realgroupmember(gid_t gid, const struct ucred *cred) { /* * Although the equality test on 'cr_rgid' below doesn't access * 'cr_groups', we check for the latter's length here as we assume that, * if 'cr_ngroups' is 0, the passed 'struct ucred' is invalid, and * 'cr_rgid' may not have been filled. */ groups_check_positive_len(cred->cr_ngroups); if (gid == cred->cr_rgid) return (true); return (group_is_supplementary(gid, cred)); } /* * Test the active securelevel against a given level. securelevel_gt() * implements (securelevel > level). securelevel_ge() implements * (securelevel >= level). Note that the logic is inverted -- these * functions return EPERM on "success" and 0 on "failure". * * Due to care taken when setting the securelevel, we know that no jail will * be less secure that its parent (or the physical system), so it is sufficient * to test the current jail only. * * XXXRW: Possibly since this has to do with privilege, it should move to * kern_priv.c. */ int securelevel_gt(struct ucred *cr, int level) { return (cr->cr_prison->pr_securelevel > level ? EPERM : 0); } int securelevel_ge(struct ucred *cr, int level) { return (cr->cr_prison->pr_securelevel >= level ? EPERM : 0); } /* * 'see_other_uids' determines whether or not visibility of processes * and sockets with credentials holding different real uids is possible * using a variety of system MIBs. * XXX: data declarations should be together near the beginning of the file. */ static int see_other_uids = 1; SYSCTL_INT(_security_bsd, OID_AUTO, see_other_uids, CTLFLAG_RW, &see_other_uids, 0, "Unprivileged processes may see subjects/objects with different real uid"); /*- * Determine if u1 "can see" the subject specified by u2, according to the * 'see_other_uids' policy. * Returns: 0 for permitted, ESRCH otherwise * Locks: none * References: *u1 and *u2 must not change during the call * u1 may equal u2, in which case only one reference is required */ static int cr_canseeotheruids(struct ucred *u1, struct ucred *u2) { if (!see_other_uids && u1->cr_ruid != u2->cr_ruid) { if (priv_check_cred(u1, PRIV_SEEOTHERUIDS) != 0) return (ESRCH); } return (0); } /* * 'see_other_gids' determines whether or not visibility of processes * and sockets with credentials holding different real gids is possible * using a variety of system MIBs. * XXX: data declarations should be together near the beginning of the file. */ static int see_other_gids = 1; SYSCTL_INT(_security_bsd, OID_AUTO, see_other_gids, CTLFLAG_RW, &see_other_gids, 0, "Unprivileged processes may see subjects/objects with different real gid"); /* * Determine if u1 can "see" the subject specified by u2, according to the * 'see_other_gids' policy. * Returns: 0 for permitted, ESRCH otherwise * Locks: none * References: *u1 and *u2 must not change during the call * u1 may equal u2, in which case only one reference is required */ static int cr_canseeothergids(struct ucred *u1, struct ucred *u2) { if (!see_other_gids) { if (realgroupmember(u1->cr_rgid, u2)) return (0); for (int i = 1; i < u1->cr_ngroups; i++) if (realgroupmember(u1->cr_groups[i], u2)) return (0); if (priv_check_cred(u1, PRIV_SEEOTHERGIDS) != 0) return (ESRCH); } return (0); } /* * 'see_jail_proc' determines whether or not visibility of processes and * sockets with credentials holding different jail ids is possible using a * variety of system MIBs. * * XXX: data declarations should be together near the beginning of the file. */ static int see_jail_proc = 1; SYSCTL_INT(_security_bsd, OID_AUTO, see_jail_proc, CTLFLAG_RW, &see_jail_proc, 0, "Unprivileged processes may see subjects/objects with different jail ids"); /*- * Determine if u1 "can see" the subject specified by u2, according to the * 'see_jail_proc' policy. * Returns: 0 for permitted, ESRCH otherwise * Locks: none * References: *u1 and *u2 must not change during the call * u1 may equal u2, in which case only one reference is required */ static int cr_canseejailproc(struct ucred *u1, struct ucred *u2) { if (see_jail_proc || /* Policy deactivated. */ u1->cr_prison == u2->cr_prison || /* Same jail. */ priv_check_cred(u1, PRIV_SEEJAILPROC) == 0) /* Privileged. */ return (0); return (ESRCH); } /* * Determine if u1 can tamper with the subject specified by u2, if they are in * different jails and 'unprivileged_parent_tampering' jail policy allows it. * * May be called if u1 and u2 are in the same jail, but it is expected that the * caller has already done a prison_check() prior to calling it. * * Returns: 0 for permitted, EPERM otherwise */ static int cr_can_tamper_with_subjail(struct ucred *u1, struct ucred *u2, int priv) { MPASS(prison_check(u1, u2) == 0); if (u1->cr_prison == u2->cr_prison) return (0); if (priv_check_cred(u1, priv) == 0) return (0); /* * Jails do not maintain a distinct UID space, so process visibility is * all that would control an unprivileged process' ability to tamper * with a process in a subjail by default if we did not have the * allow.unprivileged_parent_tampering knob to restrict it by default. */ if (prison_allow(u2, PR_ALLOW_UNPRIV_PARENT_TAMPER)) return (0); return (EPERM); } /* * Helper for cr_cansee*() functions to abide by system-wide security.bsd.see_* * policies. Determines if u1 "can see" u2 according to these policies. * Returns: 0 for permitted, ESRCH otherwise */ int cr_bsd_visible(struct ucred *u1, struct ucred *u2) { int error; error = cr_canseeotheruids(u1, u2); if (error != 0) return (error); error = cr_canseeothergids(u1, u2); if (error != 0) return (error); error = cr_canseejailproc(u1, u2); if (error != 0) return (error); return (0); } /*- * Determine if u1 "can see" the subject specified by u2. * Returns: 0 for permitted, an errno value otherwise * Locks: none * References: *u1 and *u2 must not change during the call * u1 may equal u2, in which case only one reference is required */ int cr_cansee(struct ucred *u1, struct ucred *u2) { int error; if ((error = prison_check(u1, u2))) return (error); #ifdef MAC if ((error = mac_cred_check_visible(u1, u2))) return (error); #endif if ((error = cr_bsd_visible(u1, u2))) return (error); return (0); } /*- * Determine if td "can see" the subject specified by p. * Returns: 0 for permitted, an errno value otherwise * Locks: Sufficient locks to protect p->p_ucred must be held. td really * should be curthread. * References: td and p must be valid for the lifetime of the call */ int p_cansee(struct thread *td, struct proc *p) { /* Wrap cr_cansee() for all functionality. */ KASSERT(td == curthread, ("%s: td not curthread", __func__)); PROC_LOCK_ASSERT(p, MA_OWNED); if (td->td_proc == p) return (0); return (cr_cansee(td->td_ucred, p->p_ucred)); } /* * 'conservative_signals' prevents the delivery of a broad class of * signals by unprivileged processes to processes that have changed their * credentials since the last invocation of execve(). This can prevent * the leakage of cached information or retained privileges as a result * of a common class of signal-related vulnerabilities. However, this * may interfere with some applications that expect to be able to * deliver these signals to peer processes after having given up * privilege. */ static int conservative_signals = 1; SYSCTL_INT(_security_bsd, OID_AUTO, conservative_signals, CTLFLAG_RW, &conservative_signals, 0, "Unprivileged processes prevented from " "sending certain signals to processes whose credentials have changed"); /*- * Determine whether cred may deliver the specified signal to proc. * Returns: 0 for permitted, an errno value otherwise. * Locks: A lock must be held for proc. * References: cred and proc must be valid for the lifetime of the call. */ int cr_cansignal(struct ucred *cred, struct proc *proc, int signum) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); /* * Jail semantics limit the scope of signalling to proc in the * same jail as cred, if cred is in jail. */ error = prison_check(cred, proc->p_ucred); if (error) return (error); #ifdef MAC if ((error = mac_proc_check_signal(cred, proc, signum))) return (error); #endif if ((error = cr_bsd_visible(cred, proc->p_ucred))) return (error); /* * UNIX signal semantics depend on the status of the P_SUGID * bit on the target process. If the bit is set, then additional * restrictions are placed on the set of available signals. */ if (conservative_signals && (proc->p_flag & P_SUGID)) { switch (signum) { case 0: case SIGKILL: case SIGINT: case SIGTERM: case SIGALRM: case SIGSTOP: case SIGTTIN: case SIGTTOU: case SIGTSTP: case SIGHUP: case SIGUSR1: case SIGUSR2: /* * Generally, permit job and terminal control * signals. */ break; default: /* Not permitted without privilege. */ error = priv_check_cred(cred, PRIV_SIGNAL_SUGID); if (error) return (error); } } /* * Generally, the target credential's ruid or svuid must match the * subject credential's ruid or euid. */ if (cred->cr_ruid != proc->p_ucred->cr_ruid && cred->cr_ruid != proc->p_ucred->cr_svuid && cred->cr_uid != proc->p_ucred->cr_ruid && cred->cr_uid != proc->p_ucred->cr_svuid) { error = priv_check_cred(cred, PRIV_SIGNAL_DIFFCRED); if (error) return (error); } /* * At this point, the target may be in a different jail than the * subject -- the subject must be in a parent jail to the target, * whether it is prison0 or a subordinate of prison0 that has * children. Additional privileges are required to allow this, as * whether the creds are truly equivalent or not must be determined on * a case-by-case basis. */ error = cr_can_tamper_with_subjail(cred, proc->p_ucred, PRIV_SIGNAL_DIFFJAIL); if (error) return (error); return (0); } /*- * Determine whether td may deliver the specified signal to p. * Returns: 0 for permitted, an errno value otherwise * Locks: Sufficient locks to protect various components of td and p * must be held. td must be curthread, and a lock must be * held for p. * References: td and p must be valid for the lifetime of the call */ int p_cansignal(struct thread *td, struct proc *p, int signum) { KASSERT(td == curthread, ("%s: td not curthread", __func__)); PROC_LOCK_ASSERT(p, MA_OWNED); if (td->td_proc == p) return (0); /* * UNIX signalling semantics require that processes in the same * session always be able to deliver SIGCONT to one another, * overriding the remaining protections. */ /* XXX: This will require an additional lock of some sort. */ if (signum == SIGCONT && td->td_proc->p_session == p->p_session) return (0); /* * Some compat layers use SIGTHR and higher signals for * communication between different kernel threads of the same * process, so that they expect that it's always possible to * deliver them, even for suid applications where cr_cansignal() can * deny such ability for security consideration. It should be * pretty safe to do since the only way to create two processes * with the same p_leader is via rfork(2). */ if (td->td_proc->p_leader != NULL && signum >= SIGTHR && signum < SIGTHR + 4 && td->td_proc->p_leader == p->p_leader) return (0); return (cr_cansignal(td->td_ucred, p, signum)); } /*- * Determine whether td may reschedule p. * Returns: 0 for permitted, an errno value otherwise * Locks: Sufficient locks to protect various components of td and p * must be held. td must be curthread, and a lock must * be held for p. * References: td and p must be valid for the lifetime of the call */ int p_cansched(struct thread *td, struct proc *p) { int error; KASSERT(td == curthread, ("%s: td not curthread", __func__)); PROC_LOCK_ASSERT(p, MA_OWNED); if (td->td_proc == p) return (0); if ((error = prison_check(td->td_ucred, p->p_ucred))) return (error); #ifdef MAC if ((error = mac_proc_check_sched(td->td_ucred, p))) return (error); #endif if ((error = cr_bsd_visible(td->td_ucred, p->p_ucred))) return (error); if (td->td_ucred->cr_ruid != p->p_ucred->cr_ruid && td->td_ucred->cr_uid != p->p_ucred->cr_ruid) { error = priv_check(td, PRIV_SCHED_DIFFCRED); if (error) return (error); } error = cr_can_tamper_with_subjail(td->td_ucred, p->p_ucred, PRIV_SCHED_DIFFJAIL); if (error) return (error); return (0); } /* * Handle getting or setting the prison's unprivileged_proc_debug * value. */ static int sysctl_unprivileged_proc_debug(SYSCTL_HANDLER_ARGS) { int error, val; val = prison_allow(req->td->td_ucred, PR_ALLOW_UNPRIV_DEBUG); error = sysctl_handle_int(oidp, &val, 0, req); if (error != 0 || req->newptr == NULL) return (error); if (val != 0 && val != 1) return (EINVAL); prison_set_allow(req->td->td_ucred, PR_ALLOW_UNPRIV_DEBUG, val); return (0); } /* * The 'unprivileged_proc_debug' flag may be used to disable a variety of * unprivileged inter-process debugging services, including some procfs * functionality, ptrace(), and ktrace(). In the past, inter-process * debugging has been involved in a variety of security problems, and sites * not requiring the service might choose to disable it when hardening * systems. */ SYSCTL_PROC(_security_bsd, OID_AUTO, unprivileged_proc_debug, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_SECURE | CTLFLAG_MPSAFE, 0, 0, sysctl_unprivileged_proc_debug, "I", "Unprivileged processes may use process debugging facilities"); /* * Return true if the object owner/group ids are subset of the active * credentials. */ bool cr_xids_subset(struct ucred *active_cred, struct ucred *obj_cred) { int i; bool grpsubset, uidsubset; /* * Is p's group set a subset of td's effective group set? This * includes p's egid, group access list, rgid, and svgid. */ grpsubset = true; for (i = 0; i < obj_cred->cr_ngroups; i++) { if (!groupmember(obj_cred->cr_groups[i], active_cred)) { grpsubset = false; break; } } grpsubset = grpsubset && groupmember(obj_cred->cr_rgid, active_cred) && groupmember(obj_cred->cr_svgid, active_cred); /* * Are the uids present in obj_cred's credential equal to * active_cred's effective uid? This includes obj_cred's * euid, svuid, and ruid. */ uidsubset = (active_cred->cr_uid == obj_cred->cr_uid && active_cred->cr_uid == obj_cred->cr_svuid && active_cred->cr_uid == obj_cred->cr_ruid); return (uidsubset && grpsubset); } /*- * Determine whether td may debug p. * Returns: 0 for permitted, an errno value otherwise * Locks: Sufficient locks to protect various components of td and p * must be held. td must be curthread, and a lock must * be held for p. * References: td and p must be valid for the lifetime of the call */ int p_candebug(struct thread *td, struct proc *p) { int error; KASSERT(td == curthread, ("%s: td not curthread", __func__)); PROC_LOCK_ASSERT(p, MA_OWNED); if (td->td_proc == p) return (0); if ((error = priv_check(td, PRIV_DEBUG_UNPRIV))) return (error); if ((error = prison_check(td->td_ucred, p->p_ucred))) return (error); #ifdef MAC if ((error = mac_proc_check_debug(td->td_ucred, p))) return (error); #endif if ((error = cr_bsd_visible(td->td_ucred, p->p_ucred))) return (error); /* * If p's gids aren't a subset, or the uids aren't a subset, * or the credential has changed, require appropriate privilege * for td to debug p. */ if (!cr_xids_subset(td->td_ucred, p->p_ucred)) { error = priv_check(td, PRIV_DEBUG_DIFFCRED); if (error) return (error); } /* * Has the credential of the process changed since the last exec()? */ if ((p->p_flag & P_SUGID) != 0) { error = priv_check(td, PRIV_DEBUG_SUGID); if (error) return (error); } error = cr_can_tamper_with_subjail(td->td_ucred, p->p_ucred, PRIV_DEBUG_DIFFJAIL); if (error) return (error); /* Can't trace init when securelevel > 0. */ if (p == initproc) { error = securelevel_gt(td->td_ucred, 0); if (error) return (error); } /* * Can't trace a process that's currently exec'ing. * * XXX: Note, this is not a security policy decision, it's a * basic correctness/functionality decision. Therefore, this check * should be moved to the caller's of p_candebug(). */ if ((p->p_flag & P_INEXEC) != 0) return (EBUSY); /* Denied explicitly */ if ((p->p_flag2 & P2_NOTRACE) != 0) { error = priv_check(td, PRIV_DEBUG_DENIED); if (error != 0) return (error); } return (0); } /*- * Determine whether the subject represented by cred can "see" a socket. * Returns: 0 for permitted, ENOENT otherwise. */ int cr_canseesocket(struct ucred *cred, struct socket *so) { int error; error = prison_check(cred, so->so_cred); if (error) return (ENOENT); #ifdef MAC error = mac_socket_check_visible(cred, so); if (error) return (error); #endif if (cr_bsd_visible(cred, so->so_cred)) return (ENOENT); return (0); } /*- * Determine whether td can wait for the exit of p. * Returns: 0 for permitted, an errno value otherwise * Locks: Sufficient locks to protect various components of td and p * must be held. td must be curthread, and a lock must * be held for p. * References: td and p must be valid for the lifetime of the call */ int p_canwait(struct thread *td, struct proc *p) { int error; KASSERT(td == curthread, ("%s: td not curthread", __func__)); PROC_LOCK_ASSERT(p, MA_OWNED); if ((error = prison_check(td->td_ucred, p->p_ucred))) return (error); #ifdef MAC if ((error = mac_proc_check_wait(td->td_ucred, p))) return (error); #endif #if 0 /* XXXMAC: This could have odd effects on some shells. */ if ((error = cr_bsd_visible(td->td_ucred, p->p_ucred))) return (error); #endif return (0); } /* * Credential management. * * struct ucred objects are rarely allocated but gain and lose references all * the time (e.g., on struct file alloc/dealloc) turning refcount updates into * a significant source of cache-line ping ponging. Common cases are worked * around by modifying thread-local counter instead if the cred to operate on * matches td_realucred. * * The counter is split into 2 parts: * - cr_users -- total count of all struct proc and struct thread objects * which have given cred in p_ucred and td_ucred respectively * - cr_ref -- the actual ref count, only valid if cr_users == 0 * * If users == 0 then cr_ref behaves similarly to refcount(9), in particular if * the count reaches 0 the object is freeable. * If users > 0 and curthread->td_realucred == cred, then updates are performed * against td_ucredref. * In other cases updates are performed against cr_ref. * * Changing td_realucred into something else decrements cr_users and transfers * accumulated updates. */ struct ucred * crcowget(struct ucred *cr) { mtx_lock(&cr->cr_mtx); KASSERT(cr->cr_users > 0, ("%s: users %d not > 0 on cred %p", __func__, cr->cr_users, cr)); cr->cr_users++; cr->cr_ref++; mtx_unlock(&cr->cr_mtx); return (cr); } static struct ucred * crunuse(struct thread *td) { struct ucred *cr, *crold; MPASS(td->td_realucred == td->td_ucred); cr = td->td_realucred; mtx_lock(&cr->cr_mtx); cr->cr_ref += td->td_ucredref; td->td_ucredref = 0; KASSERT(cr->cr_users > 0, ("%s: users %d not > 0 on cred %p", __func__, cr->cr_users, cr)); cr->cr_users--; if (cr->cr_users == 0) { KASSERT(cr->cr_ref > 0, ("%s: ref %ld not > 0 on cred %p", __func__, cr->cr_ref, cr)); crold = cr; } else { cr->cr_ref--; crold = NULL; } mtx_unlock(&cr->cr_mtx); td->td_realucred = NULL; return (crold); } static void crunusebatch(struct ucred *cr, u_int users, long ref) { KASSERT(users > 0, ("%s: passed users %d not > 0 ; cred %p", __func__, users, cr)); mtx_lock(&cr->cr_mtx); KASSERT(cr->cr_users >= users, ("%s: users %d not > %d on cred %p", __func__, cr->cr_users, users, cr)); cr->cr_users -= users; cr->cr_ref += ref; cr->cr_ref -= users; if (cr->cr_users > 0) { mtx_unlock(&cr->cr_mtx); return; } KASSERT(cr->cr_ref >= 0, ("%s: ref %ld not >= 0 on cred %p", __func__, cr->cr_ref, cr)); if (cr->cr_ref > 0) { mtx_unlock(&cr->cr_mtx); return; } crfree_final(cr); } void crcowfree(struct thread *td) { struct ucred *cr; cr = crunuse(td); if (cr != NULL) crfree(cr); } struct ucred * crcowsync(void) { struct thread *td; struct proc *p; struct ucred *crnew, *crold; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); MPASS(td->td_realucred == td->td_ucred); if (td->td_realucred == p->p_ucred) return (NULL); crnew = crcowget(p->p_ucred); crold = crunuse(td); td->td_realucred = crnew; td->td_ucred = td->td_realucred; return (crold); } /* * Batching. */ void credbatch_add(struct credbatch *crb, struct thread *td) { struct ucred *cr; MPASS(td->td_realucred != NULL); MPASS(td->td_realucred == td->td_ucred); MPASS(TD_GET_STATE(td) == TDS_INACTIVE); cr = td->td_realucred; KASSERT(cr->cr_users > 0, ("%s: users %d not > 0 on cred %p", __func__, cr->cr_users, cr)); if (crb->cred != cr) { if (crb->users > 0) { MPASS(crb->cred != NULL); crunusebatch(crb->cred, crb->users, crb->ref); crb->users = 0; crb->ref = 0; } } crb->cred = cr; crb->users++; crb->ref += td->td_ucredref; td->td_ucredref = 0; td->td_realucred = NULL; } void credbatch_final(struct credbatch *crb) { MPASS(crb->cred != NULL); MPASS(crb->users > 0); crunusebatch(crb->cred, crb->users, crb->ref); } /* * Allocate a zeroed cred structure. */ struct ucred * crget(void) { struct ucred *cr; cr = malloc(sizeof(*cr), M_CRED, M_WAITOK | M_ZERO); mtx_init(&cr->cr_mtx, "cred", NULL, MTX_DEF); cr->cr_ref = 1; #ifdef AUDIT audit_cred_init(cr); #endif #ifdef MAC mac_cred_init(cr); #endif cr->cr_groups = cr->cr_smallgroups; cr->cr_agroups = nitems(cr->cr_smallgroups); return (cr); } /* * Claim another reference to a ucred structure. */ struct ucred * crhold(struct ucred *cr) { struct thread *td; td = curthread; if (__predict_true(td->td_realucred == cr)) { KASSERT(cr->cr_users > 0, ("%s: users %d not > 0 on cred %p", __func__, cr->cr_users, cr)); td->td_ucredref++; return (cr); } mtx_lock(&cr->cr_mtx); cr->cr_ref++; mtx_unlock(&cr->cr_mtx); return (cr); } /* * Free a cred structure. Throws away space when ref count gets to 0. */ void crfree(struct ucred *cr) { struct thread *td; td = curthread; if (__predict_true(td->td_realucred == cr)) { KASSERT(cr->cr_users > 0, ("%s: users %d not > 0 on cred %p", __func__, cr->cr_users, cr)); td->td_ucredref--; return; } mtx_lock(&cr->cr_mtx); KASSERT(cr->cr_users >= 0, ("%s: users %d not >= 0 on cred %p", __func__, cr->cr_users, cr)); cr->cr_ref--; if (cr->cr_users > 0) { mtx_unlock(&cr->cr_mtx); return; } KASSERT(cr->cr_ref >= 0, ("%s: ref %ld not >= 0 on cred %p", __func__, cr->cr_ref, cr)); if (cr->cr_ref > 0) { mtx_unlock(&cr->cr_mtx); return; } crfree_final(cr); } static void crfree_final(struct ucred *cr) { KASSERT(cr->cr_users == 0, ("%s: users %d not == 0 on cred %p", __func__, cr->cr_users, cr)); KASSERT(cr->cr_ref == 0, ("%s: ref %ld not == 0 on cred %p", __func__, cr->cr_ref, cr)); /* * Some callers of crget(), such as nfs_statfs(), allocate a temporary * credential, but don't allocate a uidinfo structure. */ if (cr->cr_uidinfo != NULL) uifree(cr->cr_uidinfo); if (cr->cr_ruidinfo != NULL) uifree(cr->cr_ruidinfo); if (cr->cr_prison != NULL) prison_free(cr->cr_prison); if (cr->cr_loginclass != NULL) loginclass_free(cr->cr_loginclass); #ifdef AUDIT audit_cred_destroy(cr); #endif #ifdef MAC mac_cred_destroy(cr); #endif mtx_destroy(&cr->cr_mtx); if (cr->cr_groups != cr->cr_smallgroups) free(cr->cr_groups, M_CRED); free(cr, M_CRED); } /* * Copy a ucred's contents from a template. Does not block. */ void crcopy(struct ucred *dest, struct ucred *src) { bcopy(&src->cr_startcopy, &dest->cr_startcopy, (unsigned)((caddr_t)&src->cr_endcopy - (caddr_t)&src->cr_startcopy)); dest->cr_flags = src->cr_flags; crsetgroups(dest, src->cr_ngroups, src->cr_groups); uihold(dest->cr_uidinfo); uihold(dest->cr_ruidinfo); prison_hold(dest->cr_prison); loginclass_hold(dest->cr_loginclass); #ifdef AUDIT audit_cred_copy(src, dest); #endif #ifdef MAC mac_cred_copy(src, dest); #endif } /* * Dup cred struct to a new held one. */ struct ucred * crdup(struct ucred *cr) { struct ucred *newcr; newcr = crget(); crcopy(newcr, cr); return (newcr); } /* * Fill in a struct xucred based on a struct ucred. */ void cru2x(struct ucred *cr, struct xucred *xcr) { int ngroups; bzero(xcr, sizeof(*xcr)); xcr->cr_version = XUCRED_VERSION; xcr->cr_uid = cr->cr_uid; xcr->cr_gid = cr->cr_gid; /* * We use a union to alias cr_gid to cr_groups[0] in the xucred, so * this is kind of ugly; cr_ngroups still includes the egid for our * purposes to avoid bumping the xucred version. */ ngroups = MIN(cr->cr_ngroups + 1, nitems(xcr->cr_groups)); xcr->cr_ngroups = ngroups; bcopy(cr->cr_groups, xcr->cr_sgroups, (ngroups - 1) * sizeof(*cr->cr_groups)); } void cru2xt(struct thread *td, struct xucred *xcr) { cru2x(td->td_ucred, xcr); xcr->cr_pid = td->td_proc->p_pid; } /* * Change process credentials. * * Callers are responsible for providing the reference for passed credentials * and for freeing old ones. Calls chgproccnt() to correctly account the * current process to the proper real UID, if the latter has changed. Returns * whether the operation was successful. Failure can happen only on * 'enforce_proc_lim' being true and if no new process can be accounted to the * new real UID because of the current limit (see the inner comment for more * details) and the caller does not have privilege (PRIV_PROC_LIMIT) to override * that. */ static bool _proc_set_cred(struct proc *p, struct ucred *newcred, bool enforce_proc_lim) { struct ucred *const oldcred = p->p_ucred; MPASS(oldcred != NULL); PROC_LOCK_ASSERT(p, MA_OWNED); KASSERT(newcred->cr_users == 0, ("%s: users %d not 0 on cred %p", __func__, newcred->cr_users, newcred)); KASSERT(newcred->cr_ref == 1, ("%s: ref %ld not 1 on cred %p", __func__, newcred->cr_ref, newcred)); if (newcred->cr_ruidinfo != oldcred->cr_ruidinfo) { /* * XXXOC: This check is flawed but nonetheless the best we can * currently do as we don't really track limits per UID contrary * to what we pretend in setrlimit(2). Until this is reworked, * we just check here that the number of processes for our new * real UID doesn't exceed this process' process number limit * (which is meant to be associated with the current real UID). */ const int proccnt_changed = chgproccnt(newcred->cr_ruidinfo, 1, enforce_proc_lim ? lim_cur_proc(p, RLIMIT_NPROC) : 0); if (!proccnt_changed) { if (priv_check_cred(oldcred, PRIV_PROC_LIMIT) != 0) return (false); (void)chgproccnt(newcred->cr_ruidinfo, 1, 0); } } mtx_lock(&oldcred->cr_mtx); KASSERT(oldcred->cr_users > 0, ("%s: users %d not > 0 on cred %p", __func__, oldcred->cr_users, oldcred)); oldcred->cr_users--; mtx_unlock(&oldcred->cr_mtx); p->p_ucred = newcred; newcred->cr_users = 1; PROC_UPDATE_COW(p); if (newcred->cr_ruidinfo != oldcred->cr_ruidinfo) (void)chgproccnt(oldcred->cr_ruidinfo, -1, 0); return (true); } void proc_set_cred(struct proc *p, struct ucred *newcred) { bool success __diagused = _proc_set_cred(p, newcred, false); MPASS(success); } bool proc_set_cred_enforce_proc_lim(struct proc *p, struct ucred *newcred) { return (_proc_set_cred(p, newcred, true)); } void proc_unset_cred(struct proc *p, bool decrement_proc_count) { struct ucred *cr; MPASS(p->p_state == PRS_ZOMBIE || p->p_state == PRS_NEW); cr = p->p_ucred; p->p_ucred = NULL; KASSERT(cr->cr_users > 0, ("%s: users %d not > 0 on cred %p", __func__, cr->cr_users, cr)); mtx_lock(&cr->cr_mtx); cr->cr_users--; if (cr->cr_users == 0) KASSERT(cr->cr_ref > 0, ("%s: ref %ld not > 0 on cred %p", __func__, cr->cr_ref, cr)); mtx_unlock(&cr->cr_mtx); if (decrement_proc_count) (void)chgproccnt(cr->cr_ruidinfo, -1, 0); crfree(cr); } struct ucred * crcopysafe(struct proc *p, struct ucred *cr) { struct ucred *oldcred; int groups; PROC_LOCK_ASSERT(p, MA_OWNED); oldcred = p->p_ucred; while (cr->cr_agroups < oldcred->cr_ngroups) { groups = oldcred->cr_ngroups; PROC_UNLOCK(p); crextend(cr, groups); PROC_LOCK(p); oldcred = p->p_ucred; } crcopy(cr, oldcred); return (oldcred); } /* * Extend the passed-in credentials to hold n groups. * * Must not be called after groups have been set. */ void crextend(struct ucred *cr, int n) { size_t nbytes; MPASS2(cr->cr_ref == 1, "'cr_ref' must be 1 (referenced, unshared)"); MPASS2((cr->cr_flags & CRED_FLAG_GROUPSET) == 0, "groups on 'cr' already set!"); groups_check_positive_len(n); groups_check_max_len(n); if (n <= cr->cr_agroups) return; nbytes = n * sizeof(gid_t); if (nbytes < n) panic("Too many groups (memory size overflow)! " "Computation of 'kern.ngroups' should have prevented this, " "please fix it. In the meantime, reduce 'kern.ngroups'."); /* * We allocate a power of 2 larger than 'nbytes', except when that * exceeds PAGE_SIZE, in which case we allocate the right multiple of * pages. We assume PAGE_SIZE is a power of 2 (the call to roundup2() * below) but do not need to for sizeof(gid_t). */ if (nbytes < PAGE_SIZE) { if (!powerof2(nbytes)) /* fls*() return a bit index starting at 1. */ nbytes = 1 << flsl(nbytes); } else nbytes = roundup2(nbytes, PAGE_SIZE); /* Free the old array. */ if (cr->cr_groups != cr->cr_smallgroups) free(cr->cr_groups, M_CRED); cr->cr_groups = malloc(nbytes, M_CRED, M_WAITOK | M_ZERO); cr->cr_agroups = nbytes / sizeof(gid_t); } /* * Normalizes a set of groups to be applied to a 'struct ucred'. * * Normalization ensures that the supplementary groups are sorted in ascending * order and do not contain duplicates. */ static void groups_normalize(int *ngrp, gid_t *groups) { gid_t prev_g; int ins_idx; groups_check_positive_len(*ngrp); groups_check_max_len(*ngrp); if (*ngrp <= 1) return; qsort(groups, *ngrp, sizeof(*groups), gidp_cmp); /* Remove duplicates. */ prev_g = groups[0]; ins_idx = 1; for (int i = ins_idx; i < *ngrp; ++i) { const gid_t g = groups[i]; if (g != prev_g) { if (i != ins_idx) groups[ins_idx] = g; ++ins_idx; prev_g = g; } } *ngrp = ins_idx; groups_check_normalized(*ngrp, groups); } /* * Internal function copying groups into a credential. * * 'ngrp' must be strictly positive. Either the passed 'groups' array must have * been normalized in advance (see groups_normalize()), else it must be so * before the structure is to be used again. * * This function is suitable to be used under any lock (it doesn't take any lock * itself nor sleep, and in particular doesn't allocate memory). crextend() * must have been called beforehand to ensure sufficient space is available. * See also crsetgroups(), which handles that. */ static void crsetgroups_internal(struct ucred *cr, int ngrp, const gid_t *groups) { MPASS2(cr->cr_ref == 1, "'cr_ref' must be 1 (referenced, unshared)"); MPASS2(cr->cr_agroups >= ngrp, "'cr_agroups' too small"); groups_check_positive_len(ngrp); bcopy(groups, cr->cr_groups, ngrp * sizeof(gid_t)); cr->cr_ngroups = ngrp; cr->cr_flags |= CRED_FLAG_GROUPSET; } /* * Copy groups in to a credential after expanding it if required. * * May sleep in order to allocate memory (except if, e.g., crextend() was called * before with 'ngrp' or greater). Truncates the list to ngroups_max if * it is too large. Array 'groups' doesn't need to be sorted. 'ngrp' must be * strictly positive. */ void crsetgroups(struct ucred *cr, int ngrp, const gid_t *groups) { if (ngrp > ngroups_max) ngrp = ngroups_max; cr->cr_ngroups = 0; if (ngrp == 0) { cr->cr_flags |= CRED_FLAG_GROUPSET; return; } /* * crextend() asserts that groups are not set, as it may allocate a new * backing storage without copying the content of the old one. Since we * are going to install a completely new set anyway, signal that we * consider the old ones thrown away. */ cr->cr_flags &= ~CRED_FLAG_GROUPSET; crextend(cr, ngrp); crsetgroups_internal(cr, ngrp, groups); groups_normalize(&cr->cr_ngroups, cr->cr_groups); } /* * Same as crsetgroups() but sets the effective GID as well. * * This function ensures that an effective GID is always present in credentials. * An empty array will only set the effective GID to the default_egid, while a * non-empty array will peel off groups[0] to set as the effective GID and use * the remainder, if any, as supplementary groups. */ void crsetgroups_and_egid(struct ucred *cr, int ngrp, const gid_t *groups, const gid_t default_egid) { if (ngrp == 0) { cr->cr_gid = default_egid; cr->cr_ngroups = 0; cr->cr_flags |= CRED_FLAG_GROUPSET; return; } crsetgroups(cr, ngrp - 1, groups + 1); cr->cr_gid = groups[0]; } /* * Get login name, if available. */ #ifndef _SYS_SYSPROTO_H_ struct getlogin_args { char *namebuf; u_int namelen; }; #endif /* ARGSUSED */ int sys_getlogin(struct thread *td, struct getlogin_args *uap) { char login[MAXLOGNAME]; struct proc *p = td->td_proc; size_t len; if (uap->namelen > MAXLOGNAME) uap->namelen = MAXLOGNAME; PROC_LOCK(p); SESS_LOCK(p->p_session); len = strlcpy(login, p->p_session->s_login, uap->namelen) + 1; SESS_UNLOCK(p->p_session); PROC_UNLOCK(p); if (len > uap->namelen) return (ERANGE); return (copyout(login, uap->namebuf, len)); } /* * Set login name. */ #ifndef _SYS_SYSPROTO_H_ struct setlogin_args { char *namebuf; }; #endif /* ARGSUSED */ int sys_setlogin(struct thread *td, struct setlogin_args *uap) { struct proc *p = td->td_proc; int error; char logintmp[MAXLOGNAME]; CTASSERT(sizeof(p->p_session->s_login) >= sizeof(logintmp)); error = priv_check(td, PRIV_PROC_SETLOGIN); if (error) return (error); error = copyinstr(uap->namebuf, logintmp, sizeof(logintmp), NULL); if (error != 0) { if (error == ENAMETOOLONG) error = EINVAL; return (error); } AUDIT_ARG_LOGIN(logintmp); PROC_LOCK(p); SESS_LOCK(p->p_session); strcpy(p->p_session->s_login, logintmp); SESS_UNLOCK(p->p_session); PROC_UNLOCK(p); return (0); } void setsugid(struct proc *p) { PROC_LOCK_ASSERT(p, MA_OWNED); p->p_flag |= P_SUGID; } /*- * Change a process's effective uid. * Side effects: newcred->cr_uid and newcred->cr_uidinfo will be modified. * References: newcred must be an exclusive credential reference for the * duration of the call. */ void change_euid(struct ucred *newcred, struct uidinfo *euip) { newcred->cr_uid = euip->ui_uid; uihold(euip); uifree(newcred->cr_uidinfo); newcred->cr_uidinfo = euip; } /*- * Change a process's effective gid. * Side effects: newcred->cr_gid will be modified. * References: newcred must be an exclusive credential reference for the * duration of the call. */ void change_egid(struct ucred *newcred, gid_t egid) { newcred->cr_gid = egid; } /*- * Change a process's real uid. * Side effects: newcred->cr_ruid will be updated, newcred->cr_ruidinfo * will be updated. * References: newcred must be an exclusive credential reference for the * duration of the call. */ void change_ruid(struct ucred *newcred, struct uidinfo *ruip) { newcred->cr_ruid = ruip->ui_uid; uihold(ruip); uifree(newcred->cr_ruidinfo); newcred->cr_ruidinfo = ruip; } /*- * Change a process's real gid. * Side effects: newcred->cr_rgid will be updated. * References: newcred must be an exclusive credential reference for the * duration of the call. */ void change_rgid(struct ucred *newcred, gid_t rgid) { newcred->cr_rgid = rgid; } /*- * Change a process's saved uid. * Side effects: newcred->cr_svuid will be updated. * References: newcred must be an exclusive credential reference for the * duration of the call. */ void change_svuid(struct ucred *newcred, uid_t svuid) { newcred->cr_svuid = svuid; } /*- * Change a process's saved gid. * Side effects: newcred->cr_svgid will be updated. * References: newcred must be an exclusive credential reference for the * duration of the call. */ void change_svgid(struct ucred *newcred, gid_t svgid) { newcred->cr_svgid = svgid; } bool allow_ptrace = true; SYSCTL_BOOL(_security_bsd, OID_AUTO, allow_ptrace, CTLFLAG_RWTUN, &allow_ptrace, 0, "Deny ptrace(2) use by returning ENOSYS"); diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 53b5d3cbbba9..fa64597d14a5 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -1,3375 +1,3387 @@ ; System call name/number master file. ; Processed to created init_sysent.c, syscalls.c and syscall.h. ; New FreeBSD system calls should be added to the bottom of this file. ; Columns: number audit type name alt{name,tag,rtyp}/comments ; number system call number, must be in order ; audit the audit event associated with the system call ; A value of AUE_NULL means no auditing, but it also means that ; there is no audit event for the call at this time. For the ; case where the event exists, but we don't want auditing, the ; event should be #defined to AUE_NULL in audit_kevents.h. ; type one of STD, OBSOL, RESERVED, UNIMPL, SYSMUX, COMPAT*, ; NODEF, NOARGS, NOPROTO, NOSTD ; The COMPAT* options may be combined with one or more NO* ; options separated by '|' with no spaces (e.g. COMPAT|NOARGS) ; The CAPENABLED option may be ORed into a type. ; name pseudo-prototype of syscall routine ; If one of the following alts is different, then all appear: ; altname name of system call if different ; alttag name of args struct tag if different from [o]`name'"_args" ; altrtyp return type if not int (bogus - syscalls always return int) ; for UNIMPL/OBSOL, name continues with comments ; types: ; STD always included ; COMPAT included on COMPAT #ifdef ; COMPAT4 included on COMPAT_FREEBSD4 #ifdef (FreeBSD 4 compat) ; COMPAT6 included on COMPAT_FREEBSD6 #ifdef (FreeBSD 6 compat) ; COMPAT7 included on COMPAT_FREEBSD7 #ifdef (FreeBSD 7 compat) ; COMPAT10 included on COMPAT_FREEBSD10 #ifdef (FreeBSD 10 compat) ; COMPAT11 included on COMPAT_FREEBSD11 #ifdef (FreeBSD 11 compat) ; COMPAT12 included on COMPAT_FREEBSD12 #ifdef (FreeBSD 12 compat) ; COMPAT13 included on COMPAT_FREEBSD13 #ifdef (FreeBSD 13 compat) ; COMPAT14 included on COMPAT_FREEBSD14 #ifdef (FreeBSD 14 compat) ; OBSOL obsolete, not included in system, only specifies name ; RESERVED reserved for local or vendor use (not for FreeBSD) ; UNIMPL not implemented, placeholder only ; NOSTD implemented but as a lkm that can be statically ; compiled in; sysent entry will be filled with lkmressys ; so the SYSCALL_MODULE macro works ; NOARGS same as STD except do not create structure in sys/sysproto.h ; NODEF same as STD except only have the entry in the syscall table ; added. Meaning - do not create structure or function ; prototype in sys/sysproto.h ; NOPROTO same as STD except do not create structure or ; function prototype in sys/sysproto.h. Does add a ; definition to syscall.h besides adding a sysent. ; NOLIB don't create stubs in libc or libsys ; NOTSTATIC syscall is loadable ; SYSMUX syscall multiplexer. No prototype, argument struct, or ; handler is declared or used. Handled in MD syscall code. ; CAPENABLED syscall is allowed in capability mode ; NORETURN the syscall does not return ; ; To support programmatic generation of both the default ABI and 32-bit compat ; (freebsd32) we impose a number of restrictions on the types of system calls. ; For integer types: ; - Bare int and long are allowed (long is a sign of a bad interface). ; - Use u_int and u_long rather than "unsigned (int|long)". ; - size_t is allowed. ; - typedefs are allowed, but new signed types that vary between 32- and ; 64-bit ABIs must be added to config.known_abi_flags in ; sys/tools/syscalls/config.lua so it knows they require handling. ; - Always-64-bit types other than dev_t, id_t, and off_t must be added to ; util.is64bitType in sys/tools/syscalls/tools/util.lua. ; For pointers: ; - Prefer structs to typedefs so an ABI-specific suffix (e.g., "32") can ; be prepended (e.g., ucontext_t -> struct ucontext -> struct ucontext32). ; - Pointers to objects (structs, unions, etc) containing any long, pointer, ; or time_t arguments need _Contains_ annotations. Such objects should be ; padded such that all 64-bit types are 64-bit aligned. ; annotations: ; SAL 2.0 annotations are used to specify how system calls treat ; arguments that are passed using pointers. There are three basic ; annotations. ; ; _In_ Object pointed to will be read and not modified. ; _Out_ Object pointed to will be written and not read. ; _Inout_ Object pointed to will be written and read. ; ; These annotations are used alone when the pointer refers to a single ; object i.e. scalar types, structs, and pointers, and not NULL. Adding ; the _opt_ suffix, e.g. _In_opt_, implies that the pointer may also ; refer to NULL. ; ; For pointers to arrays, additional suffixes are added: ; ; _In_z_, _Out_z_, _Inout_z_: ; for a NUL terminated array e.g. a string. ; _In_reads_z_(n),_Out_writes_z_(n), _Inout_updates_z_(n): ; for a NUL terminated array e.g. a string, of known length n bytes. ; _In_reads_(n),_Out_writes_(n),_Inout_updates_(n): ; for an array of n elements. ; _In_reads_bytes_(n), _Out_writes_bytes_(n), _Inout_updates_bytes(n): ; for a buffer of n-bytes. ; ; In addition to SAL annotations, pointers are annotated to indicate ; that they point to types that change between ABIs. That means that ; they contain long, pointer, or time_t types. This is indicated with ; a _Contains_ annotation followed immediately by one or more of: ; ; long_ Object contains a direct (or typedef'd) long value and varies ; between 32- and 64-bit ABIs. This includes size_t. ; ptr_ Object contains pointers (or intptr_t) and varies between ; 32- and 64-bit ABIs. ; timet_ Object contains a time_t and varies between i386 and other ; ABIs. ; #include's, #defines's, etc. may be included, and are copied to a ; limited set of output files. Before the first syscalls, #include lines will ; be copied and %%ABI_HEADERS%% expanded. Between system call entries, ; all lines beginning with # will be copied. Caveat Emptor. ; WARNING: this functionality is deprecated. #include #include #include %%ABI_HEADERS%% 0 AUE_NULL SYSMUX { int syscall( int number, ... ); } 1 AUE_EXIT STD|CAPENABLED|NORETURN { void _exit( int rval ); } 2 AUE_FORK STD|CAPENABLED { int fork(void); } 3 AUE_READ STD|CAPENABLED { ssize_t read( int fd, _Out_writes_bytes_(nbyte) void *buf, size_t nbyte ); } 4 AUE_WRITE STD|CAPENABLED { ssize_t write( int fd, _In_reads_bytes_(nbyte) const void *buf, size_t nbyte ); } 5 AUE_OPEN_RWTC STD { int open( _In_z_ const char *path, int flags, mode_t mode ); } ; XXX should be { int open(const char *path, int flags, ...); } ; but we're not ready for varargs. 6 AUE_CLOSE STD|CAPENABLED { int close( int fd ); } 7 AUE_WAIT4 STD|CAPENABLED { int wait4( int pid, _Out_opt_ int *status, int options, _Out_opt_ _Contains_long_timet_ struct rusage *rusage ); } 8 AUE_CREAT COMPAT { int creat( _In_z_ const char *path, int mode ); } 9 AUE_LINK STD { int link( _In_z_ const char *path, _In_z_ const char *link ); } 10 AUE_UNLINK STD { int unlink( _In_z_ const char *path ); } 11 AUE_NULL OBSOL execv 12 AUE_CHDIR STD { int chdir( _In_z_ const char *path ); } 13 AUE_FCHDIR STD { int fchdir( int fd ); } 14 AUE_MKNOD COMPAT11 { int mknod( _In_z_ const char *path, int mode, uint32_t dev ); } 15 AUE_CHMOD STD { int chmod( _In_z_ const char *path, mode_t mode ); } 16 AUE_CHOWN STD { int chown( _In_z_ const char *path, int uid, int gid ); } 17 AUE_NULL STD|CAPENABLED { void *break( _In_ char *nsize ); } 18 AUE_GETFSSTAT COMPAT4 { int getfsstat( _Out_writes_bytes_opt_(bufsize) _Contains_long_ struct ostatfs *buf, long bufsize, int mode ); } 19 AUE_LSEEK COMPAT|CAPENABLED { long lseek( int fd, long offset, int whence ); } 20 AUE_GETPID STD|CAPENABLED { pid_t getpid(void); } 21 AUE_MOUNT STD { int mount( _In_z_ const char *type, _In_z_ const char *path, int flags, _In_opt_ void *data ); } 22 AUE_UMOUNT STD { int unmount( _In_z_ const char *path, int flags ); } 23 AUE_SETUID STD|CAPENABLED { int setuid( uid_t uid ); } 24 AUE_GETUID STD|CAPENABLED { uid_t getuid(void); } 25 AUE_GETEUID STD|CAPENABLED { uid_t geteuid(void); } 26 AUE_PTRACE STD { int ptrace( int req, pid_t pid, _Inout_opt_ _Contains_long_ptr_ caddr_t addr, int data ); } 27 AUE_RECVMSG STD|CAPENABLED { ssize_t recvmsg( int s, _Inout_ _Contains_ptr_ struct msghdr *msg, int flags ); } 28 AUE_SENDMSG STD|CAPENABLED { ssize_t sendmsg( int s, _In_ _Contains_ptr_ const struct msghdr *msg, int flags ); } 29 AUE_RECVFROM STD|CAPENABLED { ssize_t recvfrom( int s, _Out_writes_bytes_(len) void *buf, size_t len, int flags, _Out_writes_bytes_opt_(*fromlenaddr) struct sockaddr *from, _Inout_opt_ __socklen_t *fromlenaddr ); } 30 AUE_ACCEPT STD|CAPENABLED { int accept( int s, _Out_writes_bytes_opt_(*anamelen) struct sockaddr *name, _Inout_opt_ __socklen_t *anamelen ); } 31 AUE_GETPEERNAME STD|CAPENABLED { int getpeername( int fdes, _Out_writes_bytes_(*alen) struct sockaddr *asa, _Inout_opt_ __socklen_t *alen ); } 32 AUE_GETSOCKNAME STD|CAPENABLED { int getsockname( int fdes, _Out_writes_bytes_(*alen) struct sockaddr *asa, _Inout_ __socklen_t *alen ); } 33 AUE_ACCESS STD { int access( _In_z_ const char *path, int amode ); } 34 AUE_CHFLAGS STD { int chflags( _In_z_ const char *path, u_long flags ); } 35 AUE_FCHFLAGS STD|CAPENABLED { int fchflags( int fd, u_long flags ); } 36 AUE_SYNC STD|CAPENABLED { int sync(void); } 37 AUE_KILL STD|CAPENABLED { int kill( int pid, int signum ); } 38 AUE_STAT COMPAT { int stat( _In_z_ const char *path, _Out_ _Contains_timet_ struct ostat *ub ); } 39 AUE_GETPPID STD|CAPENABLED { pid_t getppid(void); } 40 AUE_LSTAT COMPAT { int lstat( _In_z_ const char *path, _Out_ _Contains_timet_ struct ostat *ub ); } 41 AUE_DUP STD|CAPENABLED { int dup( u_int fd ); } 42 AUE_PIPE COMPAT10|CAPENABLED { int pipe(void); } 43 AUE_GETEGID STD|CAPENABLED { gid_t getegid(void); } 44 AUE_PROFILE STD|CAPENABLED { int profil( _Out_writes_bytes_(size) char *samples, size_t size, size_t offset, u_int scale ); } 45 AUE_KTRACE STD { int ktrace( _In_z_ const char *fname, int ops, int facs, int pid ); } 46 AUE_SIGACTION COMPAT|CAPENABLED { int sigaction( int signum, _In_opt_ _Contains_ptr_ struct osigaction *nsa, _Out_opt_ _Contains_ptr_ struct osigaction *osa ); } 47 AUE_GETGID STD|CAPENABLED { gid_t getgid(void); } 48 AUE_SIGPROCMASK COMPAT|CAPENABLED { int sigprocmask( int how, osigset_t mask ); } ; XXX note nonstandard (bogus) calling convention - the libc stub passes ; us the mask, not a pointer to it, and we return the old mask as the ; (int) return value. 49 AUE_GETLOGIN STD|CAPENABLED { int getlogin( _Out_writes_z_(namelen) char *namebuf, u_int namelen ); } 50 AUE_SETLOGIN STD { int setlogin( _In_z_ const char *namebuf ); } 51 AUE_ACCT STD { int acct( _In_z_ const char *path ); } 52 AUE_SIGPENDING COMPAT|CAPENABLED { int sigpending(void); } 53 AUE_SIGALTSTACK STD|CAPENABLED { int sigaltstack( _In_opt_ _Contains_long_ptr_ const struct sigaltstack *ss, _Out_opt_ _Contains_long_ptr_ struct sigaltstack *oss ); } 54 AUE_IOCTL STD|CAPENABLED { int ioctl( int fd, u_long com, _Inout_opt_ _Contains_long_ptr_ char *data ); } 55 AUE_REBOOT STD { int reboot( int opt ); } 56 AUE_REVOKE STD { int revoke( _In_z_ const char *path ); } 57 AUE_SYMLINK STD { int symlink( _In_z_ const char *path, _In_z_ const char *link ); } 58 AUE_READLINK STD { ssize_t readlink( _In_z_ const char *path, _Out_writes_z_(count) char *buf, size_t count ); } 59 AUE_EXECVE STD { int execve( _In_z_ const char *fname, _In_z_ char **argv, _In_z_ char **envv ); } 60 AUE_UMASK STD|CAPENABLED { mode_t umask( mode_t newmask ); } 61 AUE_CHROOT STD { int chroot( _In_z_ const char *path ); } 62 AUE_FSTAT COMPAT|CAPENABLED { int fstat( int fd, _Out_ _Contains_timet_ struct ostat *sb ); } 63 AUE_NULL COMPAT { int getkerninfo( int op, _Out_writes_bytes_opt(*size) char *where, _Inout_opt_ size_t *size, int arg ); } 64 AUE_NULL COMPAT|CAPENABLED { int getpagesize(void); } 65 AUE_MSYNC STD|CAPENABLED { int msync( _In_ void *addr, size_t len, int flags ); } 66 AUE_VFORK STD|CAPENABLED { int vfork(void); } 67 AUE_NULL OBSOL vread 68 AUE_NULL OBSOL vwrite 69 AUE_NULL OBSOL sbrk 70 AUE_NULL OBSOL sstk 71 AUE_MMAP COMPAT|CAPENABLED { void *mmap( _In_ void *addr, int len, int prot, int flags, int fd, long pos ); } 72 AUE_O_VADVISE COMPAT11 { int vadvise( int anom ); } 73 AUE_MUNMAP STD|CAPENABLED { int munmap( _In_ void *addr, size_t len ); } 74 AUE_MPROTECT STD|CAPENABLED { int mprotect( _In_ void *addr, size_t len, int prot ); } 75 AUE_MADVISE STD|CAPENABLED { int madvise( _In_ void *addr, size_t len, int behav ); } 76 AUE_NULL OBSOL vhangup 77 AUE_NULL OBSOL vlimit 78 AUE_MINCORE STD|CAPENABLED { int mincore( _In_ const void *addr, size_t len, _Out_writes_bytes_(len/PAGE_SIZE) char *vec ); } -79 AUE_GETGROUPS STD|CAPENABLED { +79 AUE_GETGROUPS STD|CAPENABLED|COMPAT14 { int getgroups( int gidsetsize, _Out_writes_opt_(gidsetsize) gid_t *gidset ); } -80 AUE_SETGROUPS STD { +80 AUE_SETGROUPS STD|COMPAT14 { int setgroups( int gidsetsize, _In_reads_(gidsetsize) const gid_t *gidset ); } 81 AUE_GETPGRP STD|CAPENABLED { int getpgrp(void); } 82 AUE_SETPGRP STD { int setpgid( int pid, int pgid ); } 83 AUE_SETITIMER STD|CAPENABLED { int setitimer( int which, _In_ _Contains_timet_ const struct itimerval *itv, _Out_opt_ _Contains_timet_ struct itimerval *oitv ); } 84 AUE_WAIT4 COMPAT { int wait(void); } 85 AUE_SWAPON STD { int swapon( _In_z_ const char *name ); } 86 AUE_GETITIMER STD|CAPENABLED { int getitimer( int which, _Out_ _Contains_timet_ struct itimerval *itv ); } 87 AUE_SYSCTL COMPAT|CAPENABLED { int gethostname( _Out_writes_z_(len) char *hostname, u_int len ); } 88 AUE_SYSCTL COMPAT { int sethostname( _In_reads_z_(len) char *hostname, u_int len ); } 89 AUE_GETDTABLESIZE STD|CAPENABLED { int getdtablesize(void); } 90 AUE_DUP2 STD|CAPENABLED { int dup2( u_int from, u_int to ); } 91 AUE_NULL RESERVED 92 AUE_FCNTL STD|CAPENABLED { int fcntl( int fd, int cmd, intptr_t arg ); } ; XXX should be { int fcntl(int fd, int cmd, ...); } ; but we're not ready for varargs. 93 AUE_SELECT STD|CAPENABLED { int select( int nd, _Inout_opt_ fd_set *in, _Inout_opt_ fd_set *ou, _Inout_opt_ fd_set *ex, _In_opt_ _Contains_long_timet_ struct timeval *tv ); } 94 AUE_NULL RESERVED 95 AUE_FSYNC STD|CAPENABLED { int fsync( int fd ); } 96 AUE_SETPRIORITY STD|CAPENABLED { int setpriority( int which, int who, int prio ); } 97 AUE_SOCKET STD|CAPENABLED { int socket( int domain, int type, int protocol ); } 98 AUE_CONNECT STD { int connect( int s, _In_reads_bytes_(namelen) const struct sockaddr *name, __socklen_t namelen ); } 99 AUE_ACCEPT COMPAT|CAPENABLED { int accept( int s, _Out_writes_bytes_opt_(*anamelen) struct sockaddr *name, __socklen_t *anamelen ); } 100 AUE_GETPRIORITY STD|CAPENABLED { int getpriority( int which, int who ); } 101 AUE_SEND COMPAT|CAPENABLED { int send( int s, _In_reads_bytes_(len) const void *buf, int len, int flags ); } 102 AUE_RECV COMPAT|CAPENABLED { int recv( int s, _Out_writes_bytes_(len) void *buf, int len, int flags ); } 103 AUE_SIGRETURN COMPAT|CAPENABLED { int sigreturn( _In_ struct osigcontext *sigcntxp ); } 104 AUE_BIND STD { int bind( int s, _In_reads_bytes_(namelen) const struct sockaddr *name, __socklen_t namelen ); } 105 AUE_SETSOCKOPT STD|CAPENABLED { int setsockopt( int s, int level, int name, _In_reads_bytes_opt_(valsize) const void *val, __socklen_t valsize ); } 106 AUE_LISTEN STD|CAPENABLED { int listen( int s, int backlog ); } 107 AUE_NULL OBSOL vtimes 108 AUE_NULL COMPAT|CAPENABLED { int sigvec( int signum, _In_opt_ _Contains_ptr_ struct sigvec *nsv, _Out_opt_ _Contains_ptr_ struct sigvec *osv ); } 109 AUE_NULL COMPAT|CAPENABLED { int sigblock( int mask ); } 110 AUE_NULL COMPAT|CAPENABLED { int sigsetmask( int mask ); } 111 AUE_NULL COMPAT|CAPENABLED { int sigsuspend( osigset_t mask ); } ; XXX note nonstandard (bogus) calling convention - the libc stub passes ; us the mask, not a pointer to it. 112 AUE_NULL COMPAT|CAPENABLED { int sigstack( _In_opt_ _Contains_ptr_ struct sigstack *nss, _Out_opt_ _Contains_ptr_ struct sigstack *oss ); } 113 AUE_RECVMSG COMPAT|CAPENABLED { int recvmsg( int s, _Inout_ _Contains_ptr_ struct omsghdr *msg, int flags ); } 114 AUE_SENDMSG COMPAT|CAPENABLED { int sendmsg( int s, _In_ _Contains_ptr_ const struct omsghdr *msg, int flags ); } 115 AUE_NULL OBSOL vtrace 116 AUE_GETTIMEOFDAY STD|CAPENABLED { int gettimeofday( _Out_ _Contains_long_timet_ struct timeval *tp, _Out_opt_ struct timezone *tzp ); } 117 AUE_GETRUSAGE STD|CAPENABLED { int getrusage( int who, _Out_ _Contains_long_ struct rusage *rusage ); } 118 AUE_GETSOCKOPT STD|CAPENABLED { int getsockopt( int s, int level, int name, _Out_writes_bytes_opt_(*avalsize) void *val, _Inout_ __socklen_t *avalsize ); } 119 AUE_NULL RESERVED 120 AUE_READV STD|CAPENABLED { ssize_t readv( int fd, _In_reads_(iovcnt) _Contains_long_ptr_ const struct iovec *iovp, u_int iovcnt ); } 121 AUE_WRITEV STD|CAPENABLED { ssize_t writev( int fd, _In_reads_(iovcnt) _Contains_long_ptr_ const struct iovec *iovp, u_int iovcnt ); } 122 AUE_SETTIMEOFDAY STD { int settimeofday( _In_ _Contains_long_timet_ const struct timeval *tv, _In_opt_ const struct timezone *tzp ); } 123 AUE_FCHOWN STD|CAPENABLED { int fchown( int fd, int uid, int gid ); } 124 AUE_FCHMOD STD|CAPENABLED { int fchmod( int fd, mode_t mode ); } 125 AUE_RECVFROM COMPAT|CAPENABLED { int recvfrom( int s, _Out_writes_(len) void *buf, size_t len, int flags, _Out_writes_bytes_(*fromlenaddr) struct sockaddr *from, _Inout_ __socklen_t *fromlenaddr ); } 126 AUE_SETREUID STD|CAPENABLED { int setreuid( int ruid, int euid ); } 127 AUE_SETREGID STD|CAPENABLED { int setregid( int rgid, int egid ); } 128 AUE_RENAME STD { int rename( _In_z_ const char *from, _In_z_ const char *to ); } 129 AUE_TRUNCATE COMPAT { int truncate( _In_z_ const char *path, long length ); } 130 AUE_FTRUNCATE COMPAT|CAPENABLED { int ftruncate( int fd, long length ); } 131 AUE_FLOCK STD|CAPENABLED { int flock( int fd, int how ); } 132 AUE_MKFIFO STD { int mkfifo( _In_z_ const char *path, mode_t mode ); } 133 AUE_SENDTO STD|CAPENABLED { ssize_t sendto( int s, _In_reads_bytes_(len) const void *buf, size_t len, int flags, _In_reads_bytes_opt_(tolen) const struct sockaddr *to, __socklen_t tolen ); } 134 AUE_SHUTDOWN STD|CAPENABLED { int shutdown( int s, int how ); } 135 AUE_SOCKETPAIR STD|CAPENABLED { int socketpair( int domain, int type, int protocol, _Out_writes_(2) int *rsv ); } 136 AUE_MKDIR STD { int mkdir( _In_z_ const char *path, mode_t mode ); } 137 AUE_RMDIR STD { int rmdir( _In_z_ const char *path ); } 138 AUE_UTIMES STD { int utimes( _In_z_ const char *path, _In_ _Contains_long_timet_ const struct timeval *tptr ); } 139 AUE_NULL OBSOL sigreturn 140 AUE_ADJTIME STD { int adjtime( _In_ _Contains_long_timet_ const struct timeval *delta, _Out_opt_ _Contains_long_timet_ struct timeval *olddelta ); } 141 AUE_GETPEERNAME COMPAT|CAPENABLED { int getpeername( int fdes, _Out_writes_bytes_(*alen) struct sockaddr *asa, _Inout_opt_ __socklen_t *alen ); } 142 AUE_SYSCTL COMPAT|CAPENABLED { long gethostid(void); } 143 AUE_SYSCTL COMPAT { int sethostid( long hostid ); } 144 AUE_GETRLIMIT COMPAT|CAPENABLED { int getrlimit( u_int which, _Out_ struct orlimit *rlp ); } 145 AUE_SETRLIMIT COMPAT|CAPENABLED { int setrlimit( u_int which, _Out_ struct orlimit *rlp ); } 146 AUE_KILLPG COMPAT { int killpg( int pgid, int signum ); } 147 AUE_SETSID STD|CAPENABLED { int setsid(void); } 148 AUE_QUOTACTL STD { int quotactl( _In_z_ const char *path, int cmd, int uid, _In_ void *arg ); } 149 AUE_O_QUOTA COMPAT { int quota(void); } 150 AUE_GETSOCKNAME COMPAT|CAPENABLED { int getsockname( int fdes, _Out_writes_bytes_(*alen) struct sockaddr *asa, _Inout_ __socklen_t *alen ); } 151-153 AUE_NULL RESERVED ; 154 is initialised by the NLM code, if present. 154 AUE_NULL NOSTD { int nlm_syscall( int debug_level, int grace_period, int addr_count, _In_reads_(addr_count) char **addrs ); } ; 155 is initialized by the NFS code, if present. 155 AUE_NFS_SVC NOSTD { int nfssvc( int flag, _In_ void *argp ); } 156 AUE_GETDIRENTRIES COMPAT|CAPENABLED { int getdirentries( int fd, _Out_writes_bytes_(count) char *buf, u_int count, _Out_opt_ long *basep ); } 157 AUE_STATFS COMPAT4 { int statfs( _In_z_ const char *path, _Out_ _Contains_long_ struct ostatfs *buf ); } 158 AUE_FSTATFS COMPAT4|CAPENABLED { int fstatfs( int fd, _Out_ _Contains_long_ struct ostatfs *buf ); } 159 AUE_NULL RESERVED 160 AUE_LGETFH STD { int lgetfh( _In_z_ const char *fname, _Out_ struct fhandle *fhp ); } 161 AUE_NFS_GETFH STD { int getfh( _In_z_ const char *fname, _Out_ struct fhandle *fhp ); } 162 AUE_SYSCTL COMPAT4|CAPENABLED { int getdomainname( _Out_writes_z_(len) char *domainname, int len ); } 163 AUE_SYSCTL COMPAT4 { int setdomainname( _In_reads_z_(len) char *domainname, int len ); } 164 AUE_NULL COMPAT4 { int uname( _Out_ struct utsname *name ); } 165 AUE_SYSARCH STD|CAPENABLED { int sysarch( int op, _In_z_ char *parms ); } 166 AUE_RTPRIO STD|CAPENABLED { int rtprio( int function, pid_t pid, _Inout_ struct rtprio *rtp ); } 167-168 AUE_NULL RESERVED 169 AUE_SEMSYS NOSTD { int semsys( int which, int a2, int a3, int a4, int a5 ); } ; XXX should be { int semsys(int which, ...); } 170 AUE_MSGSYS NOSTD { int msgsys( int which, int a2, int a3, int a4, int a5, int a6 ); } ; XXX should be { int msgsys(int which, ...); } 171 AUE_SHMSYS NOSTD { int shmsys( int which, int a2, int a3, int a4 ); } ; XXX should be { int shmsys(int which, ...); } 172 AUE_NULL RESERVED 173 AUE_PREAD COMPAT6|CAPENABLED { ssize_t pread( int fd, _Out_writes_bytes_(nbyte) void *buf, size_t nbyte, int pad, off_t offset ); } 174 AUE_PWRITE COMPAT6|CAPENABLED { ssize_t pwrite( int fd, _In_reads_bytes_(nbyte) const void *buf, size_t nbyte, int pad, off_t offset ); } 175 AUE_SETFIB STD { int setfib( int fibnum ); } 176 AUE_NTP_ADJTIME STD { int ntp_adjtime( _Inout_ _Contains_long_ struct timex *tp ); } 177-180 AUE_NULL RESERVED 181 AUE_SETGID STD|CAPENABLED { int setgid( gid_t gid ); } 182 AUE_SETEGID STD|CAPENABLED { int setegid( gid_t egid ); } 183 AUE_SETEUID STD|CAPENABLED { int seteuid( uid_t euid ); } 184 AUE_NULL OBSOL lfs_bmapv 185 AUE_NULL OBSOL lfs_markv 186 AUE_NULL OBSOL lfs_segclean 187 AUE_NULL OBSOL lfs_segwait 188 AUE_STAT COMPAT11 { int stat( _In_z_ const char *path, _Out_ _Contains_timet_ struct freebsd11_stat *ub ); } 189 AUE_FSTAT COMPAT11|CAPENABLED { int fstat( int fd, _Out_ _Contains_timet_ struct freebsd11_stat *sb ); } 190 AUE_LSTAT COMPAT11 { int lstat( _In_z_ const char *path, _Out_ _Contains_timet_ struct freebsd11_stat *ub ); } 191 AUE_PATHCONF STD { int pathconf( _In_z_ const char *path, int name ); } 192 AUE_FPATHCONF STD|CAPENABLED { int fpathconf( int fd, int name ); } 193 AUE_NULL RESERVED 194 AUE_GETRLIMIT STD|CAPENABLED { int getrlimit( u_int which, _Out_ struct rlimit *rlp ); } 195 AUE_SETRLIMIT STD|CAPENABLED { int setrlimit( u_int which, _In_ struct rlimit *rlp ); } 196 AUE_GETDIRENTRIES COMPAT11|CAPENABLED { int getdirentries( int fd, _Out_writes_bytes_(count) char *buf, u_int count, _Out_opt_ long *basep ); } 197 AUE_MMAP COMPAT6|CAPENABLED { void *mmap( _In_ void *addr, size_t len, int prot, int flags, int fd, int pad, off_t pos ); } 198 AUE_NULL SYSMUX { int __syscall( int64_t number, ... ); } 199 AUE_LSEEK COMPAT6|CAPENABLED { off_t lseek( int fd, int pad, off_t offset, int whence ); } 200 AUE_TRUNCATE COMPAT6 { int truncate( _In_z_ const char *path, int pad, off_t length ); } 201 AUE_FTRUNCATE COMPAT6|CAPENABLED { int ftruncate( int fd, int pad, off_t length ); } 202 AUE_SYSCTL STD|CAPENABLED { int __sysctl( _In_reads_(namelen) const int *name, u_int namelen, _Out_writes_bytes_opt_(*oldlenp) void *old, _Inout_opt_ size_t *oldlenp, _In_reads_bytes_opt_(newlen) const void *new, size_t newlen ); } 203 AUE_MLOCK STD|CAPENABLED { int mlock( _In_ const void *addr, size_t len ); } 204 AUE_MUNLOCK STD|CAPENABLED { int munlock( _In_ const void *addr, size_t len ); } 205 AUE_UNDELETE STD { int undelete( _In_z_ const char *path ); } 206 AUE_FUTIMES STD|CAPENABLED { int futimes( int fd, _In_reads_(2) _Contains_long_timet_ const struct timeval *tptr ); } 207 AUE_GETPGID STD|CAPENABLED { int getpgid( pid_t pid ); } 208 AUE_NULL RESERVED 209 AUE_POLL STD|CAPENABLED { int poll( _Inout_updates_(nfds) struct pollfd *fds, u_int nfds, int timeout ); } ; ; The following are reserved for loadable syscalls ; 210 AUE_NULL NODEF|NOTSTATIC lkmnosys lkmnosys nosys_args int 211 AUE_NULL NODEF|NOTSTATIC lkmnosys lkmnosys nosys_args int 212 AUE_NULL NODEF|NOTSTATIC lkmnosys lkmnosys nosys_args int 213 AUE_NULL NODEF|NOTSTATIC lkmnosys lkmnosys nosys_args int 214 AUE_NULL NODEF|NOTSTATIC lkmnosys lkmnosys nosys_args int 215 AUE_NULL NODEF|NOTSTATIC lkmnosys lkmnosys nosys_args int 216 AUE_NULL NODEF|NOTSTATIC lkmnosys lkmnosys nosys_args int 217 AUE_NULL NODEF|NOTSTATIC lkmnosys lkmnosys nosys_args int 218 AUE_NULL NODEF|NOTSTATIC lkmnosys lkmnosys nosys_args int 219 AUE_NULL NODEF|NOTSTATIC lkmnosys lkmnosys nosys_args int 220 AUE_SEMCTL COMPAT7|NOSTD { int __semctl( int semid, int semnum, int cmd, _Contains_ptr_ union semun_old *arg ); } 221 AUE_SEMGET NOSTD { int semget( key_t key, int nsems, int semflg ); } 222 AUE_SEMOP NOSTD { int semop( int semid, _In_reads_(nsops) struct sembuf *sops, size_t nsops ); } 223 AUE_NULL OBSOL semconfig 224 AUE_MSGCTL COMPAT7|NOSTD { int msgctl( int msqid, int cmd, _Contains_long_ptr_timet_ struct msqid_ds_old *buf ); } 225 AUE_MSGGET NOSTD { int msgget( key_t key, int msgflg ); } 226 AUE_MSGSND NOSTD { int msgsnd( int msqid, _In_reads_bytes_(msgsz) _Contains_long_ const void *msgp, size_t msgsz, int msgflg ); } 227 AUE_MSGRCV NOSTD { ssize_t msgrcv( int msqid, _Out_writes_bytes_(msgsz) _Contains_long_ void *msgp, size_t msgsz, long msgtyp, int msgflg ); } 228 AUE_SHMAT NOSTD { void *shmat( int shmid, _In_ const void *shmaddr, int shmflg ); } 229 AUE_SHMCTL COMPAT7|NOSTD { int shmctl( int shmid, int cmd, _Inout_opt_ _Contains_long_ struct shmid_ds_old *buf ); } 230 AUE_SHMDT NOSTD { int shmdt( _In_ const void *shmaddr ); } 231 AUE_SHMGET NOSTD { int shmget( key_t key, size_t size, int shmflg ); } 232 AUE_NULL STD|CAPENABLED { int clock_gettime( clockid_t clock_id, _Out_ _Contains_long_timet_ struct timespec *tp ); } 233 AUE_CLOCK_SETTIME STD { int clock_settime( clockid_t clock_id, _In_ _Contains_long_timet_ const struct timespec *tp ); } 234 AUE_NULL STD|CAPENABLED { int clock_getres( clockid_t clock_id, _Out_ _Contains_long_timet_ struct timespec *tp ); } 235 AUE_NULL STD|CAPENABLED { int ktimer_create( clockid_t clock_id, _In_ _Contains_long_ptr_ struct sigevent *evp, _Out_ int *timerid ); } 236 AUE_NULL STD|CAPENABLED { int ktimer_delete( int timerid ); } 237 AUE_NULL STD|CAPENABLED { int ktimer_settime( int timerid, int flags, _In_ _Contains_long_timet_ const struct itimerspec *value, _Out_opt_ _Contains_long_timet_ struct itimerspec *ovalue ); } 238 AUE_NULL STD|CAPENABLED { int ktimer_gettime( int timerid, _Out_ _Contains_long_timet_ struct itimerspec *value ); } 239 AUE_NULL STD|CAPENABLED { int ktimer_getoverrun( int timerid ); } 240 AUE_NULL STD|CAPENABLED { int nanosleep( _In_ _Contains_long_timet_ const struct timespec *rqtp, _Out_opt_ _Contains_long_timet_ struct timespec *rmtp ); } 241 AUE_NULL STD { int ffclock_getcounter( _Out_ ffcounter *ffcount ); } 242 AUE_NULL STD { int ffclock_setestimate( _In_ _Contains_timet_ struct ffclock_estimate *cest ); } 243 AUE_NULL STD { int ffclock_getestimate( _Out_ _Contains_timet_ struct ffclock_estimate *cest ); } 244 AUE_NULL STD { int clock_nanosleep( clockid_t clock_id, int flags, _In_ _Contains_long_timet_ const struct timespec *rqtp, _Out_opt_ _Contains_long_timet_ struct timespec *rmtp ); } 245-246 AUE_NULL RESERVED 247 AUE_NULL STD { int clock_getcpuclockid2( id_t id, int which, _Out_ clockid_t *clock_id ); } 248 AUE_NULL STD|CAPENABLED { int ntp_gettime( _Out_ _Contains_long_timet_ struct ntptimeval *ntvp ); } 249 AUE_NULL RESERVED 250 AUE_MINHERIT STD|CAPENABLED { int minherit( _In_ void *addr, size_t len, int inherit ); } 251 AUE_RFORK STD|CAPENABLED { int rfork( int flags ); } 252 AUE_POLL OBSOL openbsd_poll 253 AUE_ISSETUGID STD|CAPENABLED { int issetugid(void); } 254 AUE_LCHOWN STD { int lchown( _In_z_ const char *path, int uid, int gid ); } 255 AUE_AIO_READ STD|CAPENABLED { int aio_read( _Inout_ _Contains_long_ptr_ struct aiocb *aiocbp ); } 256 AUE_AIO_WRITE STD|CAPENABLED { int aio_write( _Inout_ _Contains_long_ptr_ struct aiocb *aiocbp ); } 257 AUE_LIO_LISTIO STD|CAPENABLED { int lio_listio( int mode, _Inout_updates_(nent) _Contains_long_ptr_ struct aiocb * const *acb_list, int nent, _In_opt_ _Contains_long_ptr_ struct sigevent *sig ); } 258-271 AUE_NULL RESERVED 272 AUE_O_GETDENTS COMPAT11|CAPENABLED { int getdents( int fd, _Out_writes_bytes_(count) char *buf, size_t count ); } 273 AUE_NULL RESERVED 274 AUE_LCHMOD STD { int lchmod( _In_z_ const char *path, mode_t mode ); } 275 AUE_NULL OBSOL netbsd_lchown 276 AUE_LUTIMES STD { int lutimes( _In_z_ const char *path, _In_ _Contains_long_timet_ const struct timeval *tptr ); } 277 AUE_NULL OBSOL netbsd_msync 278 AUE_STAT COMPAT11 { int nstat( _In_z_ const char *path, _Out_ _Contains_long_timet_ struct nstat *ub ); } 279 AUE_FSTAT COMPAT11 { int nfstat( int fd, _Out_ _Contains_long_timet_ struct nstat *sb ); } 280 AUE_LSTAT COMPAT11 { int nlstat( _In_z_ const char *path, _Out_ _Contains_long_timet_ struct nstat *ub ); } 281-288 AUE_NULL RESERVED 289 AUE_PREADV STD|CAPENABLED { ssize_t preadv( int fd, _In_reads_(iovcnt) _Contains_long_ptr_ struct iovec *iovp, u_int iovcnt, off_t offset ); } 290 AUE_PWRITEV STD|CAPENABLED { ssize_t pwritev( int fd, _In_reads_(iovcnt) _Contains_long_ptr_ struct iovec *iovp, u_int iovcnt, off_t offset ); } 291-296 AUE_NULL RESERVED 297 AUE_FHSTATFS COMPAT4 { int fhstatfs( _In_ const struct fhandle *u_fhp, _Out_ _Contains_long_ struct ostatfs *buf ); } 298 AUE_FHOPEN STD { int fhopen( _In_ const struct fhandle *u_fhp, int flags ); } 299 AUE_FHSTAT COMPAT11 { int fhstat( _In_ const struct fhandle *u_fhp, _Out_ _Contains_long_timet_ struct freebsd11_stat *sb ); } 300 AUE_NULL STD { int modnext( int modid ); } 301 AUE_NULL STD { int modstat( int modid, _Out_ _Contains_long_ struct module_stat *stat ); } 302 AUE_NULL STD { int modfnext( int modid ); } 303 AUE_NULL STD { int modfind( _In_z_ const char *name ); } 304 AUE_MODLOAD STD { int kldload( _In_z_ const char *file ); } 305 AUE_MODUNLOAD STD { int kldunload( int fileid ); } 306 AUE_NULL STD { int kldfind( _In_z_ const char *file ); } 307 AUE_NULL STD { int kldnext( int fileid ); } 308 AUE_NULL STD { int kldstat( int fileid, _Out_ _Contains_long_ptr_ struct kld_file_stat *stat ); } 309 AUE_NULL STD { int kldfirstmod( int fileid ); } 310 AUE_GETSID STD|CAPENABLED { int getsid( pid_t pid ); } 311 AUE_SETRESUID STD|CAPENABLED { int setresuid( uid_t ruid, uid_t euid, uid_t suid ); } 312 AUE_SETRESGID STD|CAPENABLED { int setresgid( gid_t rgid, gid_t egid, gid_t sgid ); } 313 AUE_NULL OBSOL signanosleep 314 AUE_AIO_RETURN STD|CAPENABLED { ssize_t aio_return( _Inout_ _Contains_long_ptr_ struct aiocb *aiocbp ); } 315 AUE_AIO_SUSPEND STD|CAPENABLED { int aio_suspend( _Inout_updates_(nent) _Contains_long_ptr_ const struct aiocb * const * aiocbp, int nent, _In_opt_ _Contains_long_timet_ const struct timespec *timeout ); } 316 AUE_AIO_CANCEL STD|CAPENABLED { int aio_cancel( int fd, _In_opt_ _Contains_long_ptr_ struct aiocb *aiocbp ); } 317 AUE_AIO_ERROR STD|CAPENABLED { int aio_error( _In_ _Contains_long_ptr_ struct aiocb *aiocbp ); } 318 AUE_AIO_READ COMPAT6|CAPENABLED { int aio_read( _Inout_ _Contains_long_ptr_ struct oaiocb *aiocbp ); } 319 AUE_AIO_WRITE COMPAT6|CAPENABLED { int aio_write( _Inout_ _Contains_long_ptr_ struct oaiocb *aiocbp ); } 320 AUE_LIO_LISTIO COMPAT6|CAPENABLED { int lio_listio( int mode, _Inout_updates_(nent) _Contains_long_ptr_ struct oaiocb * const *acb_list, int nent, _In_opt_ _Contains_ptr_ struct osigevent *sig ); } 321 AUE_NULL STD|CAPENABLED|NOLIB { int yield(void); } 322 AUE_NULL OBSOL thr_sleep 323 AUE_NULL OBSOL thr_wakeup 324 AUE_MLOCKALL STD|CAPENABLED { int mlockall( int how ); } 325 AUE_MUNLOCKALL STD|CAPENABLED { int munlockall(void); } 326 AUE_GETCWD STD { int __getcwd( _Out_writes_z_(buflen) char *buf, size_t buflen ); } 327 AUE_NULL STD|CAPENABLED { int sched_setparam( pid_t pid, _In_ const struct sched_param *param ); } 328 AUE_NULL STD|CAPENABLED { int sched_getparam( pid_t pid, _Out_ struct sched_param *param ); } 329 AUE_NULL STD|CAPENABLED { int sched_setscheduler( pid_t pid, int policy, _In_ const struct sched_param *param ); } 330 AUE_NULL STD|CAPENABLED { int sched_getscheduler( pid_t pid ); } 331 AUE_NULL STD|CAPENABLED { int sched_yield(void); } 332 AUE_NULL STD|CAPENABLED { int sched_get_priority_max( int policy ); } 333 AUE_NULL STD|CAPENABLED { int sched_get_priority_min( int policy ); } 334 AUE_NULL STD|CAPENABLED { int sched_rr_get_interval( pid_t pid, _Out_ _Contains_long_timet_ struct timespec *interval ); } 335 AUE_NULL STD|CAPENABLED { int utrace( _In_reads_bytes_(len) const void *addr, size_t len ); } 336 AUE_SENDFILE COMPAT4|CAPENABLED { int sendfile( int fd, int s, off_t offset, size_t nbytes, _In_opt_ _Contains_ptr_ struct sf_hdtr *hdtr, _Out_opt_ off_t *sbytes, int flags ); } 337 AUE_NULL STD { int kldsym( int fileid, int cmd, _In_ _Contains_long_ptr_ void *data ); } 338 AUE_JAIL STD { int jail( _In_ _Contains_ptr_ struct jail *jail ); } 339 AUE_NULL NOSTD|NOTSTATIC { int nnpfs_syscall( int operation, char *a_pathP, int a_opcode, void *a_paramsP, int a_followSymlinks ); } 340 AUE_SIGPROCMASK STD|CAPENABLED { int sigprocmask( int how, _In_opt_ const sigset_t *set, _Out_opt_ sigset_t *oset ); } 341 AUE_SIGSUSPEND STD|CAPENABLED { int sigsuspend( _In_ const sigset_t *sigmask ); } 342 AUE_SIGACTION COMPAT4|CAPENABLED { int sigaction( int sig, _In_opt_ _Contains_ptr_ const struct sigaction *act, _Out_opt_ _Contains_ptr_ struct sigaction *oact ); } 343 AUE_SIGPENDING STD|CAPENABLED { int sigpending( _In_ sigset_t *set ); } 344 AUE_SIGRETURN COMPAT4|CAPENABLED { int sigreturn( _In_ _Contains_long_ptr_ const struct freebsd4_ucontext *sigcntxp ); } 345 AUE_SIGWAIT STD|CAPENABLED { int sigtimedwait( _In_ const sigset_t *set, _Out_opt_ _Contains_long_ptr_ struct __siginfo *info, _In_opt_ _Contains_long_timet_ const struct timespec *timeout ); } 346 AUE_NULL STD|CAPENABLED { int sigwaitinfo( _In_ const sigset_t *set, _Out_opt_ _Contains_long_ptr_ struct __siginfo *info ); } 347 AUE_ACL_GET_FILE STD { int __acl_get_file( _In_z_ const char *path, __acl_type_t type, _Out_ struct acl *aclp ); } 348 AUE_ACL_SET_FILE STD { int __acl_set_file( _In_z_ const char *path, __acl_type_t type, _In_ struct acl *aclp ); } 349 AUE_ACL_GET_FD STD|CAPENABLED { int __acl_get_fd( int filedes, __acl_type_t type, _Out_ struct acl *aclp ); } 350 AUE_ACL_SET_FD STD|CAPENABLED { int __acl_set_fd( int filedes, __acl_type_t type, _In_ struct acl *aclp ); } 351 AUE_ACL_DELETE_FILE STD { int __acl_delete_file( _In_z_ const char *path, __acl_type_t type ); } 352 AUE_ACL_DELETE_FD STD|CAPENABLED { int __acl_delete_fd( int filedes, __acl_type_t type ); } 353 AUE_ACL_CHECK_FILE STD { int __acl_aclcheck_file( _In_z_ const char *path, __acl_type_t type, _In_ struct acl *aclp ); } 354 AUE_ACL_CHECK_FD STD|CAPENABLED { int __acl_aclcheck_fd( int filedes, __acl_type_t type, _In_ struct acl *aclp ); } 355 AUE_EXTATTRCTL STD { int extattrctl( _In_z_ const char *path, int cmd, _In_z_opt_ const char *filename, int attrnamespace, _In_z_ const char *attrname ); } 356 AUE_EXTATTR_SET_FILE STD { ssize_t extattr_set_file( _In_z_ const char *path, int attrnamespace, _In_z_ const char *attrname, _In_reads_bytes_(nbytes) void *data, size_t nbytes ); } 357 AUE_EXTATTR_GET_FILE STD { ssize_t extattr_get_file( _In_z_ const char *path, int attrnamespace, _In_z_ const char *attrname, _Out_writes_bytes_(nbytes) void *data, size_t nbytes ); } 358 AUE_EXTATTR_DELETE_FILE STD { int extattr_delete_file( _In_z_ const char *path, int attrnamespace, _In_z_ const char *attrname ); } 359 AUE_AIO_WAITCOMPLETE STD|CAPENABLED { ssize_t aio_waitcomplete( _Outptr_result_maybenull_ struct aiocb **aiocbp, _In_opt_ _Contains_long_timet_ struct timespec *timeout ); } 360 AUE_GETRESUID STD|CAPENABLED { int getresuid( _Out_opt_ uid_t *ruid, _Out_opt_ uid_t *euid, _Out_opt_ uid_t *suid ); } 361 AUE_GETRESGID STD|CAPENABLED { int getresgid( _Out_opt_ gid_t *rgid, _Out_opt_ gid_t *egid, _Out_opt_ gid_t *sgid ); } 362 AUE_KQUEUE STD|CAPENABLED { int kqueue(void); } 363 AUE_KEVENT COMPAT11|CAPENABLED { int kevent( int fd, _In_reads_opt_(nchanges) _Contains_ptr_ const struct freebsd11_kevent *changelist, int nchanges, _Out_writes_opt_(nevents) _Contains_ptr_ struct freebsd11_kevent *eventlist, int nevents, _In_opt_ _Contains_long_timet_ const struct timespec *timeout ); } 364 AUE_NULL OBSOL __cap_get_proc 365 AUE_NULL OBSOL __cap_set_proc 366 AUE_NULL OBSOL __cap_get_fd 367 AUE_NULL OBSOL __cap_get_file 368 AUE_NULL OBSOL __cap_set_fd 369 AUE_NULL OBSOL __cap_set_file 370 AUE_NULL RESERVED 371 AUE_EXTATTR_SET_FD STD|CAPENABLED { ssize_t extattr_set_fd( int fd, int attrnamespace, _In_z_ const char *attrname, _In_reads_bytes_(nbytes) void *data, size_t nbytes ); } 372 AUE_EXTATTR_GET_FD STD|CAPENABLED { ssize_t extattr_get_fd( int fd, int attrnamespace, _In_z_ const char *attrname, _Out_writes_bytes_(nbytes) void *data, size_t nbytes ); } 373 AUE_EXTATTR_DELETE_FD STD|CAPENABLED { int extattr_delete_fd( int fd, int attrnamespace, _In_z_ const char *attrname ); } 374 AUE_SETUGID STD { int __setugid( int flag ); } 375 AUE_NULL OBSOL nfsclnt 376 AUE_EACCESS STD { int eaccess( _In_z_ const char *path, int amode ); } 377 AUE_NULL NOSTD|NOTSTATIC { int afs3_syscall( long syscall, long parm1, long parm2, long parm3, long parm4, long parm5, long parm6 ); } 378 AUE_NMOUNT STD { int nmount( _In_reads_(iovcnt) _Contains_long_ptr_ struct iovec *iovp, unsigned int iovcnt, int flags ); } 379 AUE_NULL OBSOL kse_exit 380 AUE_NULL OBSOL kse_wakeup 381 AUE_NULL OBSOL kse_create 382 AUE_NULL OBSOL kse_thr_interrupt 383 AUE_NULL OBSOL kse_release 384 AUE_NULL STD|CAPENABLED { int __mac_get_proc( _In_ _Contains_long_ptr_ struct mac *mac_p ); } 385 AUE_NULL STD|CAPENABLED { int __mac_set_proc( _In_ _Contains_long_ptr_ struct mac *mac_p ); } 386 AUE_NULL STD|CAPENABLED { int __mac_get_fd( int fd, _In_ _Contains_long_ptr_ struct mac *mac_p ); } 387 AUE_NULL STD { int __mac_get_file( _In_z_ const char *path_p, _In_ _Contains_long_ptr_ struct mac *mac_p ); } 388 AUE_NULL STD|CAPENABLED { int __mac_set_fd( int fd, _In_ _Contains_long_ptr_ struct mac *mac_p ); } 389 AUE_NULL STD { int __mac_set_file( _In_z_ const char *path_p, _In_ _Contains_long_ptr_ struct mac *mac_p ); } 390 AUE_NULL STD { int kenv( int what, _In_z_opt_ const char *name, _Inout_updates_opt_(len) char *value, int len ); } 391 AUE_LCHFLAGS STD { int lchflags( _In_z_ const char *path, u_long flags ); } 392 AUE_NULL STD|CAPENABLED { int uuidgen( _Out_writes_(count) struct uuid *store, int count ); } 393 AUE_SENDFILE STD|CAPENABLED { int sendfile( int fd, int s, off_t offset, size_t nbytes, _In_opt_ _Contains_ptr_ struct sf_hdtr *hdtr, _Out_opt_ off_t *sbytes, int flags ); } 394 AUE_NULL STD { int mac_syscall( _In_z_ const char *policy, int call, _In_opt_ void *arg ); } 395 AUE_GETFSSTAT COMPAT11 { int getfsstat( _Out_writes_bytes_opt_(bufsize) struct freebsd11_statfs *buf, long bufsize, int mode ); } 396 AUE_STATFS COMPAT11 { int statfs( _In_z_ const char *path, _Out_ struct freebsd11_statfs *buf ); } 397 AUE_FSTATFS COMPAT11|CAPENABLED { int fstatfs( int fd, _Out_ struct freebsd11_statfs *buf ); } 398 AUE_FHSTATFS COMPAT11 { int fhstatfs( _In_ const struct fhandle *u_fhp, _Out_ struct freebsd11_statfs *buf ); } 399 AUE_NULL RESERVED 400 AUE_SEMCLOSE NOSTD { int ksem_close( semid_t id ); } 401 AUE_SEMPOST NOSTD { int ksem_post( semid_t id ); } 402 AUE_SEMWAIT NOSTD { int ksem_wait( semid_t id ); } 403 AUE_SEMTRYWAIT NOSTD { int ksem_trywait( semid_t id ); } 404 AUE_SEMINIT NOSTD { int ksem_init( _Out_ semid_t *idp, unsigned int value ); } 405 AUE_SEMOPEN NOSTD { int ksem_open( _Out_ semid_t *idp, _In_z_ const char *name, int oflag, mode_t mode, unsigned int value ); } 406 AUE_SEMUNLINK NOSTD { int ksem_unlink( _In_z_ const char *name ); } 407 AUE_SEMGETVALUE NOSTD { int ksem_getvalue( semid_t id, _Out_ int *val ); } 408 AUE_SEMDESTROY NOSTD { int ksem_destroy( semid_t id ); } 409 AUE_NULL STD { int __mac_get_pid( pid_t pid, _In_ _Contains_long_ptr_ struct mac *mac_p ); } 410 AUE_NULL STD { int __mac_get_link( _In_z_ const char *path_p, _In_ _Contains_long_ptr_ struct mac *mac_p ); } 411 AUE_NULL STD { int __mac_set_link( _In_z_ const char *path_p, _In_ _Contains_long_ptr_ struct mac *mac_p ); } 412 AUE_EXTATTR_SET_LINK STD { ssize_t extattr_set_link( _In_z_ const char *path, int attrnamespace, _In_z_ const char *attrname, _In_reads_bytes_(nbytes) void *data, size_t nbytes ); } 413 AUE_EXTATTR_GET_LINK STD { ssize_t extattr_get_link( _In_z_ const char *path, int attrnamespace, _In_z_ const char *attrname, _Out_writes_bytes_(nbytes) void *data, size_t nbytes ); } 414 AUE_EXTATTR_DELETE_LINK STD { int extattr_delete_link( _In_z_ const char *path, int attrnamespace, _In_z_ const char *attrname ); } 415 AUE_NULL STD { int __mac_execve( _In_z_ const char *fname, _In_ char **argv, _In_ char **envv, _In_ _Contains_long_ptr_ struct mac *mac_p ); } 416 AUE_SIGACTION STD|CAPENABLED { int sigaction( int sig, _In_opt_ _Contains_ptr_ const struct sigaction *act, _Out_opt_ _Contains_ptr_ struct sigaction *oact ); } 417 AUE_SIGRETURN STD|CAPENABLED { int sigreturn( _In_ _Contains_long_ptr_ const struct __ucontext *sigcntxp ); } 418-420 AUE_NULL RESERVED 421 AUE_NULL STD|CAPENABLED { int getcontext( _Out_ _Contains_long_ptr_ struct __ucontext *ucp ); } 422 AUE_NULL STD|CAPENABLED { int setcontext( _In_ _Contains_long_ptr_ const struct __ucontext *ucp ); } 423 AUE_NULL STD { int swapcontext( _Out_ _Contains_long_ptr_ struct __ucontext *oucp, _In_ _Contains_long_ptr_ const struct __ucontext *ucp ); } 424 AUE_SWAPOFF COMPAT13 { int swapoff( _In_z_ const char *name ); } 425 AUE_ACL_GET_LINK STD { int __acl_get_link( _In_z_ const char *path, __acl_type_t type, _Out_ struct acl *aclp ); } 426 AUE_ACL_SET_LINK STD { int __acl_set_link( _In_z_ const char *path, __acl_type_t type, _In_ struct acl *aclp ); } 427 AUE_ACL_DELETE_LINK STD { int __acl_delete_link( _In_z_ const char *path, __acl_type_t type ); } 428 AUE_ACL_CHECK_LINK STD { int __acl_aclcheck_link( _In_z_ const char *path, __acl_type_t type, _In_ struct acl *aclp ); } 429 AUE_SIGWAIT STD|CAPENABLED { int sigwait( _In_ const sigset_t *set, _Out_ int *sig ); } 430 AUE_THR_CREATE STD|CAPENABLED { int thr_create( _In_ _Contains_long_ptr_ ucontext_t *ctx, _Out_ long *id, int flags ); } 431 AUE_THR_EXIT STD|CAPENABLED { void thr_exit( _Out_opt_ long *state ); } 432 AUE_NULL STD|CAPENABLED { int thr_self( _Out_ long *id ); } 433 AUE_THR_KILL STD|CAPENABLED { int thr_kill( long id, int sig ); } 434 AUE_NULL COMPAT10 { int _umtx_lock( _Inout_ struct umtx *umtx ); } 435 AUE_NULL COMPAT10 { int _umtx_unlock( _Inout_ struct umtx *umtx ); } 436 AUE_JAIL_ATTACH STD { int jail_attach( int jid ); } 437 AUE_EXTATTR_LIST_FD STD|CAPENABLED { ssize_t extattr_list_fd( int fd, int attrnamespace, _Out_writes_bytes_opt_(nbytes) void *data, size_t nbytes ); } 438 AUE_EXTATTR_LIST_FILE STD { ssize_t extattr_list_file( _In_z_ const char *path, int attrnamespace, _Out_writes_bytes_opt_(nbytes) void *data, size_t nbytes ); } 439 AUE_EXTATTR_LIST_LINK STD { ssize_t extattr_list_link( _In_z_ const char *path, int attrnamespace, _Out_writes_bytes_opt_(nbytes) void *data, size_t nbytes ); } 440 AUE_NULL OBSOL kse_switchin 441 AUE_SEMWAIT NOSTD { int ksem_timedwait( semid_t id, _In_opt_ _Contains_long_timet_ const struct timespec *abstime ); } 442 AUE_NULL STD|CAPENABLED { int thr_suspend( _In_opt_ _Contains_long_timet_ const struct timespec *timeout ); } 443 AUE_NULL STD|CAPENABLED { int thr_wake( long id ); } 444 AUE_MODUNLOAD STD { int kldunloadf( int fileid, int flags ); } 445 AUE_AUDIT STD { int audit( _In_reads_bytes_(length) const void *record, u_int length ); } 446 AUE_AUDITON STD { int auditon( int cmd, _In_opt_ void *data, u_int length ); } 447 AUE_GETAUID STD|CAPENABLED { int getauid( _Out_ uid_t *auid ); } 448 AUE_SETAUID STD|CAPENABLED { int setauid( _In_ uid_t *auid ); } 449 AUE_GETAUDIT STD|CAPENABLED { int getaudit( _Out_ struct auditinfo *auditinfo ); } 450 AUE_SETAUDIT STD|CAPENABLED { int setaudit( _In_ struct auditinfo *auditinfo ); } 451 AUE_GETAUDIT_ADDR STD|CAPENABLED { int getaudit_addr( _Out_writes_bytes_(length) struct auditinfo_addr *auditinfo_addr, u_int length ); } 452 AUE_SETAUDIT_ADDR STD|CAPENABLED { int setaudit_addr( _In_reads_bytes_(length) struct auditinfo_addr *auditinfo_addr, u_int length ); } 453 AUE_AUDITCTL STD { int auditctl( _In_z_ const char *path ); } 454 AUE_NULL STD|CAPENABLED { int _umtx_op( _Inout_ void *obj, int op, u_long val, _In_ void *uaddr1, _In_ void *uaddr2 ); } 455 AUE_THR_NEW STD|CAPENABLED { int thr_new( _In_ _Contains_long_ptr_ struct thr_param *param, int param_size ); } 456 AUE_NULL STD|CAPENABLED { int sigqueue( pid_t pid, int signum, _In_ void *value ); } 457 AUE_MQ_OPEN NOSTD { int kmq_open( _In_z_ const char *path, int flags, mode_t mode, _In_opt_ _Contains_long_ const struct mq_attr *attr ); } 458 AUE_MQ_SETATTR NOSTD|CAPENABLED { int kmq_setattr( int mqd, _In_opt_ _Contains_long_ const struct mq_attr *attr, _Out_opt_ _Contains_long_ struct mq_attr *oattr ); } 459 AUE_MQ_TIMEDRECEIVE NOSTD|CAPENABLED { int kmq_timedreceive( int mqd, _Out_writes_bytes_(msg_len) char *msg_ptr, size_t msg_len, _Out_opt_ unsigned *msg_prio, _In_opt_ _Contains_long_timet_ const struct timespec *abs_timeout ); } 460 AUE_MQ_TIMEDSEND NOSTD|CAPENABLED { int kmq_timedsend( int mqd, _In_reads_bytes_(msg_len) const char *msg_ptr, size_t msg_len, unsigned msg_prio, _In_opt_ _Contains_long_timet_ const struct timespec *abs_timeout ); } 461 AUE_MQ_NOTIFY NOSTD|CAPENABLED { int kmq_notify( int mqd, _In_opt_ _Contains_long_ptr_ const struct sigevent *sigev ); } 462 AUE_MQ_UNLINK NOSTD { int kmq_unlink( _In_z_ const char *path ); } 463 AUE_NULL STD|CAPENABLED { void abort2( _In_z_ const char *why, int nargs, _In_reads_(nargs) void **args ); } 464 AUE_NULL STD|CAPENABLED { int thr_set_name( long id, _In_z_ const char *name ); } 465 AUE_AIO_FSYNC STD|CAPENABLED { int aio_fsync( int op, _In_ _Contains_long_ptr_ struct aiocb *aiocbp ); } 466 AUE_RTPRIO STD|CAPENABLED { int rtprio_thread( int function, lwpid_t lwpid, _Inout_ struct rtprio *rtp ); } 467-470 AUE_NULL RESERVED 471 AUE_SCTP_PEELOFF NOSTD|CAPENABLED { int sctp_peeloff( int sd, uint32_t name ); } 472 AUE_SCTP_GENERIC_SENDMSG NOSTD|CAPENABLED { int sctp_generic_sendmsg( int sd, _In_reads_bytes_(mlen) void *msg, int mlen, _In_reads_bytes_(tolen) const struct sockaddr *to, __socklen_t tolen, _In_opt_ struct sctp_sndrcvinfo *sinfo, int flags ); } 473 AUE_SCTP_GENERIC_SENDMSG_IOV NOSTD|CAPENABLED { int sctp_generic_sendmsg_iov( int sd, _In_reads_(iovlen) _Contains_long_ptr_ struct iovec *iov, int iovlen, _In_reads_bytes_(tolen) const struct sockaddr *to, __socklen_t tolen, _In_opt_ struct sctp_sndrcvinfo *sinfo, int flags ); } 474 AUE_SCTP_GENERIC_RECVMSG NOSTD|CAPENABLED { int sctp_generic_recvmsg( int sd, _In_reads_(iovlen) _Contains_long_ptr_ struct iovec *iov, int iovlen, _Out_writes_bytes_(*fromlenaddr) struct sockaddr *from, _Out_ __socklen_t *fromlenaddr, _In_opt_ struct sctp_sndrcvinfo *sinfo, _Out_opt_ int *msg_flags ); } 475 AUE_PREAD STD|CAPENABLED { ssize_t pread( int fd, _Out_writes_bytes_(nbyte) void *buf, size_t nbyte, off_t offset ); } 476 AUE_PWRITE STD|CAPENABLED { ssize_t pwrite( int fd, _In_reads_bytes_(nbyte) const void *buf, size_t nbyte, off_t offset ); } 477 AUE_MMAP STD|CAPENABLED { void *mmap( _In_ void *addr, size_t len, int prot, int flags, int fd, off_t pos ); } 478 AUE_LSEEK STD|CAPENABLED { off_t lseek( int fd, off_t offset, int whence ); } 479 AUE_TRUNCATE STD { int truncate( _In_z_ const char *path, off_t length ); } 480 AUE_FTRUNCATE STD|CAPENABLED { int ftruncate( int fd, off_t length ); } 481 AUE_THR_KILL2 STD { int thr_kill2( pid_t pid, long id, int sig ); } 482 AUE_SHMOPEN COMPAT12|CAPENABLED { int shm_open( _In_z_ const char *path, int flags, mode_t mode ); } 483 AUE_SHMUNLINK STD { int shm_unlink( _In_z_ const char *path ); } 484 AUE_NULL STD { int cpuset( _Out_ cpusetid_t *setid ); } 485 AUE_NULL STD { int cpuset_setid( cpuwhich_t which, id_t id, cpusetid_t setid ); } 486 AUE_NULL STD { int cpuset_getid( cpulevel_t level, cpuwhich_t which, id_t id, _Out_ cpusetid_t *setid ); } 487 AUE_NULL STD|CAPENABLED { int cpuset_getaffinity( cpulevel_t level, cpuwhich_t which, id_t id, size_t cpusetsize, _Out_ cpuset_t *mask ); } 488 AUE_NULL STD|CAPENABLED { int cpuset_setaffinity( cpulevel_t level, cpuwhich_t which, id_t id, size_t cpusetsize, _Out_ const cpuset_t *mask ); } 489 AUE_FACCESSAT STD|CAPENABLED { int faccessat( int fd, _In_z_ const char *path, int amode, int flag ); } 490 AUE_FCHMODAT STD|CAPENABLED { int fchmodat( int fd, _In_z_ const char *path, mode_t mode, int flag ); } 491 AUE_FCHOWNAT STD|CAPENABLED { int fchownat( int fd, _In_z_ const char *path, uid_t uid, gid_t gid, int flag ); } 492 AUE_FEXECVE STD|CAPENABLED { int fexecve( int fd, _In_ char **argv, _In_ char **envv ); } 493 AUE_FSTATAT COMPAT11|CAPENABLED { int fstatat( int fd, _In_z_ const char *path, _Out_ _Contains_long_timet_ struct freebsd11_stat *buf, int flag ); } 494 AUE_FUTIMESAT STD|CAPENABLED { int futimesat( int fd, _In_z_ const char *path, _In_reads_(2) _Contains_long_timet_ const struct timeval *times ); } 495 AUE_LINKAT STD|CAPENABLED { int linkat( int fd1, _In_z_ const char *path1, int fd2, _In_z_ const char *path2, int flag ); } 496 AUE_MKDIRAT STD|CAPENABLED { int mkdirat( int fd, _In_z_ const char *path, mode_t mode ); } 497 AUE_MKFIFOAT STD|CAPENABLED { int mkfifoat( int fd, _In_z_ const char *path, mode_t mode ); } 498 AUE_MKNODAT COMPAT11|CAPENABLED { int mknodat( int fd, _In_z_ const char *path, mode_t mode, uint32_t dev ); } ; XXX: see the comment for open 499 AUE_OPENAT_RWTC STD|CAPENABLED { int openat( int fd, _In_z_ const char *path, int flag, mode_t mode ); } 500 AUE_READLINKAT STD|CAPENABLED { ssize_t readlinkat( int fd, _In_z_ const char *path, _Out_writes_bytes_(bufsize) char *buf, size_t bufsize ); } 501 AUE_RENAMEAT STD|CAPENABLED { int renameat( int oldfd, _In_z_ const char *old, int newfd, _In_z_ const char *new ); } 502 AUE_SYMLINKAT STD|CAPENABLED { int symlinkat( _In_z_ const char *path1, int fd, _In_z_ const char *path2 ); } 503 AUE_UNLINKAT STD|CAPENABLED { int unlinkat( int fd, _In_z_ const char *path, int flag ); } 504 AUE_POSIX_OPENPT STD { int posix_openpt( int flags ); } 505 AUE_NULL OBSOL kgssapi 506 AUE_JAIL_GET STD { int jail_get( _In_reads_(iovcnt) _Contains_long_ptr_ struct iovec *iovp, unsigned int iovcnt, int flags ); } 507 AUE_JAIL_SET STD { int jail_set( _In_reads_(iovcnt) _Contains_long_ptr_ struct iovec *iovp, unsigned int iovcnt, int flags ); } 508 AUE_JAIL_REMOVE STD { int jail_remove( int jid ); } 509 AUE_CLOSEFROM COMPAT12|CAPENABLED { int closefrom( int lowfd ); } 510 AUE_SEMCTL NOSTD { int __semctl( int semid, int semnum, int cmd, _Inout_ _Contains_ptr_ union semun *arg ); } 511 AUE_MSGCTL NOSTD { int msgctl( int msqid, int cmd, _Inout_opt_ _Contains_long_ptr_ struct msqid_ds *buf ); } 512 AUE_SHMCTL NOSTD { int shmctl( int shmid, int cmd, _Inout_opt_ _Contains_long_ struct shmid_ds *buf ); } 513 AUE_LPATHCONF STD { int lpathconf( _In_z_ const char *path, int name ); } 514 AUE_NULL OBSOL cap_new 515 AUE_CAP_RIGHTS_GET STD|CAPENABLED { int __cap_rights_get( int version, int fd, _Out_ cap_rights_t *rightsp ); } 516 AUE_CAP_ENTER STD|CAPENABLED { int cap_enter(void); } 517 AUE_CAP_GETMODE STD|CAPENABLED { int cap_getmode( _Out_ u_int *modep ); } 518 AUE_PDFORK STD|CAPENABLED { int pdfork( _Out_ int *fdp, int flags ); } 519 AUE_PDKILL STD|CAPENABLED { int pdkill( int fd, int signum ); } 520 AUE_PDGETPID STD|CAPENABLED { int pdgetpid( int fd, _Out_ pid_t *pidp ); } 521 AUE_NULL RESERVED 522 AUE_SELECT STD|CAPENABLED { int pselect( int nd, _Inout_opt_ fd_set *in, _Inout_opt_ fd_set *ou, _Inout_opt_ fd_set *ex, _In_opt_ _Contains_long_timet_ const struct timespec *ts, _In_opt_ const sigset_t *sm ); } 523 AUE_GETLOGINCLASS STD|CAPENABLED { int getloginclass( _Out_writes_z_(namelen) char *namebuf, size_t namelen ); } 524 AUE_SETLOGINCLASS STD { int setloginclass( _In_z_ const char *namebuf ); } 525 AUE_NULL STD { int rctl_get_racct( _In_reads_bytes_(inbuflen) const void *inbufp, size_t inbuflen, _Out_writes_bytes_(outbuflen) void *outbufp, size_t outbuflen ); } 526 AUE_NULL STD { int rctl_get_rules( _In_reads_bytes_(inbuflen) const void *inbufp, size_t inbuflen, _Out_writes_bytes_(outbuflen) void *outbufp, size_t outbuflen ); } 527 AUE_NULL STD { int rctl_get_limits( _In_reads_bytes_(inbuflen) const void *inbufp, size_t inbuflen, _Out_writes_bytes_(outbuflen) void *outbufp, size_t outbuflen ); } 528 AUE_NULL STD { int rctl_add_rule( _In_reads_bytes_(inbuflen) const void *inbufp, size_t inbuflen, _Out_writes_bytes_(outbuflen) void *outbufp, size_t outbuflen ); } 529 AUE_NULL STD { int rctl_remove_rule( _In_reads_bytes_(inbuflen) const void *inbufp, size_t inbuflen, _Out_writes_bytes_(outbuflen) void *outbufp, size_t outbuflen ); } 530 AUE_POSIX_FALLOCATE STD|CAPENABLED { int posix_fallocate( int fd, off_t offset, off_t len ); } 531 AUE_POSIX_FADVISE STD|CAPENABLED { int posix_fadvise( int fd, off_t offset, off_t len, int advice ); } 532 AUE_WAIT6 STD { int wait6( idtype_t idtype, id_t id, _Out_opt_ int *status, int options, _Out_opt_ _Contains_long_ struct __wrusage *wrusage, _Out_opt_ _Contains_long_ptr_ struct __siginfo *info ); } 533 AUE_CAP_RIGHTS_LIMIT STD|CAPENABLED { int cap_rights_limit( int fd, _In_ cap_rights_t *rightsp ); } 534 AUE_CAP_IOCTLS_LIMIT STD|CAPENABLED { int cap_ioctls_limit( int fd, _In_reads_(ncmds) const u_long *cmds, size_t ncmds ); } 535 AUE_CAP_IOCTLS_GET STD|CAPENABLED { ssize_t cap_ioctls_get( int fd, _Out_writes_(maxcmds) u_long *cmds, size_t maxcmds ); } 536 AUE_CAP_FCNTLS_LIMIT STD|CAPENABLED { int cap_fcntls_limit( int fd, uint32_t fcntlrights ); } 537 AUE_CAP_FCNTLS_GET STD|CAPENABLED { int cap_fcntls_get( int fd, _Out_ uint32_t *fcntlrightsp ); } 538 AUE_BINDAT STD|CAPENABLED { int bindat( int fd, int s, _In_reads_bytes_(namelen) const struct sockaddr *name, __socklen_t namelen ); } 539 AUE_CONNECTAT STD|CAPENABLED { int connectat( int fd, int s, _In_reads_bytes_(namelen) const struct sockaddr *name, __socklen_t namelen ); } 540 AUE_CHFLAGSAT STD|CAPENABLED { int chflagsat( int fd, _In_z_ const char *path, u_long flags, int atflag ); } 541 AUE_ACCEPT STD|CAPENABLED { int accept4( int s, _Out_writes_bytes_opt_(*anamelen) struct sockaddr *name, _Inout_opt_ __socklen_t *anamelen, int flags ); } 542 AUE_PIPE STD|CAPENABLED { int pipe2( _Out_writes_(2) int *fildes, int flags ); } 543 AUE_AIO_MLOCK STD { int aio_mlock( _In_ _Contains_long_ptr_ struct aiocb *aiocbp ); } 544 AUE_PROCCTL STD { int procctl( idtype_t idtype, id_t id, int com, _In_opt_ void *data ); } 545 AUE_POLL STD|CAPENABLED { int ppoll( _Inout_updates_(nfds) struct pollfd *fds, u_int nfds, _In_opt_ _Contains_long_timet_ const struct timespec *ts, _In_opt_ const sigset_t *set ); } 546 AUE_FUTIMES STD|CAPENABLED { int futimens( int fd, _In_reads_(2) _Contains_long_timet_ const struct timespec *times ); } 547 AUE_FUTIMESAT STD|CAPENABLED { int utimensat( int fd, _In_z_ const char *path, _In_reads_(2) _Contains_long_timet_ const struct timespec *times, int flag ); } 548 AUE_NULL OBSOL numa_getaffinity 549 AUE_NULL OBSOL numa_setaffinity 550 AUE_FSYNC STD|CAPENABLED { int fdatasync( int fd ); } 551 AUE_FSTAT STD|CAPENABLED { int fstat( int fd, _Out_ _Contains_long_timet_ struct stat *sb ); } 552 AUE_FSTATAT STD|CAPENABLED { int fstatat( int fd, _In_z_ const char *path, _Out_ _Contains_long_timet_ struct stat *buf, int flag ); } 553 AUE_FHSTAT STD { int fhstat( _In_ const struct fhandle *u_fhp, _Out_ _Contains_long_timet_ struct stat *sb ); } 554 AUE_GETDIRENTRIES STD|CAPENABLED { ssize_t getdirentries( int fd, _Out_writes_bytes_(count) char *buf, size_t count, _Out_opt_ off_t *basep ); } 555 AUE_STATFS STD { int statfs( _In_z_ const char *path, _Out_ struct statfs *buf ); } 556 AUE_FSTATFS STD|CAPENABLED { int fstatfs( int fd, _Out_ struct statfs *buf ); } 557 AUE_GETFSSTAT STD { int getfsstat( _Out_writes_bytes_opt_(bufsize) struct statfs *buf, long bufsize, int mode ); } 558 AUE_FHSTATFS STD { int fhstatfs( _In_ const struct fhandle *u_fhp, _Out_ struct statfs *buf ); } 559 AUE_MKNODAT STD|CAPENABLED { int mknodat( int fd, _In_z_ const char *path, mode_t mode, dev_t dev ); } 560 AUE_KEVENT STD|CAPENABLED { int kevent( int fd, _In_reads_opt_(nchanges) _Contains_ptr_ const struct kevent *changelist, int nchanges, _Out_writes_opt_(nevents) _Contains_ptr_ struct kevent *eventlist, int nevents, _In_opt_ _Contains_long_timet_ const struct timespec *timeout ); } 561 AUE_NULL STD|CAPENABLED { int cpuset_getdomain( cpulevel_t level, cpuwhich_t which, id_t id, size_t domainsetsize, _Out_writes_bytes_(domainsetsize) domainset_t *mask, _Out_ int *policy ); } 562 AUE_NULL STD|CAPENABLED { int cpuset_setdomain( cpulevel_t level, cpuwhich_t which, id_t id, size_t domainsetsize, _In_ domainset_t *mask, int policy ); } 563 AUE_NULL STD|CAPENABLED { int getrandom( _Out_writes_bytes_(buflen) void *buf, size_t buflen, unsigned int flags ); } 564 AUE_NULL STD { int getfhat( int fd, _In_z_ char *path, _Out_ struct fhandle *fhp, int flags ); } 565 AUE_NULL STD { int fhlink( _In_ struct fhandle *fhp, _In_z_ const char *to ); } 566 AUE_NULL STD { int fhlinkat( _In_ struct fhandle *fhp, int tofd, _In_z_ const char *to, ); } 567 AUE_NULL STD { int fhreadlink( _In_ struct fhandle *fhp, _Out_writes_(bufsize) char *buf, size_t bufsize ); } 568 AUE_UNLINKAT STD|CAPENABLED { int funlinkat( int dfd, _In_z_ const char *path, int fd, int flag ); } 569 AUE_NULL STD|CAPENABLED { ssize_t copy_file_range( int infd, _Inout_opt_ off_t *inoffp, int outfd, _Inout_opt_ off_t *outoffp, size_t len, unsigned int flags ); } 570 AUE_SYSCTL STD|CAPENABLED { int __sysctlbyname( _In_reads_(namelen) const char *name, size_t namelen, _Out_writes_bytes_opt_(*oldlenp) void *old, _Inout_opt_ size_t *oldlenp, _In_reads_bytes_opt_(newlen) void *new, size_t newlen ); } 571 AUE_SHMOPEN STD|CAPENABLED { int shm_open2( _In_z_ const char *path, int flags, mode_t mode, int shmflags, _In_z_ const char *name ); } 572 AUE_SHMRENAME STD { int shm_rename( _In_z_ const char *path_from, _In_z_ const char *path_to, int flags ); } 573 AUE_NULL STD|CAPENABLED { int sigfastblock( int cmd, _Inout_updates_bytes_opt_(4) void *ptr ); } 574 AUE_REALPATHAT STD { int __realpathat( int fd, _In_z_ const char *path, _Out_writes_z_(size) char *buf, size_t size, int flags ); } 575 AUE_CLOSERANGE STD|CAPENABLED { int close_range( u_int lowfd, u_int highfd, int flags ); } ; 576 is initialised by the krpc code, if present. 576 AUE_NULL NOSTD { int rpctls_syscall( uint64_t socookie ); } 577 AUE_SPECIALFD STD|CAPENABLED { int __specialfd( int type, _In_reads_bytes_(len) const void *req, size_t len ); } 578 AUE_AIO_WRITEV STD|CAPENABLED { int aio_writev( _Inout_ _Contains_long_ptr_ struct aiocb *aiocbp ); } 579 AUE_AIO_READV STD|CAPENABLED { int aio_readv( _Inout_ _Contains_long_ptr_ struct aiocb *aiocbp ); } 580 AUE_FSPACECTL STD|CAPENABLED { int fspacectl( int fd, int cmd, _In_ const struct spacectl_range *rqsr, int flags, _Out_opt_ struct spacectl_range *rmsr, ); } 581 AUE_NULL STD|CAPENABLED { int sched_getcpu(void); } 582 AUE_SWAPOFF STD { int swapoff( _In_z_ const char *name, u_int flags, ); } 583 AUE_KQUEUE STD|CAPENABLED { int kqueuex( u_int flags ); } 584 AUE_NULL STD|CAPENABLED { int membarrier( int cmd, unsigned flags, int cpu_id ); } 585 AUE_TIMERFD STD|CAPENABLED { int timerfd_create( int clockid, int flags ); } 586 AUE_TIMERFD STD|CAPENABLED { int timerfd_gettime( int fd, _Out_ _Contains_long_timet_ struct itimerspec *curr_value ); } 587 AUE_TIMERFD STD|CAPENABLED { int timerfd_settime( int fd, int flags, _In_ _Contains_long_timet_ const struct itimerspec *new_value, _Out_opt_ _Contains_long_timet_ struct itimerspec *old_value ); } 588 AUE_NULL STD { int kcmp( pid_t pid1, pid_t pid2, int type, uintptr_t idx1, uintptr_t idx2 ); } 589 AUE_NULL STD|CAPENABLED { int getrlimitusage( u_int which, int flags, _Out_ rlim_t *res ); } 590 AUE_NULL STD { int fchroot( int fd ); } 591 AUE_SETCRED STD|CAPENABLED { int setcred( u_int flags, _In_reads_bytes_(size) _Contains_ptr_ const struct setcred *wcred, size_t size ); } 592 AUE_NULL STD|CAPENABLED { int exterrctl( u_int op, u_int flags, _In_reads_bytes_(4) void *ptr ); } 593 AUE_INOTIFY STD|CAPENABLED { int inotify_add_watch_at( int fd, int dfd, _In_z_ const char *path, uint32_t mask ); } 594 AUE_INOTIFY STD|CAPENABLED { int inotify_rm_watch( int fd, int wd ); } +595 AUE_GETGROUPS STD|CAPENABLED { + int getgroups( + int gidsetsize, + _Out_writes_opt_(gidsetsize) gid_t *gidset + ); + } +596 AUE_SETGROUPS STD { + int setgroups( + int gidsetsize, + _In_reads_(gidsetsize) const gid_t *gidset + ); + } ; vim: syntax=off diff --git a/usr.bin/newgrp/newgrp.c b/usr.bin/newgrp/newgrp.c index f1da1c8cb1f5..0971f4d13b49 100644 --- a/usr.bin/newgrp/newgrp.c +++ b/usr.bin/newgrp/newgrp.c @@ -1,309 +1,313 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2002 Tim J. Robbins. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * newgrp -- change to a new group */ #include #include #include #include #include #include #include #include #include #include #include #include static void addgroup(const char *grpname); static void doshell(void); static int inarray(gid_t, const gid_t[], int); static void loginshell(void); static void restoregrps(void); static void usage(void); static struct passwd *pwd; static uid_t euid; extern char **environ; /* Manipulate effective user ID. */ #define PRIV_START do { \ if (seteuid(euid) < 0) \ err(1, "seteuid"); \ } while (0) #define PRIV_END do { \ if (seteuid(getuid()) < 0) \ err(1, "seteuid"); \ } while (0) int main(int argc, char *argv[]) { int ch, login; if ((euid = geteuid()) != 0) warnx("need root permissions to function properly, check setuid bit"); if (seteuid(getuid()) < 0) err(1, "seteuid"); if ((pwd = getpwuid(getuid())) == NULL) errx(1, "unknown user"); login = 0; while ((ch = getopt(argc, argv, "-l")) != -1) { switch (ch) { case '-': /* Obsolescent */ case 'l': login = 1; break; default: usage(); } } argc -= optind; argv += optind; switch (argc) { case 0: restoregrps(); break; case 1: addgroup(*argv); break; default: usage(); } if (seteuid(euid) < 0) err(1, "seteuid"); if (setuid(getuid()) < 0) err(1, "setuid"); if (login) loginshell(); else doshell(); /*NOTREACHED*/ exit(1); } static void usage(void) { fprintf(stderr, "usage: newgrp [-l] [group]\n"); exit(1); } static void restoregrps(void) { int initres, setres; PRIV_START; initres = initgroups(pwd->pw_name, pwd->pw_gid); setres = setgid(pwd->pw_gid); PRIV_END; if (initres < 0) warn("initgroups"); if (setres < 0) warn("setgid"); } static void addgroup(const char *grpname) { gid_t *grps; long lgid, ngrps_max; int dbmember, i, ngrps; gid_t egid; struct group *grp; char *ep, *pass, *cryptpw; char **p; egid = getegid(); /* Try it as a group name, then a group id. */ if ((grp = getgrnam(grpname)) == NULL) if ((lgid = strtol(grpname, &ep, 10)) <= 0 || *ep != '\0' || (grp = getgrgid((gid_t)lgid)) == NULL ) { warnx("%s: bad group name", grpname); return; } /* * If the user is not a member of the requested group and the group * has a password, prompt and check it. */ dbmember = 0; if (pwd->pw_gid == grp->gr_gid) dbmember = 1; for (p = grp->gr_mem; *p != NULL; p++) if (strcmp(*p, pwd->pw_name) == 0) { dbmember = 1; break; } if (!dbmember && *grp->gr_passwd != '\0' && getuid() != 0) { pass = getpass("Password:"); if (pass == NULL) return; cryptpw = crypt(pass, grp->gr_passwd); if (cryptpw == NULL || strcmp(grp->gr_passwd, cryptpw) != 0) { fprintf(stderr, "Sorry\n"); return; } } - ngrps_max = sysconf(_SC_NGROUPS_MAX) + 1; + ngrps_max = sysconf(_SC_NGROUPS_MAX); if ((grps = malloc(sizeof(gid_t) * ngrps_max)) == NULL) err(1, "malloc"); if ((ngrps = getgroups(ngrps_max, (gid_t *)grps)) < 0) { warn("getgroups"); goto end; } - /* Remove requested gid from supp. list if it exists. */ + /* + * Remove requested gid from supp. list if it exists and doesn't match + * our prior egid -- this exception is to avoid providing the user a + * means to get rid of a group that could be used for, e.g., negative + * permissions. + */ if (grp->gr_gid != egid && inarray(grp->gr_gid, grps, ngrps)) { for (i = 0; i < ngrps; i++) if (grps[i] == grp->gr_gid) break; ngrps--; memmove(&grps[i], &grps[i + 1], (ngrps - i) * sizeof(gid_t)); PRIV_START; if (setgroups(ngrps, (const gid_t *)grps) < 0) { PRIV_END; warn("setgroups"); goto end; } PRIV_END; } PRIV_START; if (setgid(grp->gr_gid)) { PRIV_END; warn("setgid"); goto end; } PRIV_END; - grps[0] = grp->gr_gid; /* Add old effective gid to supp. list if it does not exist. */ - if (egid != grp->gr_gid && !inarray(egid, grps, ngrps)) { + if (!inarray(egid, grps, ngrps)) { if (ngrps == ngrps_max) warnx("too many groups"); else { grps[ngrps++] = egid; PRIV_START; if (setgroups(ngrps, (const gid_t *)grps)) { PRIV_END; warn("setgroups"); goto end; } PRIV_END; } } end: free(grps); } static int inarray(gid_t gid, const gid_t grps[], int ngrps) { int i; for (i = 0; i < ngrps; i++) if (grps[i] == gid) return (1); return (0); } /* * Set the environment to what would be expected if the user logged in * again; this performs the same steps as su(1)'s -l option. */ static void loginshell(void) { char *args[2], **cleanenv, *term, *ticket; const char *shell; login_cap_t *lc; shell = pwd->pw_shell; if (*shell == '\0') shell = _PATH_BSHELL; if (chdir(pwd->pw_dir) < 0) { warn("%s", pwd->pw_dir); chdir("/"); } term = getenv("TERM"); ticket = getenv("KRBTKFILE"); if ((cleanenv = calloc(20, sizeof(char *))) == NULL) err(1, "calloc"); *cleanenv = NULL; environ = cleanenv; lc = login_getpwclass(pwd); setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV); login_close(lc); setenv("USER", pwd->pw_name, 1); setenv("SHELL", shell, 1); setenv("HOME", pwd->pw_dir, 1); if (term != NULL) setenv("TERM", term, 1); if (ticket != NULL) setenv("KRBTKFILE", ticket, 1); if (asprintf(args, "-%s", shell) < 0) err(1, "asprintf"); args[1] = NULL; execv(shell, args); err(1, "%s", shell); } static void doshell(void) { const char *shell; shell = pwd->pw_shell; if (*shell == '\0') shell = _PATH_BSHELL; execl(shell, shell, (char *)NULL); err(1, "%s", shell); } diff --git a/usr.bin/quota/quota.c b/usr.bin/quota/quota.c index b5d28fd7c184..9ad4076cec40 100644 --- a/usr.bin/quota/quota.c +++ b/usr.bin/quota/quota.c @@ -1,687 +1,690 @@ /* * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Robert Elz at The University of Melbourne. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Disk quota reporting program. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *qfextension[] = INITQFNAMES; struct quotause { struct quotause *next; long flags; struct dqblk dqblk; char fsname[MAXPATHLEN + 1]; }; static char *timeprt(int64_t seconds); static struct quotause *getprivs(long id, int quotatype); static void usage(void) __dead2; static int showuid(u_long uid); static int showgid(u_long gid); static int showusrname(char *name); static int showgrpname(char *name); static int showquotas(int type, u_long id, const char *name); static void showrawquotas(int type, u_long id, struct quotause *qup); static void heading(int type, u_long id, const char *name, const char *tag); static int getufsquota(struct fstab *fs, struct quotause *qup, long id, int quotatype); static int getnfsquota(struct statfs *fst, struct quotause *qup, long id, int quotatype); static enum clnt_stat callaurpc(char *host, int prognum, int versnum, int procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out); static int alldigits(char *s); static int hflag; static int lflag; static int rflag; static int qflag; static int vflag; static char *filename = NULL; int main(int argc, char *argv[]) { - int ngroups; - gid_t mygid, gidset[NGROUPS]; + int ngroups; int i, ch, gflag = 0, uflag = 0, errflag = 0; while ((ch = getopt(argc, argv, "f:ghlrquv")) != -1) { switch(ch) { case 'f': filename = optarg; break; case 'g': gflag++; break; case 'h': hflag++; break; case 'l': lflag++; break; case 'q': qflag++; break; case 'r': rflag++; break; case 'u': uflag++; break; case 'v': vflag++; break; default: usage(); } } argc -= optind; argv += optind; if (!uflag && !gflag) uflag++; if (argc == 0) { if (uflag) errflag += showuid(getuid()); if (gflag) { + gid_t mygid, myegid, gidset[NGROUPS_MAX]; + mygid = getgid(); - ngroups = getgroups(NGROUPS, gidset); + errflag += showgid(mygid); + myegid = getegid(); + errflag += showgid(myegid); + ngroups = getgroups(NGROUPS_MAX, gidset); if (ngroups < 0) err(1, "getgroups"); - errflag += showgid(mygid); for (i = 0; i < ngroups; i++) if (gidset[i] != mygid) errflag += showgid(gidset[i]); } return(errflag); } if (uflag && gflag) usage(); if (uflag) { for (; argc > 0; argc--, argv++) { if (alldigits(*argv)) errflag += showuid(atoi(*argv)); else errflag += showusrname(*argv); } return(errflag); } if (gflag) { for (; argc > 0; argc--, argv++) { if (alldigits(*argv)) errflag += showgid(atoi(*argv)); else errflag += showgrpname(*argv); } } return(errflag); } static void usage(void) { fprintf(stderr, "%s\n%s\n%s\n", "usage: quota [-ghlu] [-f path] [-v | -q | -r]", " quota [-hlu] [-f path] [-v | -q | -r] user ...", " quota -g [-hl] [-f path] [-v | -q | -r] group ..."); exit(1); } /* * Print out quotas for a specified user identifier. */ static int showuid(u_long uid) { struct passwd *pwd = getpwuid(uid); const char *name; if (pwd == NULL) name = "(no account)"; else name = pwd->pw_name; return(showquotas(USRQUOTA, uid, name)); } /* * Print out quotas for a specified user name. */ static int showusrname(char *name) { struct passwd *pwd = getpwnam(name); if (pwd == NULL) { warnx("%s: unknown user", name); return(1); } return(showquotas(USRQUOTA, pwd->pw_uid, name)); } /* * Print out quotas for a specified group identifier. */ static int showgid(u_long gid) { struct group *grp = getgrgid(gid); const char *name; if (grp == NULL) name = "(no entry)"; else name = grp->gr_name; return(showquotas(GRPQUOTA, gid, name)); } /* * Print out quotas for a specified group name. */ static int showgrpname(char *name) { struct group *grp = getgrnam(name); if (grp == NULL) { warnx("%s: unknown group", name); return(1); } return(showquotas(GRPQUOTA, grp->gr_gid, name)); } static void prthumanval(int len, u_int64_t bytes) { char buf[len + 1]; /* * Limit the width to 5 bytes as that is what users expect. */ humanize_number(buf, MIN(sizeof(buf), 5), bytes, "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); (void)printf(" %*s", len, buf); } static int showquotas(int type, u_long id, const char *name) { struct quotause *qup; struct quotause *quplist; const char *msgi, *msgb; const char *nam; char *bgrace = NULL, *igrace = NULL; int lines = 0, overquota = 0; static time_t now; if (now == 0) time(&now); quplist = getprivs(id, type); for (qup = quplist; qup; qup = qup->next) { msgi = NULL; if (qup->dqblk.dqb_ihardlimit && qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit) { overquota++; msgi = "File limit reached on"; } else if (qup->dqblk.dqb_isoftlimit && qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit) { overquota++; if (qup->dqblk.dqb_itime > now) msgi = "In file grace period on"; else msgi = "Over file quota on"; } msgb = NULL; if (qup->dqblk.dqb_bhardlimit && qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit) { overquota++; msgb = "Block limit reached on"; } else if (qup->dqblk.dqb_bsoftlimit && qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit) { overquota++; if (qup->dqblk.dqb_btime > now) msgb = "In block grace period on"; else msgb = "Over block quota on"; } if (rflag) { showrawquotas(type, id, qup); continue; } if (!vflag && qup->dqblk.dqb_isoftlimit == 0 && qup->dqblk.dqb_ihardlimit == 0 && qup->dqblk.dqb_bsoftlimit == 0 && qup->dqblk.dqb_bhardlimit == 0) continue; if (qflag) { if ((msgi != NULL || msgb != NULL) && lines++ == 0) heading(type, id, name, ""); if (msgi != NULL) printf("\t%s %s\n", msgi, qup->fsname); if (msgb != NULL) printf("\t%s %s\n", msgb, qup->fsname); continue; } if (!vflag && qup->dqblk.dqb_curblocks == 0 && qup->dqblk.dqb_curinodes == 0) continue; if (lines++ == 0) heading(type, id, name, ""); nam = qup->fsname; if (strlen(qup->fsname) > 15) { printf("%s\n", qup->fsname); nam = ""; } printf("%-15s", nam); if (hflag) { prthumanval(7, dbtob(qup->dqblk.dqb_curblocks)); printf("%c", (msgb == NULL) ? ' ' : '*'); prthumanval(7, dbtob(qup->dqblk.dqb_bsoftlimit)); prthumanval(7, dbtob(qup->dqblk.dqb_bhardlimit)); } else { printf(" %7ju%c %7ju %7ju", (uintmax_t)dbtob(qup->dqblk.dqb_curblocks) / 1024, (msgb == NULL) ? ' ' : '*', (uintmax_t)dbtob(qup->dqblk.dqb_bsoftlimit) / 1024, (uintmax_t)dbtob(qup->dqblk.dqb_bhardlimit) / 1024); } if (msgb != NULL) bgrace = timeprt(qup->dqblk.dqb_btime); if (msgi != NULL) igrace = timeprt(qup->dqblk.dqb_itime); printf("%8s %6ju%c %6ju %6ju%8s\n" , (msgb == NULL) ? "" : bgrace , (uintmax_t)qup->dqblk.dqb_curinodes , (msgi == NULL) ? ' ' : '*' , (uintmax_t)qup->dqblk.dqb_isoftlimit , (uintmax_t)qup->dqblk.dqb_ihardlimit , (msgi == NULL) ? "" : igrace ); if (msgb != NULL) free(bgrace); if (msgi != NULL) free(igrace); } if (!qflag && !rflag && lines == 0) heading(type, id, name, "none"); return (overquota); } static void showrawquotas(int type, u_long id, struct quotause *qup) { time_t t; printf("Raw %s quota information for id %lu on %s\n", type == USRQUOTA ? "user" : "group", id, qup->fsname); printf("block hard limit: %ju\n", (uintmax_t)qup->dqblk.dqb_bhardlimit); printf("block soft limit: %ju\n", (uintmax_t)qup->dqblk.dqb_bsoftlimit); printf("current block count: %ju\n", (uintmax_t)qup->dqblk.dqb_curblocks); printf("i-node hard limit: %ju\n", (uintmax_t)qup->dqblk.dqb_ihardlimit); printf("i-node soft limit: %ju\n", (uintmax_t)qup->dqblk.dqb_isoftlimit); printf("current i-node count: %ju\n", (uintmax_t)qup->dqblk.dqb_curinodes); printf("block grace time: %jd", (intmax_t)qup->dqblk.dqb_btime); if (qup->dqblk.dqb_btime != 0) { t = qup->dqblk.dqb_btime; printf(" %s", ctime(&t)); } else { printf("\n"); } printf("i-node grace time: %jd", (intmax_t)qup->dqblk.dqb_itime); if (qup->dqblk.dqb_itime != 0) { t = qup->dqblk.dqb_itime; printf(" %s", ctime(&t)); } else { printf("\n"); } } static void heading(int type, u_long id, const char *name, const char *tag) { printf("Disk quotas for %s %s (%cid %lu): %s\n", qfextension[type], name, *qfextension[type], id, tag); if (!qflag && tag[0] == '\0') { printf("%-15s %7s %8s %7s %7s %6s %7s %6s%8s\n" , "Filesystem" , "usage" , "quota" , "limit" , "grace" , "files" , "quota" , "limit" , "grace" ); } } /* * Calculate the grace period and return a printable string for it. */ static char * timeprt(int64_t seconds) { time_t hours, minutes; char *buf; static time_t now; if (now == 0) time(&now); if (now > seconds) { if ((buf = strdup("none")) == NULL) errx(1, "strdup() failed in timeprt()"); return (buf); } seconds -= now; minutes = (seconds + 30) / 60; hours = (minutes + 30) / 60; if (hours >= 36) { if (asprintf(&buf, "%lddays", ((long)hours + 12) / 24) < 0) errx(1, "asprintf() failed in timeprt(1)"); return (buf); } if (minutes >= 60) { if (asprintf(&buf, "%2ld:%ld", (long)minutes / 60, (long)minutes % 60) < 0) errx(1, "asprintf() failed in timeprt(2)"); return (buf); } if (asprintf(&buf, "%2ld", (long)minutes) < 0) errx(1, "asprintf() failed in timeprt(3)"); return (buf); } /* * Collect the requested quota information. */ static struct quotause * getprivs(long id, int quotatype) { struct quotause *qup, *quptail = NULL; struct fstab *fs; struct quotause *quphead; struct statfs *fst; int nfst, i; struct statfs sfb; qup = quphead = (struct quotause *)0; if (filename != NULL && statfs(filename, &sfb) != 0) err(1, "cannot statfs %s", filename); nfst = getmntinfo(&fst, MNT_NOWAIT); if (nfst == 0) errx(2, "no filesystems mounted!"); setfsent(); for (i = 0; i < nfst; i++) { if (qup == NULL) { if ((qup = (struct quotause *)malloc(sizeof *qup)) == NULL) errx(2, "out of memory"); } /* * See if the user requested a specific file system * or specified a file inside a mounted file system. */ if (filename != NULL && strcmp(sfb.f_mntonname, fst[i].f_mntonname) != 0) continue; if (strcmp(fst[i].f_fstypename, "nfs") == 0) { if (lflag) continue; if (getnfsquota(&fst[i], qup, id, quotatype) == 0) continue; } else if (strcmp(fst[i].f_fstypename, "ufs") == 0) { /* * XXX * UFS filesystems must be in /etc/fstab, and must * indicate that they have quotas on (?!) This is quite * unlike SunOS where quotas can be enabled/disabled * on a filesystem independent of /etc/fstab, and it * will still print quotas for them. */ if ((fs = getfsspec(fst[i].f_mntfromname)) == NULL) continue; if (getufsquota(fs, qup, id, quotatype) == 0) continue; } else continue; strcpy(qup->fsname, fst[i].f_mntonname); if (quphead == NULL) quphead = qup; else quptail->next = qup; quptail = qup; quptail->next = 0; qup = NULL; } if (qup) free(qup); endfsent(); return (quphead); } /* * Check to see if a particular quota is available. */ static int getufsquota(struct fstab *fs, struct quotause *qup, long id, int quotatype) { struct quotafile *qf; if ((qf = quota_open(fs, quotatype, O_RDONLY)) == NULL) return (0); if (quota_read(qf, &qup->dqblk, id) != 0) return (0); quota_close(qf); return (1); } static int getnfsquota(struct statfs *fst, struct quotause *qup, long id, int quotatype) { struct ext_getquota_args gq_args; struct getquota_args old_gq_args; struct getquota_rslt gq_rslt; struct dqblk *dqp = &qup->dqblk; struct timeval tv; char *cp, host[NI_MAXHOST]; enum clnt_stat call_stat; if (fst->f_flags & MNT_LOCAL) return (0); /* * must be some form of "hostname:/path" */ cp = fst->f_mntfromname; do { cp = strrchr(cp, ':'); } while (cp != NULL && *(cp + 1) != '/'); if (cp == NULL) { warnx("cannot find hostname for %s", fst->f_mntfromname); return (0); } memset(host, 0, sizeof(host)); memcpy(host, fst->f_mntfromname, cp - fst->f_mntfromname); host[sizeof(host) - 1] = '\0'; /* Avoid attempting the RPC for special amd(8) filesystems. */ if (strncmp(fst->f_mntfromname, "pid", 3) == 0 && strchr(fst->f_mntfromname, '@') != NULL) return (0); gq_args.gqa_pathp = cp + 1; gq_args.gqa_id = id; gq_args.gqa_type = quotatype; call_stat = callaurpc(host, RQUOTAPROG, EXT_RQUOTAVERS, RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_ext_getquota_args, (char *)&gq_args, (xdrproc_t)xdr_getquota_rslt, (char *)&gq_rslt); if (call_stat == RPC_PROGVERSMISMATCH || call_stat == RPC_PROGNOTREGISTERED) { if (quotatype == USRQUOTA) { old_gq_args.gqa_pathp = cp + 1; old_gq_args.gqa_uid = id; call_stat = callaurpc(host, RQUOTAPROG, RQUOTAVERS, RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_getquota_args, (char *)&old_gq_args, (xdrproc_t)xdr_getquota_rslt, (char *)&gq_rslt); } else { /* Old rpc quota does not support group type */ return (0); } } if (call_stat != 0) return (call_stat); switch (gq_rslt.status) { case Q_NOQUOTA: break; case Q_EPERM: warnx("quota permission error, host: %s", fst->f_mntfromname); break; case Q_OK: gettimeofday(&tv, NULL); /* blocks*/ dqp->dqb_bhardlimit = ((uint64_t)gq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit * gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize) / DEV_BSIZE; dqp->dqb_bsoftlimit = ((uint64_t)gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit * gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize) / DEV_BSIZE; dqp->dqb_curblocks = ((uint64_t)gq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks * gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize) / DEV_BSIZE; /* inodes */ dqp->dqb_ihardlimit = gq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit; dqp->dqb_isoftlimit = gq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit; dqp->dqb_curinodes = gq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles; /* grace times */ dqp->dqb_btime = tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft; dqp->dqb_itime = tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft; return (1); default: warnx("bad rpc result, host: %s", fst->f_mntfromname); break; } return (0); } static enum clnt_stat callaurpc(char *host, int prognum, int versnum, int procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out) { enum clnt_stat clnt_stat; struct timeval timeout, tottimeout; CLIENT *client = NULL; client = clnt_create(host, prognum, versnum, "udp"); if (client == NULL) return ((int)rpc_createerr.cf_stat); timeout.tv_usec = 0; timeout.tv_sec = 6; CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)(void *)&timeout); client->cl_auth = authunix_create_default(); tottimeout.tv_sec = 25; tottimeout.tv_usec = 0; clnt_stat = clnt_call(client, procnum, inproc, in, outproc, out, tottimeout); return (clnt_stat); } static int alldigits(char *s) { int c; c = *s++; do { if (!isdigit(c)) return (0); } while ((c = *s++)); return (1); } diff --git a/usr.sbin/chroot/chroot.c b/usr.sbin/chroot/chroot.c index 7ec5a00b50f0..e1af0a4131d3 100644 --- a/usr.sbin/chroot/chroot.c +++ b/usr.sbin/chroot/chroot.c @@ -1,212 +1,207 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void usage(void) __dead2; static gid_t resolve_group(const char *group) { char *endp; struct group *gp; unsigned long gid; gp = getgrnam(group); if (gp != NULL) return (gp->gr_gid); /* * Numeric IDs don't need a trip through the database to check them, * POSIX seems to think we should generally accept a numeric ID as long * as it's within the valid range. */ errno = 0; gid = strtoul(group, &endp, 0); if (errno == 0 && *endp == '\0' && gid <= GID_MAX) return (gid); errx(1, "no such group '%s'", group); } static uid_t resolve_user(const char *user) { char *endp; struct passwd *pw; unsigned long uid; pw = getpwnam(user); if (pw != NULL) return (pw->pw_uid); errno = 0; uid = strtoul(user, &endp, 0); if (errno == 0 && *endp == '\0' && uid <= UID_MAX) return (uid); errx(1, "no such user '%s'", user); } int main(int argc, char *argv[]) { const char *group, *p, *shell, *user; char *grouplist; long ngroups_max; gid_t gid, *gidlist; uid_t uid; int arg, ch, error, gids; bool nonprivileged; gid = 0; uid = 0; gids = 0; user = group = grouplist = NULL; gidlist = NULL; nonprivileged = false; while ((ch = getopt(argc, argv, "G:g:u:n")) != -1) { switch(ch) { case 'u': user = optarg; if (*user == '\0') usage(); break; case 'g': group = optarg; if (*group == '\0') usage(); break; case 'G': grouplist = optarg; /* * XXX Why not allow us to drop all of our supplementary * groups? */ if (*grouplist == '\0') usage(); break; case 'n': nonprivileged = true; break; case '?': default: usage(); } } argc -= optind; argv += optind; if (argc < 1) usage(); if (group != NULL) gid = resolve_group(group); if (grouplist != NULL) { - ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1; + ngroups_max = sysconf(_SC_NGROUPS_MAX); if ((gidlist = malloc(sizeof(gid_t) * ngroups_max)) == NULL) err(1, "malloc"); - /* Populate the egid slot in our groups to avoid accidents. */ - if (gid == 0) - gidlist[0] = getegid(); - else - gidlist[0] = gid; - for (gids = 1; (p = strsep(&grouplist, ",")) != NULL && + for (gids = 0; (p = strsep(&grouplist, ",")) != NULL && gids < ngroups_max; ) { if (*p == '\0') continue; gidlist[gids++] = resolve_group(p); } if (p != NULL && gids == ngroups_max) errx(1, "too many supplementary groups provided"); } if (user != NULL) uid = resolve_user(user); if (nonprivileged) { arg = PROC_NO_NEW_PRIVS_ENABLE; error = procctl(P_PID, getpid(), PROC_NO_NEW_PRIVS_CTL, &arg); if (error != 0) err(1, "procctl"); } if (chdir(argv[0]) == -1) err(1, "%s", argv[0]); if (chroot(".") == -1) { if (errno == EPERM && !nonprivileged && geteuid() != 0) errx(1, "unprivileged use requires -n"); err(1, "%s", argv[0]); } if (gidlist != NULL && setgroups(gids, gidlist) == -1) err(1, "setgroups"); if (group && setgid(gid) == -1) err(1, "setgid"); if (user && setuid(uid) == -1) err(1, "setuid"); if (argv[1]) { execvp(argv[1], &argv[1]); err(1, "%s", argv[1]); } if (!(shell = getenv("SHELL"))) shell = _PATH_BSHELL; execlp(shell, shell, "-i", (char *)NULL); err(1, "%s", shell); /* NOTREACHED */ } static void usage(void) { (void)fprintf(stderr, "usage: chroot [-g group] [-G group,group,...] " "[-u user] [-n] newroot [command]\n"); exit(1); } diff --git a/usr.sbin/lpr/lpc/lpc.c b/usr.sbin/lpr/lpc/lpc.c index a3da852de46e..b4db5bb2e29f 100644 --- a/usr.sbin/lpr/lpc/lpc.c +++ b/usr.sbin/lpr/lpc/lpc.c @@ -1,407 +1,409 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "lp.cdefs.h" /* A cross-platform version of */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lp.h" #include "lpc.h" #include "extern.h" #ifndef LPR_OPER #define LPR_OPER "operator" /* group name of lpr operators */ #endif /* * lpc -- line printer control program */ #define MAX_CMDLINE 200 #define MAX_MARGV 20 static int fromatty; static char cmdline[MAX_CMDLINE]; static int margc; static char *margv[MAX_MARGV]; uid_t uid, euid; int main(int _argc, char *_argv[]); static void cmdscanner(void); static struct cmd *getcmd(const char *_name); static void intr(int _signo); static void makeargv(void); static int ingroup(const char *_grname); int main(int argc, char *argv[]) { register struct cmd *c; euid = geteuid(); uid = getuid(); PRIV_END progname = argv[0]; openlog("lpd", 0, LOG_LPR); if (--argc > 0) { c = getcmd(*++argv); if (c == (struct cmd *)-1) { printf("?Ambiguous command\n"); exit(1); } if (c == NULL) { printf("?Invalid command\n"); exit(1); } if ((c->c_opts & LPC_PRIVCMD) && getuid() && ingroup(LPR_OPER) == 0) { printf("?Privileged command\n"); exit(1); } if (c->c_generic != NULL) generic(c->c_generic, c->c_opts, c->c_handler, argc, argv); else (*c->c_handler)(argc, argv); exit(0); } fromatty = isatty(fileno(stdin)); if (!fromatty) signal(SIGINT, intr); for (;;) { cmdscanner(); } } static void intr(int signo __unused) { /* (the '__unused' is just to avoid a compile-time warning) */ exit(0); } static const char * lpc_prompt(void) { return ("lpc> "); } /* * Command parser. */ static void cmdscanner(void) { register struct cmd *c; static EditLine *el; static History *hist; HistEvent he; size_t len; int num; const char *bp; num = 0; bp = NULL; el = NULL; hist = NULL; for (;;) { if (fromatty) { if (!el) { el = el_init("lpc", stdin, stdout, stderr); hist = history_init(); history(hist, &he, H_SETSIZE, 100); el_set(el, EL_HIST, history, hist); el_set(el, EL_EDITOR, "emacs"); el_set(el, EL_PROMPT, lpc_prompt); el_set(el, EL_SIGNAL, 1); el_source(el, NULL); /* * EditLine init may call 'cgetset()' to set a * capability-db meant for termcap (eg: to set * terminal type 'xterm'). Reset that now, or * that same db-information will be used for * printcap (giving us an "xterm" printer, with * all kinds of invalid capabilities...). */ cgetset(NULL); } if ((bp = el_gets(el, &num)) == NULL || num == 0) quit(0, NULL); len = MIN(MAX_CMDLINE - 1, num); memcpy(cmdline, bp, len); cmdline[len] = 0; history(hist, &he, H_ENTER, bp); } else { if (fgets(cmdline, MAX_CMDLINE, stdin) == NULL) quit(0, NULL); if (cmdline[0] == 0 || cmdline[0] == '\n') break; } makeargv(); if (margc == 0) continue; if (el != NULL && el_parse(el, margc, (const char **)margv) != -1) continue; c = getcmd(margv[0]); if (c == (struct cmd *)-1) { printf("?Ambiguous command\n"); continue; } if (c == NULL) { printf("?Invalid command\n"); continue; } if ((c->c_opts & LPC_PRIVCMD) && getuid() && ingroup(LPR_OPER) == 0) { printf("?Privileged command\n"); continue; } /* * Two different commands might have the same generic rtn * (eg: "clean" and "tclean"), and just use different * handler routines for distinct command-setup. The handler * routine might also be set on a generic routine for * initial parameter processing. */ if (c->c_generic != NULL) generic(c->c_generic, c->c_opts, c->c_handler, margc, margv); else (*c->c_handler)(margc, margv); } } static struct cmd * getcmd(const char *name) { register const char *p, *q; register struct cmd *c, *found; register int nmatches, longest; longest = 0; nmatches = 0; found = NULL; for (c = cmdtab; (p = c->c_name); c++) { for (q = name; *q == *p++; q++) if (*q == 0) /* exact match? */ return(c); if (!*q) { /* the name was a prefix */ if (q - name > longest) { longest = q - name; nmatches = 1; found = c; } else if (q - name == longest) nmatches++; } } if (nmatches > 1) return((struct cmd *)-1); return(found); } /* * Slice a string up into argc/argv. */ static void makeargv(void) { register char *cp; register char **argp = margv; register int n = 0; margc = 0; for (cp = cmdline; *cp && (size_t)(cp - cmdline) < sizeof(cmdline) && n < MAX_MARGV - 1; n++) { while (isspace(*cp)) cp++; if (*cp == '\0') break; *argp++ = cp; margc += 1; while (*cp != '\0' && !isspace(*cp)) cp++; if (*cp == '\0') break; *cp++ = '\0'; } *argp++ = NULL; } #define HELPINDENT (sizeof ("directory")) /* * Help command. */ void help(int argc, char *argv[]) { register struct cmd *c; if (argc == 1) { register int i, j, w; int columns, width = 0, lines; printf("Commands may be abbreviated. Commands are:\n\n"); for (c = cmdtab; c->c_name; c++) { int len = strlen(c->c_name); if (len > width) width = len; } width = (width + 8) &~ 7; columns = 80 / width; if (columns == 0) columns = 1; lines = (NCMDS + columns - 1) / columns; for (i = 0; i < lines; i++) { for (j = 0; j < columns; j++) { c = cmdtab + j * lines + i; if (c->c_name) printf("%s", c->c_name); if (c + lines >= &cmdtab[NCMDS]) { printf("\n"); break; } w = strlen(c->c_name); while (w < width) { w = (w + 8) &~ 7; putchar('\t'); } } } return; } while (--argc > 0) { register char *arg; arg = *++argv; c = getcmd(arg); if (c == (struct cmd *)-1) printf("?Ambiguous help command %s\n", arg); else if (c == (struct cmd *)0) printf("?Invalid help command %s\n", arg); else printf("%-*s\t%s\n", (int) HELPINDENT, c->c_name, c->c_help); } } /* * return non-zero if the user is a member of the given group */ static int ingroup(const char *grname) { static struct group *gptr=NULL; static int ngroups = 0; static long ngroups_max; static gid_t *groups; register gid_t gid; register int i; if (gptr == NULL) { if ((gptr = getgrnam(grname)) == NULL) { warnx("warning: unknown group '%s'", grname); return(0); } ngroups_max = sysconf(_SC_NGROUPS_MAX); if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL) err(1, "malloc"); ngroups = getgroups(ngroups_max, groups); if (ngroups < 0) err(1, "getgroups"); } gid = gptr->gr_gid; + if (gid == getegid()) + return(1); for (i = 0; i < ngroups; i++) if (gid == groups[i]) return(1); return(0); } /* * Routine to get the information for a single printer (which will be * called by the routines which implement individual commands). * Note: This is for commands operating on a *single* printer. */ struct printer * setup_myprinter(char *pwanted, struct printer *pp, int sump_opts) { int cdres, cmdstatus; init_printer(pp); cmdstatus = getprintcap(pwanted, pp); switch (cmdstatus) { default: fatal(pp, "%s", pcaperr(cmdstatus)); /* NOTREACHED */ case PCAPERR_NOTFOUND: printf("unknown printer %s\n", pwanted); return (NULL); case PCAPERR_TCOPEN: printf("warning: %s: unresolved tc= reference(s)", pwanted); break; case PCAPERR_SUCCESS: break; } if ((sump_opts & SUMP_NOHEADER) == 0) printf("%s:\n", pp->printer); if (sump_opts & SUMP_CHDIR_SD) { PRIV_START cdres = chdir(pp->spool_dir); PRIV_END if (cdres < 0) { printf("\tcannot chdir to %s\n", pp->spool_dir); free_printer(pp); return (NULL); } } return (pp); }