/* * Change the mode on a file. * Inode must be locked before calling. */ static int ext2_chmod(struct vnode *vp, int mode, struct ucred *cred, struct thread *td) { struct inode *ip = VTOI(vp); int error; /* * To modify the permissions on a file, must possess VADMIN * for that file. */ if ((error = VOP_ACCESS(vp, VADMIN, cred, td))) return (error); /* * Privileged processes may set the sticky bit on non-directories, * as well as set the setgid bit on a file with a group that the * process is not a member of. */ if (vp->v_type != VDIR && (mode & S_ISTXT)) { error = priv_check_cred(cred, PRIV_VFS_STICKYFILE, 0); if (error) return (EFTYPE); } if (!groupmember(ip->i_gid, cred) && (mode & ISGID)) { error = priv_check_cred(cred, PRIV_VFS_SETGID, 0); if (error) return (error); } ip->i_mode &= ~ALLPERMS; ip->i_mode |= (mode & ALLPERMS); ip->i_flag |= IN_CHANGE; return (0); }
static int random_ioctl(cdev_t dev, u_long cmd, caddr_t data, int flags, struct ucred *cred) { int error; int intr; /* * Even inspecting the state is privileged, since it gives a hint * about how easily the randomness might be guessed. */ error = 0; switch (cmd) { /* Really handled in upper layer */ case FIOASYNC: break; case MEM_SETIRQ: intr = *(int16_t *)data; if ((error = priv_check_cred(cred, PRIV_ROOT, 0)) != 0) break; if (intr < 0 || intr >= MAX_INTS) return (EINVAL); register_randintr(intr); break; case MEM_CLEARIRQ: intr = *(int16_t *)data; if ((error = priv_check_cred(cred, PRIV_ROOT, 0)) != 0) break; if (intr < 0 || intr >= MAX_INTS) return (EINVAL); unregister_randintr(intr); break; case MEM_RETURNIRQ: error = ENOTSUP; break; case MEM_FINDIRQ: intr = *(int16_t *)data; if ((error = priv_check_cred(cred, PRIV_ROOT, 0)) != 0) break; if (intr < 0 || intr >= MAX_INTS) return (EINVAL); intr = next_registered_randintr(intr); if (intr == MAX_INTS) return (ENOENT); *(u_int16_t *)data = intr; break; default: error = ENOTSUP; break; } return (error); }
static int mmopen(struct dev_open_args *ap) { cdev_t dev = ap->a_head.a_dev; int error; switch (minor(dev)) { case 0: case 1: if (ap->a_oflags & FWRITE) { if (securelevel > 0 || kernel_mem_readonly) return (EPERM); } error = 0; break; case 14: error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0); if (error != 0) break; if (securelevel > 0 || kernel_mem_readonly) { error = EPERM; break; } error = cpu_set_iopl(); break; default: error = 0; break; } return (error); }
/* system call implementation */ int system_override(__unused struct proc *p, struct system_override_args * uap, __unused int32_t *retval) { uint64_t timeout = uap->timeout; uint64_t flags = uap->flags; int error = 0; /* Check credentials for caller. Only entitled processes are allowed to make this call. */ if ((error = priv_check_cred(kauth_cred_get(), PRIV_SYSTEM_OVERRIDE, 0))) { goto out; } /* Check to see if some flags are specified. Zero flags are invalid. */ if ((flags == 0) || ((flags & ~SYS_OVERRIDE_FLAGS_MASK) != 0)) { error = EINVAL; goto out; } lck_mtx_lock(&sys_override_lock); enable_system_override(flags); PROCESS_OVERRIDING_SYSTEM_DEFAULTS(timeout); disable_system_override(flags); lck_mtx_unlock(&sys_override_lock); out: return error; }
/* * mlockall(int how) * * No requirements */ int sys_mlockall(struct mlockall_args *uap) { struct thread *td = curthread; struct proc *p = td->td_proc; vm_map_t map = &p->p_vmspace->vm_map; vm_map_entry_t entry; int how = uap->how; int rc = KERN_SUCCESS; if (((how & MCL_CURRENT) == 0) && ((how & MCL_FUTURE) == 0)) return (EINVAL); rc = priv_check_cred(td->td_ucred, PRIV_ROOT, 0); if (rc) return (rc); vm_map_lock(map); do { if (how & MCL_CURRENT) { for(entry = map->header.next; entry != &map->header; entry = entry->next); rc = ENOSYS; break; } if (how & MCL_FUTURE) map->flags |= MAP_WIREFUTURE; } while(0); vm_map_unlock(map); return (rc); }
int zone_dataset_attach(struct ucred *cred, const char *dataset, int jailid) { struct zone_dataset_head *head; zone_dataset_t *zd, *zd2; struct prison *pr; int dofree, error; if ((error = priv_check_cred(cred, PRIV_ZFS_JAIL, 0)) != 0) return (error); /* Allocate memory before we grab prison's mutex. */ zd = malloc(sizeof(*zd) + strlen(dataset) + 1, M_ZONES, M_WAITOK); sx_slock(&allprison_lock); pr = prison_find(jailid); /* Locks &pr->pr_mtx. */ sx_sunlock(&allprison_lock); if (pr == NULL) { free(zd, M_ZONES); return (ENOENT); } head = osd_jail_get(pr, zone_slot); if (head != NULL) { dofree = 0; LIST_FOREACH(zd2, head, zd_next) { if (strcmp(dataset, zd2->zd_dataset) == 0) { free(zd, M_ZONES); error = EEXIST; goto end; } } } else {
int sys_osethostname(struct sethostname_args *uap) { struct thread *td = curthread; size_t len; char *hostname; int name[2]; int error; name[0] = CTL_KERN; name[1] = KERN_HOSTNAME; error = priv_check_cred(td->td_ucred, PRIV_SETHOSTNAME, 0); if (error) return (error); len = MIN(uap->len, MAXHOSTNAMELEN); hostname = kmalloc(MAXHOSTNAMELEN, M_TEMP, M_WAITOK); error = copyin(uap->hostname, hostname, len); if (error) { kfree(hostname, M_TEMP); return (error); } error = kernel_sysctl(name, 2, NULL, 0, hostname, len, NULL); kfree(hostname, M_TEMP); return (error); }
int sys_setegid(struct setegid_args *uap) { struct proc *p = curproc; struct ucred *cr; gid_t egid; int error; lwkt_gettoken(&proc_token); cr = p->p_ucred; egid = uap->egid; if (egid != cr->cr_rgid && /* allow setegid(getgid()) */ egid != cr->cr_svgid && /* allow setegid(saved gid) */ (error = priv_check_cred(cr, PRIV_CRED_SETEGID, 0))) { goto done; } if (cr->cr_groups[0] != egid) { cr = cratom(&p->p_ucred); cr->cr_groups[0] = egid; setsugid(); } error = 0; done: lwkt_reltoken(&proc_token); return (error); }
/* ARGSUSED */ int setauid(struct thread *td, struct setauid_args *uap) { struct ucred *newcred, *oldcred; au_id_t id; int error; if (jailed(td->td_ucred)) return (ENOSYS); error = copyin(uap->auid, &id, sizeof(id)); if (error) return (error); audit_arg_auid(id); newcred = crget(); PROC_LOCK(td->td_proc); oldcred = td->td_proc->p_ucred; crcopy(newcred, oldcred); #ifdef MAC error = mac_cred_check_setauid(oldcred, id); if (error) goto fail; #endif error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT, 0); if (error) goto fail; newcred->cr_audit.ai_auid = id; td->td_proc->p_ucred = newcred; PROC_UNLOCK(td->td_proc); crfree(oldcred); return (0); fail: PROC_UNLOCK(td->td_proc); crfree(newcred); return (error); }
int sys_seteuid(struct seteuid_args *uap) { struct proc *p = curproc; struct ucred *cr; uid_t euid; int error; lwkt_gettoken(&proc_token); cr = p->p_ucred; euid = uap->euid; if (euid != cr->cr_ruid && /* allow seteuid(getuid()) */ euid != cr->cr_svuid && /* allow seteuid(saved uid) */ (error = priv_check_cred(cr, PRIV_CRED_SETEUID, 0))) { lwkt_reltoken(&proc_token); return (error); } /* * Everything's okay, do it. Copy credentials so other references do * not see our changes. */ if (cr->cr_uid != euid) { change_euid(euid); setsugid(); } lwkt_reltoken(&proc_token); return (0); }
static int ksem_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred, struct thread *td) { struct ksem *ks; int error; error = 0; ks = fp->f_data; mtx_lock(&sem_lock); #ifdef MAC error = mac_posixsem_check_setowner(active_cred, ks, uid, gid); if (error != 0) goto out; #endif if (uid == (uid_t)-1) uid = ks->ks_uid; if (gid == (gid_t)-1) gid = ks->ks_gid; if (((uid != ks->ks_uid && uid != active_cred->cr_uid) || (gid != ks->ks_gid && !groupmember(gid, active_cred))) && (error = priv_check_cred(active_cred, PRIV_VFS_CHOWN, 0))) goto out; ks->ks_uid = uid; ks->ks_gid = gid; out: mtx_unlock(&sem_lock); return (error); }
int vop_helper_setattr_flags(u_int32_t *ino_flags, u_int32_t vaflags, uid_t uid, struct ucred *cred) { int error; /* * If uid doesn't match only a privileged user can change the flags */ if (cred->cr_uid != uid && (error = priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0))) { return(error); } if (cred->cr_uid == 0 && (!jailed(cred)|| jail_chflags_allowed)) { if ((*ino_flags & (SF_NOUNLINK|SF_IMMUTABLE|SF_APPEND)) && securelevel > 0) return (EPERM); *ino_flags = vaflags; } else { if (*ino_flags & (SF_NOUNLINK|SF_IMMUTABLE|SF_APPEND) || (vaflags & UF_SETTABLE) != vaflags) return (EPERM); *ino_flags &= SF_SETTABLE; *ino_flags |= vaflags & UF_SETTABLE; } return(0); }
static int seeotheruids_check(struct ucred *cr1, struct ucred *cr2) { if (!seeotheruids_enabled) return (0); if (primarygroup_enabled) { if (cr1->cr_rgid == cr2->cr_rgid) return (0); } if (specificgid_enabled) { if (cr1->cr_rgid == specificgid || groupmember(specificgid, cr1)) return (0); } if (cr1->cr_ruid == cr2->cr_ruid) return (0); if (suser_privileged) { if (priv_check_cred(cr1, PRIV_SEEOTHERUIDS, 0) == 0) return (0); } return (ESRCH); }
/* * Object-specific entry points are sorted alphabetically by object type name * and then by operation. */ static int partition_cred_check_relabel(struct ucred *cred, struct label *newlabel) { int error; error = 0; /* * Treat "0" as a no-op request because it reflects an unset * partition label. If we ever want to support switching back to an * unpartitioned state for a process, we'll need to differentiate the * "not in a partition" and "no partition defined during internalize" * conditions. */ if (SLOT(newlabel) != 0) { /* * Require BSD privilege in order to change the partition. * Originally we also required that the process not be in a * partition in the first place, but this didn't interact * well with sendmail. */ error = priv_check_cred(cred, PRIV_MAC_PARTITION, 0); } return (error); }
static int shm_chown(struct file *fp, uid_t uid, gid_t gid, struct ucred *active_cred, struct thread *td) { struct shmfd *shmfd; int error; error = 0; shmfd = fp->f_data; mtx_lock(&shm_timestamp_lock); #ifdef MAC error = mac_posixshm_check_setowner(active_cred, shmfd, uid, gid); if (error != 0) goto out; #endif if (uid == (uid_t)-1) uid = shmfd->shm_uid; if (gid == (gid_t)-1) gid = shmfd->shm_gid; if (((uid != shmfd->shm_uid && uid != active_cred->cr_uid) || (gid != shmfd->shm_gid && !groupmember(gid, active_cred))) && (error = priv_check_cred(active_cred, PRIV_VFS_CHOWN, 0))) goto out; shmfd->shm_uid = uid; shmfd->shm_gid = gid; out: mtx_unlock(&shm_timestamp_lock); return (error); }
/* * Test whether the specified credentials have the privilege * in question. * * A kernel thread without a process context is assumed to have * the privilege in question. In situations where the caller always * expect a cred to exist, the cred should be passed separately and * priv_check_cred() should be used instead of priv_check(). * * Returns 0 or error. * * MPSAFE */ int priv_check(struct thread *td, int priv) { if (td->td_lwp != NULL) return priv_check_cred(td->td_ucred, priv, 0); return (0); }
/* * Set login name. */ int sys_setlogin(struct setlogin_args *uap) { struct thread *td = curthread; struct proc *p; struct ucred *cred; char buf[MAXLOGNAME]; int error; cred = td->td_ucred; p = td->td_proc; if ((error = priv_check_cred(cred, PRIV_PROC_SETLOGIN, 0))) return (error); bzero(buf, sizeof(buf)); error = copyinstr(uap->namebuf, buf, sizeof(buf), NULL); if (error == ENAMETOOLONG) error = EINVAL; if (error == 0) { lwkt_gettoken(&proc_token); memcpy(p->p_pgrp->pg_session->s_login, buf, sizeof(buf)); lwkt_reltoken(&proc_token); } return (error); }
int priv_check(struct thread *td, int priv) { KASSERT(td == curthread, ("priv_check: td != curthread")); return (priv_check_cred(td->td_ucred, priv, 0)); }
int secpolicy_vnode_setdac(kauth_cred_t cred, uid_t owner) { if (owner == cred->cr_uid) return (0); return (priv_check_cred(cred, PRIV_VFS_ADMIN, 0)); }
/* * Determine if the credentials have sufficient permissions for read * and write access. */ static int ksem_access(struct ksem *ks, struct ucred *ucred) { int error; error = vaccess(VREG, ks->ks_mode, ks->ks_uid, ks->ks_gid, VREAD | VWRITE, ucred, NULL); if (error) error = priv_check_cred(ucred, PRIV_SEM_WRITE, 0); return (error); }
/* * Perform chown operation on inode ip; * inode must be locked prior to call. */ static int ext2_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, struct thread *td) { struct inode *ip = VTOI(vp); uid_t ouid; gid_t ogid; int error = 0; if (uid == (uid_t)VNOVAL) uid = ip->i_uid; if (gid == (gid_t)VNOVAL) gid = ip->i_gid; /* * To modify the ownership of a file, must possess VADMIN * for that file. */ if ((error = VOP_ACCESS(vp, VADMIN, cred, td))) return (error); /* * To change the owner of a file, or change the group of a file * to a group of which we are not a member, the caller must * have privilege. */ if (uid != ip->i_uid || (gid != ip->i_gid && !groupmember(gid, cred))) { error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0); if (error) return (error); } ogid = ip->i_gid; ouid = ip->i_uid; ip->i_gid = gid; ip->i_uid = uid; ip->i_flag |= IN_CHANGE; if ((ip->i_mode & (ISUID | ISGID)) && (ouid != uid || ogid != gid)) { if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0) != 0) ip->i_mode &= ~(ISUID | ISGID); } return (0); }
static int tmpfs_write(struct vop_write_args *v) { struct vnode *vp; struct uio *uio; struct tmpfs_node *node; off_t oldsize; int error, ioflag; boolean_t extended; vp = v->a_vp; uio = v->a_uio; ioflag = v->a_ioflag; error = 0; node = VP_TO_TMPFS_NODE(vp); oldsize = node->tn_size; if (uio->uio_offset < 0 || vp->v_type != VREG) return (EINVAL); if (uio->uio_resid == 0) return (0); if (ioflag & IO_APPEND) uio->uio_offset = node->tn_size; if (uio->uio_offset + uio->uio_resid > VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize) return (EFBIG); if (vn_rlimit_fsize(vp, uio, uio->uio_td)) return (EFBIG); extended = uio->uio_offset + uio->uio_resid > node->tn_size; if (extended) { error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid, FALSE); if (error != 0) goto out; } error = uiomove_object(node->tn_reg.tn_aobj, node->tn_size, uio); node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | (extended ? TMPFS_NODE_CHANGED : 0); if (node->tn_mode & (S_ISUID | S_ISGID)) { if (priv_check_cred(v->a_cred, PRIV_VFS_RETAINSUGID, 0)) node->tn_mode &= ~(S_ISUID | S_ISGID); } if (error != 0) (void)tmpfs_reg_resize(vp, oldsize, TRUE); out: MPASS(IMPLIES(error == 0, uio->uio_resid == 0)); MPASS(IMPLIES(error != 0, oldsize == node->tn_size)); return (error); }
void secpolicy_setid_clear(struct vattr *vap, kauth_cred_t cred) { if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL)) return; if ((vap->va_mode & (S_ISUID | S_ISGID)) != 0) { if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0)) { vap->va_mask |= AT_MODE; vap->va_mode &= ~(S_ISUID|S_ISGID); } } }
static void ngc_attach(netmsg_t msg) { struct socket *so = msg->attach.base.nm_so; struct pru_attach_info *ai = msg->attach.nm_ai; struct ngpcb *const pcbp = sotongpcb(so); int error; if (priv_check_cred(ai->p_ucred, PRIV_ROOT, NULL_CRED_OKAY) != 0) error = EPERM; else if (pcbp != NULL) error = EISCONN; else error = ng_attach_cntl(so); lwkt_replymsg(&msg->attach.base.lmsg, error); }
/* * mlock system call handler * * mlock_args(const void *addr, size_t len) * * No requirements */ int sys_mlock(struct mlock_args *uap) { vm_offset_t addr; vm_offset_t tmpaddr; vm_size_t size, pageoff; struct thread *td = curthread; struct proc *p = td->td_proc; int error; addr = (vm_offset_t) uap->addr; size = uap->len; pageoff = (addr & PAGE_MASK); addr -= pageoff; size += pageoff; size = (vm_size_t) round_page(size); if (size < uap->len) /* wrap */ return(EINVAL); tmpaddr = addr + size; /* workaround gcc4 opt */ if (tmpaddr < addr) /* wrap */ return (EINVAL); if (atop(size) + vmstats.v_wire_count > vm_page_max_wired) return (EAGAIN); /* * We do not need to synchronize against other threads updating ucred; * they update p->ucred, which is synchronized into td_ucred ourselves. */ #ifdef pmap_wired_count if (size + ptoa(pmap_wired_count(vm_map_pmap(&p->p_vmspace->vm_map))) > p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur) { return (ENOMEM); } #else error = priv_check_cred(td->td_ucred, PRIV_ROOT, 0); if (error) { return (error); } #endif error = vm_map_unwire(&p->p_vmspace->vm_map, addr, addr + size, FALSE); return (error == KERN_SUCCESS ? 0 : ENOMEM); }
int sys_setgroups(struct setgroups_args *uap) { struct proc *p = curproc; struct ucred *cr; u_int ngrp; int error; lwkt_gettoken(&proc_token); cr = p->p_ucred; if ((error = priv_check_cred(cr, PRIV_CRED_SETGROUPS, 0))) goto done; ngrp = uap->gidsetsize; if (ngrp > NGROUPS) { error = EINVAL; goto done; } /* * XXX A little bit lazy here. We could test if anything has * changed before cratom() and setting P_SUGID. */ cr = cratom(&p->p_ucred); if (ngrp < 1) { /* * 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. */ cr->cr_ngroups = 1; } else { error = copyin(uap->gidset, cr->cr_groups, ngrp * sizeof(gid_t)); if (error) goto done; cr->cr_ngroups = ngrp; } setsugid(); error = 0; done: lwkt_reltoken(&proc_token); return (error); }
/* * Return zero if p1 can fondle p2, return errno (EPERM/ESRCH) otherwise. */ int p_trespass(struct ucred *cr1, struct ucred *cr2) { if (cr1 == cr2) return (0); if (!PRISON_CHECK(cr1, cr2)) return (ESRCH); if (cr1->cr_ruid == cr2->cr_ruid) return (0); if (cr1->cr_uid == cr2->cr_ruid) return (0); if (cr1->cr_ruid == cr2->cr_uid) return (0); if (cr1->cr_uid == cr2->cr_uid) return (0); if (priv_check_cred(cr1, PRIV_PROC_TRESPASS, 0) == 0) return (0); return (EPERM); }
/* * setresgid(rgid, egid, sgid) is like setregid except control over the * saved gid is explicit. */ int sys_setresgid(struct setresgid_args *uap) { struct proc *p = curproc; struct ucred *cr; gid_t rgid, egid, sgid; int error; lwkt_gettoken(&proc_token); cr = p->p_ucred; rgid = uap->rgid; egid = uap->egid; sgid = uap->sgid; if (((rgid != (gid_t)-1 && rgid != cr->cr_rgid && rgid != cr->cr_svgid && rgid != cr->cr_groups[0]) || (egid != (gid_t)-1 && egid != cr->cr_rgid && egid != cr->cr_svgid && egid != cr->cr_groups[0]) || (sgid != (gid_t)-1 && sgid != cr->cr_rgid && sgid != cr->cr_svgid && sgid != cr->cr_groups[0])) && (error = priv_check_cred(cr, PRIV_CRED_SETRESGID, 0)) != 0) { goto done; } if (egid != (gid_t)-1 && cr->cr_groups[0] != egid) { cr = cratom(&p->p_ucred); cr->cr_groups[0] = egid; setsugid(); } if (rgid != (gid_t)-1 && cr->cr_rgid != rgid) { cr = cratom(&p->p_ucred); cr->cr_rgid = rgid; setsugid(); } if (sgid != (gid_t)-1 && cr->cr_svgid != sgid) { cr = cratom(&p->p_ucred); cr->cr_svgid = sgid; setsugid(); } error = 0; done: lwkt_reltoken(&proc_token); return (error); }
/* ARGSUSED */ int setaudit(struct thread *td, struct setaudit_args *uap) { struct ucred *newcred, *oldcred; struct auditinfo ai; int error; if (jailed(td->td_ucred)) return (ENOSYS); error = copyin(uap->auditinfo, &ai, sizeof(ai)); if (error) return (error); audit_arg_auditinfo(&ai); newcred = crget(); PROC_LOCK(td->td_proc); oldcred = td->td_proc->p_ucred; crcopy(newcred, oldcred); #ifdef MAC error = mac_cred_check_setaudit(oldcred, &ai); if (error) goto fail; #endif error = priv_check_cred(oldcred, PRIV_AUDIT_SETAUDIT, 0); if (error) goto fail; bzero(&newcred->cr_audit, sizeof(newcred->cr_audit)); newcred->cr_audit.ai_auid = ai.ai_auid; newcred->cr_audit.ai_mask = ai.ai_mask; newcred->cr_audit.ai_asid = ai.ai_asid; newcred->cr_audit.ai_termid.at_addr[0] = ai.ai_termid.machine; newcred->cr_audit.ai_termid.at_port = ai.ai_termid.port; newcred->cr_audit.ai_termid.at_type = AU_IPv4; td->td_proc->p_ucred = newcred; PROC_UNLOCK(td->td_proc); crfree(oldcred); return (0); fail: PROC_UNLOCK(td->td_proc); crfree(newcred); return (error); }
/* * setresuid(ruid, euid, suid) is like setreuid except control over the * saved uid is explicit. */ int sys_setresuid(struct setresuid_args *uap) { struct proc *p = curproc; struct ucred *cr; uid_t ruid, euid, suid; int error; lwkt_gettoken(&proc_token); cr = p->p_ucred; ruid = uap->ruid; euid = uap->euid; suid = uap->suid; if (((ruid != (uid_t)-1 && ruid != cr->cr_ruid && ruid != cr->cr_svuid && ruid != cr->cr_uid) || (euid != (uid_t)-1 && euid != cr->cr_ruid && euid != cr->cr_svuid && euid != cr->cr_uid) || (suid != (uid_t)-1 && suid != cr->cr_ruid && suid != cr->cr_svuid && suid != cr->cr_uid)) && (error = priv_check_cred(cr, PRIV_CRED_SETRESUID, 0)) != 0) { goto done; } if (euid != (uid_t)-1 && cr->cr_uid != euid) { cr = change_euid(euid); setsugid(); } if (ruid != (uid_t)-1 && cr->cr_ruid != ruid) { cr = change_ruid(ruid); setsugid(); } if (suid != (uid_t)-1 && cr->cr_svuid != suid) { cr = cratom(&p->p_ucred); cr->cr_svuid = suid; setsugid(); } error = 0; done: lwkt_reltoken(&proc_token); return (error); }