/* $NetBSD: secmodel_bsd44_suser.c,v 1.59 2008/10/22 11:16:29 ad Exp $ */ /*- * Copyright (c) 2006 Elad Efrat <elad@NetBSD.org> * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 contains kauth(9) listeners needed to implement the traditional * NetBSD superuser access restrictions. * * There are two main resources a request can be issued to: user-owned and * system owned. For the first, traditional Unix access checks are done, as * well as superuser checks. If needed, the request context is examined before * a decision is made. For the latter, usually only superuser checks are done * as normal users are not allowed to access system resources. */ #include <sys/cdefs.h> __KERNEL_RCSID(0, "$NetBSD: secmodel_bsd44_suser.c,v 1.59 2008/10/22 11:16:29 ad Exp $"); #include <sys/types.h> #include <sys/param.h> #include <sys/kauth.h> #include <sys/acct.h> #include <sys/mutex.h> #include <sys/ktrace.h> #include <sys/mount.h> #include <sys/pset.h> #include <sys/socketvar.h> #include <sys/sysctl.h> #include <sys/tty.h> #include <net/route.h> #include <sys/ptrace.h> #include <sys/vnode.h> #include <sys/proc.h> #include <sys/uidinfo.h> #include <miscfs/procfs/procfs.h> #include <secmodel/bsd44/suser.h> extern int dovfsusermount; static kauth_listener_t l_generic, l_system, l_process, l_network, l_machdep, l_device; void secmodel_bsd44_suser_start(void) { l_generic = kauth_listen_scope(KAUTH_SCOPE_GENERIC, secmodel_bsd44_suser_generic_cb, NULL); l_system = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, secmodel_bsd44_suser_system_cb, NULL); l_process = kauth_listen_scope(KAUTH_SCOPE_PROCESS, secmodel_bsd44_suser_process_cb, NULL); l_network = kauth_listen_scope(KAUTH_SCOPE_NETWORK, secmodel_bsd44_suser_network_cb, NULL); l_machdep = kauth_listen_scope(KAUTH_SCOPE_MACHDEP, secmodel_bsd44_suser_machdep_cb, NULL); l_device = kauth_listen_scope(KAUTH_SCOPE_DEVICE, secmodel_bsd44_suser_device_cb, NULL); } #if defined(_LKM) void secmodel_bsd44_suser_stop(void) { kauth_unlisten_scope(l_generic); kauth_unlisten_scope(l_system); kauth_unlisten_scope(l_process); kauth_unlisten_scope(l_network); kauth_unlisten_scope(l_machdep); kauth_unlisten_scope(l_device); } #endif /* _LKM */ /* * kauth(9) listener * * Security model: Traditional NetBSD * Scope: Generic * Responsibility: Superuser access */ int secmodel_bsd44_suser_generic_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) { bool isroot; int result; isroot = (kauth_cred_geteuid(cred) == 0); result = KAUTH_RESULT_DEFER; switch (action) { case KAUTH_GENERIC_ISSUSER: if (isroot) result = KAUTH_RESULT_ALLOW; break; case KAUTH_GENERIC_CANSEE: if (!secmodel_bsd44_curtain) result = KAUTH_RESULT_ALLOW; else if (isroot || kauth_cred_uidmatch(cred, arg0)) result = KAUTH_RESULT_ALLOW; break; default: result = KAUTH_RESULT_DEFER; break; } return (result); } /* * kauth(9) listener * * Security model: Traditional NetBSD * Scope: System * Responsibility: Superuser access */ int secmodel_bsd44_suser_system_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) { bool isroot; int result; enum kauth_system_req req; isroot = (kauth_cred_geteuid(cred) == 0); result = KAUTH_RESULT_DEFER; req = (enum kauth_system_req)arg0; switch (action) { case KAUTH_SYSTEM_CPU: switch (req) { case KAUTH_REQ_SYSTEM_CPU_SETSTATE: if (isroot) result = KAUTH_RESULT_ALLOW; break; default: break; } break; case KAUTH_SYSTEM_MOUNT: switch (req) { case KAUTH_REQ_SYSTEM_MOUNT_GET: result = KAUTH_RESULT_ALLOW; break; case KAUTH_REQ_SYSTEM_MOUNT_NEW: if (isroot) result = KAUTH_RESULT_ALLOW; else if (dovfsusermount) { struct vnode *vp = arg1; u_long flags = (u_long)arg2; if (!(flags & MNT_NODEV) || !(flags & MNT_NOSUID)) break; if ((vp->v_mount->mnt_flag & MNT_NOEXEC) && !(flags & MNT_NOEXEC)) break; result = KAUTH_RESULT_ALLOW; } break; case KAUTH_REQ_SYSTEM_MOUNT_UNMOUNT: if (isroot) result = KAUTH_RESULT_ALLOW; else { struct mount *mp = arg1; if (mp->mnt_stat.f_owner == kauth_cred_geteuid(cred)) result = KAUTH_RESULT_ALLOW; } break; case KAUTH_REQ_SYSTEM_MOUNT_UPDATE: if (isroot) result = KAUTH_RESULT_ALLOW; else if (dovfsusermount) { struct mount *mp = arg1; u_long flags = (u_long)arg2; /* No exporting for non-root. */ if (flags & MNT_EXPORTED) break; if (!(flags & MNT_NODEV) || !(flags & MNT_NOSUID)) break; /* * Only super-user, or user that did the mount, * can update. */ if (mp->mnt_stat.f_owner != kauth_cred_geteuid(cred)) break; /* Retain 'noexec'. */ if ((mp->mnt_flag & MNT_NOEXEC) && !(flags & MNT_NOEXEC)) break; result = KAUTH_RESULT_ALLOW; } break; default: result = KAUTH_RESULT_DEFER; break; } break; case KAUTH_SYSTEM_PSET: { psetid_t id; id = (psetid_t)(unsigned long)arg1; switch (req) { case KAUTH_REQ_SYSTEM_PSET_ASSIGN: case KAUTH_REQ_SYSTEM_PSET_BIND: if (isroot || id == PS_QUERY) result = KAUTH_RESULT_ALLOW; break; case KAUTH_REQ_SYSTEM_PSET_CREATE: case KAUTH_REQ_SYSTEM_PSET_DESTROY: if (isroot) result = KAUTH_RESULT_ALLOW; break; default: break; } break; } case KAUTH_SYSTEM_TIME: switch (req) { case KAUTH_REQ_SYSTEM_TIME_ADJTIME: case KAUTH_REQ_SYSTEM_TIME_NTPADJTIME: case KAUTH_REQ_SYSTEM_TIME_TIMECOUNTERS: if (isroot) result = KAUTH_RESULT_ALLOW; break; case KAUTH_REQ_SYSTEM_TIME_SYSTEM: { bool device_context = (bool)arg3; if (device_context || isroot) result = KAUTH_RESULT_ALLOW; break; } case KAUTH_REQ_SYSTEM_TIME_RTCOFFSET: /* * Decisions here are root-agnostic. * * KAUTH_REQ_SYSTEM_TIME_RTCOFFSET - Should be used * only after the caller was determined as someone * who can modify sysctl. For us, this means root. */ result = KAUTH_RESULT_ALLOW; break; default: result = KAUTH_RESULT_DEFER; break; } break; case KAUTH_SYSTEM_SYSCTL: switch (req) { case KAUTH_REQ_SYSTEM_SYSCTL_ADD: case KAUTH_REQ_SYSTEM_SYSCTL_DELETE: case KAUTH_REQ_SYSTEM_SYSCTL_DESC: case KAUTH_REQ_SYSTEM_SYSCTL_PRVT: if (isroot) result = KAUTH_RESULT_ALLOW; break; default: break; } break; case KAUTH_SYSTEM_SWAPCTL: case KAUTH_SYSTEM_ACCOUNTING: case KAUTH_SYSTEM_REBOOT: case KAUTH_SYSTEM_CHROOT: case KAUTH_SYSTEM_FILEHANDLE: case KAUTH_SYSTEM_MKNOD: if (isroot) result = KAUTH_RESULT_ALLOW; break; case KAUTH_SYSTEM_DEBUG: switch (req) { case KAUTH_REQ_SYSTEM_DEBUG_IPKDB: default: /* Decisions are root-agnostic. */ result = KAUTH_RESULT_ALLOW; break; } break; case KAUTH_SYSTEM_CHSYSFLAGS: case KAUTH_SYSTEM_LKM: case KAUTH_SYSTEM_SETIDCORE: /* * Decisions here are root-agnostic. * * CHSYSFLAGS - Should be used only after the caller was * determined as root. Needs to be re-factored * anyway. Infects ufs, ext2fs, tmpfs, and rump. * * LKM - Subject to permissions on /dev/lkm for now. * * SETIDCORE - Should be used only after the caller was * determined as someone who can modify sysctl * data. For us, this means root. */ result = KAUTH_RESULT_ALLOW; break; case KAUTH_SYSTEM_MODULE: if (isroot) result = KAUTH_RESULT_ALLOW; if ((uintptr_t)arg1 != 0) /* autoload */ result = KAUTH_RESULT_ALLOW; break; default: result = KAUTH_RESULT_DEFER; break; } return (result); } /* * common code for corename, rlimit, and stopflag. */ static int proc_uidmatch(kauth_cred_t cred, kauth_cred_t target) { int r = 0; if (kauth_cred_getuid(cred) != kauth_cred_getuid(target) || kauth_cred_getuid(cred) != kauth_cred_getsvuid(target)) { /* * suid proc of ours or proc not ours */ r = EPERM; } else if (kauth_cred_getgid(target) != kauth_cred_getsvgid(target)) { /* * sgid proc has sgid back to us temporarily */ r = EPERM; } else { /* * our rgid must be in target's group list (ie, * sub-processes started by a sgid process) */ int ismember = 0; if (kauth_cred_ismember_gid(cred, kauth_cred_getgid(target), &ismember) != 0 || !ismember) r = EPERM; } return (r); } /* * kauth(9) listener * * Security model: Traditional NetBSD * Scope: Process * Responsibility: Superuser access */ int secmodel_bsd44_suser_process_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) { struct proc *p; bool isroot; int result; isroot = (kauth_cred_geteuid(cred) == 0); result = KAUTH_RESULT_DEFER; p = arg0; switch (action) { case KAUTH_PROCESS_SIGNAL: { int signum; signum = (int)(unsigned long)arg1; if (isroot || kauth_cred_uidmatch(cred, p->p_cred) || (signum == SIGCONT && (curproc->p_session == p->p_session))) result = KAUTH_RESULT_ALLOW; break; } case KAUTH_PROCESS_CANSEE: { unsigned long req; req = (unsigned long)arg1; switch (req) { case KAUTH_REQ_PROCESS_CANSEE_ARGS: case KAUTH_REQ_PROCESS_CANSEE_ENTRY: case KAUTH_REQ_PROCESS_CANSEE_OPENFILES: if (!secmodel_bsd44_curtain) result = KAUTH_RESULT_ALLOW; else if (isroot || kauth_cred_uidmatch(cred, p->p_cred)) result = KAUTH_RESULT_ALLOW; break; case KAUTH_REQ_PROCESS_CANSEE_ENV: if (!isroot && (kauth_cred_getuid(cred) != kauth_cred_getuid(p->p_cred) || kauth_cred_getuid(cred) != kauth_cred_getsvuid(p->p_cred))) break; else result = KAUTH_RESULT_ALLOW; break; default: break; } break; } case KAUTH_PROCESS_KTRACE: { enum kauth_process_req req; req = (enum kauth_process_req)(unsigned long)arg1; if (isroot) { result = KAUTH_RESULT_ALLOW; break; } else if (req == KAUTH_REQ_PROCESS_KTRACE_PERSISTENT) { break; } if ((p->p_traceflag & KTRFAC_PERSISTENT) || (p->p_flag & PK_SUGID)) { break; } if (kauth_cred_geteuid(cred) == kauth_cred_getuid(p->p_cred) && kauth_cred_getuid(cred) == kauth_cred_getsvuid(p->p_cred) && kauth_cred_getgid(cred) == kauth_cred_getgid(p->p_cred) && kauth_cred_getgid(cred) == kauth_cred_getsvgid(p->p_cred)) { result = KAUTH_RESULT_ALLOW; break; } break; } case KAUTH_PROCESS_PROCFS: { enum kauth_process_req req = (enum kauth_process_req)arg2; struct pfsnode *pfs = arg1; if (isroot) { result = KAUTH_RESULT_ALLOW; break; } if (req == KAUTH_REQ_PROCESS_PROCFS_CTL) { break; } switch (pfs->pfs_type) { case PFSregs: case PFSfpregs: case PFSmem: if (kauth_cred_getuid(cred) != kauth_cred_getuid(p->p_cred) || ISSET(p->p_flag, PK_SUGID)) { break; } /*FALLTHROUGH*/ default: result = KAUTH_RESULT_ALLOW; break; } break; } case KAUTH_PROCESS_PTRACE: { switch ((u_long)arg1) { case PT_TRACE_ME: case PT_ATTACH: case PT_WRITE_I: case PT_WRITE_D: case PT_READ_I: case PT_READ_D: case PT_IO: #ifdef PT_GETREGS case PT_GETREGS: #endif #ifdef PT_SETREGS case PT_SETREGS: #endif #ifdef PT_GETFPREGS case PT_GETFPREGS: #endif #ifdef PT_SETFPREGS case PT_SETFPREGS: #endif #ifdef __HAVE_PTRACE_MACHDEP PTRACE_MACHDEP_REQUEST_CASES #endif if (isroot) { result = KAUTH_RESULT_ALLOW; break; } if (kauth_cred_getuid(cred) != kauth_cred_getuid(p->p_cred) || ISSET(p->p_flag, PK_SUGID)) { break; } result = KAUTH_RESULT_ALLOW; break; #ifdef PT_STEP case PT_STEP: #endif case PT_CONTINUE: case PT_KILL: case PT_DETACH: case PT_LWPINFO: case PT_SYSCALL: case PT_DUMPCORE: result = KAUTH_RESULT_ALLOW; break; default: result = KAUTH_RESULT_DEFER; break; } break; } case KAUTH_PROCESS_CORENAME: if (isroot || proc_uidmatch(cred, p->p_cred) == 0) result = KAUTH_RESULT_ALLOW; break; case KAUTH_PROCESS_FORK: { int lnprocs = (int)(unsigned long)arg2; /* * Don't allow a nonprivileged user to use the last few * processes. The variable lnprocs is the current number of * processes, maxproc is the limit. */ if (__predict_false((lnprocs >= maxproc - 5) && !isroot)) break; else result = KAUTH_RESULT_ALLOW; break; } case KAUTH_PROCESS_KEVENT_FILTER: if ((kauth_cred_getuid(p->p_cred) != kauth_cred_getuid(cred) || ISSET(p->p_flag, PK_SUGID)) && !isroot) break; else result = KAUTH_RESULT_ALLOW; break; case KAUTH_PROCESS_NICE: if (isroot) { result = KAUTH_RESULT_ALLOW; break; } if (kauth_cred_geteuid(cred) != kauth_cred_geteuid(p->p_cred) && kauth_cred_getuid(cred) != kauth_cred_geteuid(p->p_cred)) { break; } if ((u_long)arg1 >= p->p_nice) result = KAUTH_RESULT_ALLOW; break; case KAUTH_PROCESS_RLIMIT: { unsigned long req; req = (unsigned long)arg1; switch (req) { case KAUTH_REQ_PROCESS_RLIMIT_SET: { struct rlimit *new_rlimit; u_long which; if (isroot) { result = KAUTH_RESULT_ALLOW; break; } if ((p != curlwp->l_proc) && (proc_uidmatch(cred, p->p_cred) != 0)) { break; } new_rlimit = arg2; which = (u_long)arg3; if (new_rlimit->rlim_max <= p->p_rlimit[which].rlim_max) result = KAUTH_RESULT_ALLOW; break; } case KAUTH_REQ_PROCESS_RLIMIT_GET: result = KAUTH_RESULT_ALLOW; break; default: break; } break; } case KAUTH_PROCESS_SCHEDULER_GETPARAM: if (isroot || kauth_cred_uidmatch(cred, p->p_cred)) result = KAUTH_RESULT_ALLOW; break; case KAUTH_PROCESS_SCHEDULER_SETPARAM: if (isroot) result = KAUTH_RESULT_ALLOW; else if (kauth_cred_uidmatch(cred, p->p_cred)) { struct lwp *l; int policy; pri_t priority; l = arg1; policy = (int)(unsigned long)arg2; priority = (pri_t)(unsigned long)arg3; if ((policy == l->l_class || (policy != SCHED_FIFO && policy != SCHED_RR)) && priority <= l->l_priority) result = KAUTH_RESULT_ALLOW; } break; case KAUTH_PROCESS_SCHEDULER_GETAFFINITY: result = KAUTH_RESULT_ALLOW; break; case KAUTH_PROCESS_SCHEDULER_SETAFFINITY: if (isroot) result = KAUTH_RESULT_ALLOW; break; case KAUTH_PROCESS_SETID: if (isroot) result = KAUTH_RESULT_ALLOW; break; case KAUTH_PROCESS_STOPFLAG: if (isroot || proc_uidmatch(cred, p->p_cred) == 0) { result = KAUTH_RESULT_ALLOW; break; } break; default: result = KAUTH_RESULT_DEFER; break; } return (result); } /* * kauth(9) listener * * Security model: Traditional NetBSD * Scope: Network * Responsibility: Superuser access */ int secmodel_bsd44_suser_network_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) { bool isroot; int result; enum kauth_network_req req; isroot = (kauth_cred_geteuid(cred) == 0); result = KAUTH_RESULT_DEFER; req = (enum kauth_network_req)arg0; switch (action) { case KAUTH_NETWORK_ALTQ: switch (req) { case KAUTH_REQ_NETWORK_ALTQ_AFMAP: case KAUTH_REQ_NETWORK_ALTQ_BLUE: case KAUTH_REQ_NETWORK_ALTQ_CBQ: case KAUTH_REQ_NETWORK_ALTQ_CDNR: case KAUTH_REQ_NETWORK_ALTQ_CONF: case KAUTH_REQ_NETWORK_ALTQ_FIFOQ: case KAUTH_REQ_NETWORK_ALTQ_HFSC: case KAUTH_REQ_NETWORK_ALTQ_JOBS: case KAUTH_REQ_NETWORK_ALTQ_PRIQ: case KAUTH_REQ_NETWORK_ALTQ_RED: case KAUTH_REQ_NETWORK_ALTQ_RIO: case KAUTH_REQ_NETWORK_ALTQ_WFQ: if (isroot) result = KAUTH_RESULT_ALLOW; break; default: result = KAUTH_RESULT_DEFER; break; } break; case KAUTH_NETWORK_BIND: switch (req) { case KAUTH_REQ_NETWORK_BIND_PRIVPORT: if (isroot) result = KAUTH_RESULT_ALLOW; break; default: result = KAUTH_RESULT_ALLOW; break; } break; case KAUTH_NETWORK_FIREWALL: switch (req) { case KAUTH_REQ_NETWORK_FIREWALL_FW: case KAUTH_REQ_NETWORK_FIREWALL_NAT: /* * Decisions are root-agnostic. * * Both requests are issued from the context of a * device with permission bits acting as access * control. */ result = KAUTH_RESULT_ALLOW; break; default: break; } break; case KAUTH_NETWORK_FORWSRCRT: /* * Decision is root-agnostic. * * Can only be issued from sysctl context, in our case, only * root can get here. */ result = KAUTH_RESULT_ALLOW; break; case KAUTH_NETWORK_INTERFACE: switch (req) { case KAUTH_REQ_NETWORK_INTERFACE_GET: case KAUTH_REQ_NETWORK_INTERFACE_SET: result = KAUTH_RESULT_ALLOW; break; case KAUTH_REQ_NETWORK_INTERFACE_GETPRIV: case KAUTH_REQ_NETWORK_INTERFACE_SETPRIV: if (isroot) result = KAUTH_RESULT_ALLOW; break; default: result = KAUTH_RESULT_DEFER; break; } break; case KAUTH_NETWORK_NFS: switch (req) { case KAUTH_REQ_NETWORK_NFS_EXPORT: case KAUTH_REQ_NETWORK_NFS_SVC: if (isroot) result = KAUTH_RESULT_ALLOW; break; default: result = KAUTH_RESULT_DEFER; break; } break; case KAUTH_NETWORK_ROUTE: switch (((struct rt_msghdr *)arg1)->rtm_type) { case RTM_GET: result = KAUTH_RESULT_ALLOW; break; default: if (isroot) result = KAUTH_RESULT_ALLOW; break; } break; case KAUTH_NETWORK_SOCKET: switch (req) { case KAUTH_REQ_NETWORK_SOCKET_OPEN: if ((u_long)arg1 == PF_ROUTE || (u_long)arg1 == PF_BLUETOOTH) result = KAUTH_RESULT_ALLOW; else if ((u_long)arg2 == SOCK_RAW) { if (isroot) result = KAUTH_RESULT_ALLOW; } else result = KAUTH_RESULT_ALLOW; break; case KAUTH_REQ_NETWORK_SOCKET_RAWSOCK: if (isroot) result = KAUTH_RESULT_ALLOW; break; case KAUTH_REQ_NETWORK_SOCKET_CANSEE: if (secmodel_bsd44_curtain) { uid_t so_uid; so_uid = ((struct socket *)arg1)->so_uidinfo->ui_uid; if (isroot || kauth_cred_geteuid(cred) == so_uid) result = KAUTH_RESULT_ALLOW; } else result = KAUTH_RESULT_ALLOW; break; default: result = KAUTH_RESULT_ALLOW; break; } break; default: result = KAUTH_RESULT_DEFER; break; } return (result); } /* * kauth(9) listener * * Security model: Traditional NetBSD * Scope: Machdep * Responsibility: Superuser access */ int secmodel_bsd44_suser_machdep_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) { bool isroot; int result; isroot = (kauth_cred_geteuid(cred) == 0); result = KAUTH_RESULT_DEFER; switch (action) { case KAUTH_MACHDEP_IOPERM_GET: case KAUTH_MACHDEP_LDT_GET: case KAUTH_MACHDEP_LDT_SET: case KAUTH_MACHDEP_MTRR_GET: result = KAUTH_RESULT_ALLOW; break; case KAUTH_MACHDEP_IOPERM_SET: case KAUTH_MACHDEP_IOPL: case KAUTH_MACHDEP_MTRR_SET: case KAUTH_MACHDEP_UNMANAGEDMEM: if (isroot) result = KAUTH_RESULT_ALLOW; break; default: result = KAUTH_RESULT_DEFER; break; } return (result); } /* * kauth(9) listener * * Security model: Traditional NetBSD * Scope: Device * Responsibility: Superuser access */ int secmodel_bsd44_suser_device_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) { struct tty *tty; bool isroot; int result; isroot = (kauth_cred_geteuid(cred) == 0); result = KAUTH_RESULT_DEFER; switch (action) { case KAUTH_DEVICE_RAWIO_SPEC: case KAUTH_DEVICE_RAWIO_PASSTHRU: /* * Decision is root-agnostic. * * Both requests can be issued on devices subject to their * permission bits. */ result = KAUTH_RESULT_ALLOW; break; case KAUTH_DEVICE_TTY_OPEN: tty = arg0; if (!(tty->t_state & TS_ISOPEN)) result = KAUTH_RESULT_ALLOW; else if (tty->t_state & TS_XCLUDE) { if (isroot) result = KAUTH_RESULT_ALLOW; } else result = KAUTH_RESULT_ALLOW; break; case KAUTH_DEVICE_TTY_PRIVSET: if (isroot) result = KAUTH_RESULT_ALLOW; break; case KAUTH_DEVICE_TTY_STI: if (isroot) result = KAUTH_RESULT_ALLOW; break; default: result = KAUTH_RESULT_DEFER; break; } return (result); }