xref: /openbsd-src/sys/kern/kern_prot.c (revision 86fea0ff7a710ad2158aa65bb194c96a275dbea6)
1 /*	$OpenBSD: kern_prot.c,v 1.57 2014/03/24 00:19:48 guenther Exp $	*/
2 /*	$NetBSD: kern_prot.c,v 1.33 1996/02/09 18:59:42 christos Exp $	*/
3 
4 /*
5  * Copyright (c) 1982, 1986, 1989, 1990, 1991, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  * (c) UNIX System Laboratories, Inc.
8  * All or some portions of this file are derived from material licensed
9  * to the University of California by American Telephone and Telegraph
10  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
11  * the permission of UNIX System Laboratories, Inc.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *	@(#)kern_prot.c	8.6 (Berkeley) 1/21/94
38  */
39 
40 /*
41  * System calls related to processes and protection
42  */
43 
44 #include <sys/param.h>
45 #include <sys/acct.h>
46 #include <sys/systm.h>
47 #include <sys/ucred.h>
48 #include <sys/proc.h>
49 #include <sys/times.h>
50 #include <sys/malloc.h>
51 #include <sys/filedesc.h>
52 #include <sys/pool.h>
53 
54 #include <sys/mount.h>
55 #include <sys/syscallargs.h>
56 
57 #ifdef	__HAVE_MD_TCB
58 # include <machine/tcb.h>
59 #endif
60 
61 /* ARGSUSED */
62 int
63 sys_getpid(struct proc *p, void *v, register_t *retval)
64 {
65 
66 	*retval = p->p_p->ps_pid;
67 	return (0);
68 }
69 
70 /* ARGSUSED */
71 int
72 sys_getthrid(struct proc *p, void *v, register_t *retval)
73 {
74 
75 	*retval = p->p_pid + THREAD_PID_OFFSET;
76 	return (0);
77 }
78 
79 /* ARGSUSED */
80 int
81 sys_getppid(struct proc *p, void *v, register_t *retval)
82 {
83 
84 	*retval = p->p_p->ps_pptr->ps_pid;
85 	return (0);
86 }
87 
88 /* Get process group ID; note that POSIX getpgrp takes no parameter */
89 int
90 sys_getpgrp(struct proc *p, void *v, register_t *retval)
91 {
92 
93 	*retval = p->p_p->ps_pgrp->pg_id;
94 	return (0);
95 }
96 
97 /*
98  * SysVR.4 compatible getpgid()
99  */
100 int
101 sys_getpgid(struct proc *curp, void *v, register_t *retval)
102 {
103 	struct sys_getpgid_args /* {
104 		syscallarg(pid_t) pid;
105 	} */ *uap = v;
106 	struct process *targpr = curp->p_p;
107 
108 	if (SCARG(uap, pid) == 0 || SCARG(uap, pid) == targpr->ps_pid)
109 		goto found;
110 	if ((targpr = prfind(SCARG(uap, pid))) == NULL)
111 		return (ESRCH);
112 	if (targpr->ps_session != curp->p_p->ps_session)
113 		return (EPERM);
114 found:
115 	*retval = targpr->ps_pgid;
116 	return (0);
117 }
118 
119 int
120 sys_getsid(struct proc *curp, void *v, register_t *retval)
121 {
122 	struct sys_getsid_args /* {
123 		syscallarg(pid_t) pid;
124 	} */ *uap = v;
125 	struct process *targpr = curp->p_p;
126 
127 	if (SCARG(uap, pid) == 0 || SCARG(uap, pid) == targpr->ps_pid)
128 		goto found;
129 	if ((targpr = prfind(SCARG(uap, pid))) == NULL)
130 		return (ESRCH);
131 	if (targpr->ps_session != curp->p_p->ps_session)
132 		return (EPERM);
133 found:
134 	/* Skip exiting processes */
135 	if (targpr->ps_pgrp->pg_session->s_leader == NULL)
136 		return (ESRCH);
137 	*retval = targpr->ps_pgrp->pg_session->s_leader->ps_pid;
138 	return (0);
139 }
140 
141 /* ARGSUSED */
142 int
143 sys_getuid(struct proc *p, void *v, register_t *retval)
144 {
145 
146 	*retval = p->p_cred->p_ruid;
147 	return (0);
148 }
149 
150 /* ARGSUSED */
151 int
152 sys_geteuid(struct proc *p, void *v, register_t *retval)
153 {
154 
155 	*retval = p->p_ucred->cr_uid;
156 	return (0);
157 }
158 
159 /* ARGSUSED */
160 int
161 sys_issetugid(struct proc *p, void *v, register_t *retval)
162 {
163 	if (p->p_p->ps_flags & PS_SUGIDEXEC)
164 		*retval = 1;
165 	else
166 		*retval = 0;
167 	return (0);
168 }
169 
170 /* ARGSUSED */
171 int
172 sys_getgid(struct proc *p, void *v, register_t *retval)
173 {
174 
175 	*retval = p->p_cred->p_rgid;
176 	return (0);
177 }
178 
179 /*
180  * Get effective group ID.  The "egid" is groups[0], and could be obtained
181  * via getgroups.  This syscall exists because it is somewhat painful to do
182  * correctly in a library function.
183  */
184 /* ARGSUSED */
185 int
186 sys_getegid(struct proc *p, void *v, register_t *retval)
187 {
188 
189 	*retval = p->p_ucred->cr_gid;
190 	return (0);
191 }
192 
193 int
194 sys_getgroups(struct proc *p, void *v, register_t *retval)
195 {
196 	struct sys_getgroups_args /* {
197 		syscallarg(int) gidsetsize;
198 		syscallarg(gid_t *) gidset;
199 	} */ *uap = v;
200 	struct pcred *pc = p->p_cred;
201 	u_int ngrp;
202 	int error;
203 
204 	if ((ngrp = SCARG(uap, gidsetsize)) == 0) {
205 		*retval = pc->pc_ucred->cr_ngroups;
206 		return (0);
207 	}
208 	if (ngrp < pc->pc_ucred->cr_ngroups)
209 		return (EINVAL);
210 	ngrp = pc->pc_ucred->cr_ngroups;
211 	error = copyout((caddr_t)pc->pc_ucred->cr_groups,
212 	    (caddr_t)SCARG(uap, gidset), ngrp * sizeof(gid_t));
213 	if (error)
214 		return (error);
215 	*retval = ngrp;
216 	return (0);
217 }
218 
219 /* ARGSUSED */
220 int
221 sys_setsid(struct proc *p, void *v, register_t *retval)
222 {
223 	struct session *newsess;
224 	struct pgrp *newpgrp;
225 	struct process *pr = p->p_p;
226 	pid_t pid = pr->ps_pid;
227 
228 	newsess = pool_get(&session_pool, PR_WAITOK);
229 	newpgrp = pool_get(&pgrp_pool, PR_WAITOK);
230 
231 	if (pr->ps_pgid == pid || pgfind(pid)) {
232 		pool_put(&pgrp_pool, newpgrp);
233 		pool_put(&session_pool, newsess);
234 		return (EPERM);
235 	} else {
236 		(void) enterpgrp(pr, pid, newpgrp, newsess);
237 		*retval = pid;
238 		return (0);
239 	}
240 }
241 
242 /*
243  * set process group (setpgid/old setpgrp)
244  *
245  * caller does setpgid(targpid, targpgid)
246  *
247  * pid must be caller or child of caller (ESRCH)
248  * if a child
249  *	pid must be in same session (EPERM)
250  *	pid can't have done an exec (EACCES)
251  * if pgid != pid
252  * 	there must exist some pid in same session having pgid (EPERM)
253  * pid must not be session leader (EPERM)
254  */
255 /* ARGSUSED */
256 int
257 sys_setpgid(struct proc *curp, void *v, register_t *retval)
258 {
259 	struct sys_setpgid_args /* {
260 		syscallarg(pid_t) pid;
261 		syscallarg(int) pgid;
262 	} */ *uap = v;
263 	struct process *curpr = curp->p_p;
264 	struct process *targpr;		/* target process */
265 	struct pgrp *pgrp, *newpgrp;	/* target pgrp */
266 	pid_t pid;
267 	int pgid, error;
268 
269 	pid = SCARG(uap, pid);
270 	pgid = SCARG(uap, pgid);
271 
272 	if (pgid < 0)
273 		return (EINVAL);
274 
275 	newpgrp = pool_get(&pgrp_pool, PR_WAITOK);
276 
277 	if (pid != 0 && pid != curpr->ps_pid) {
278 		if ((targpr = prfind(pid)) == 0 || !inferior(targpr, curpr)) {
279 			error = ESRCH;
280 			goto out;
281 		}
282 		if (targpr->ps_session != curpr->ps_session) {
283 			error = EPERM;
284 			goto out;
285 		}
286 		if (targpr->ps_flags & PS_EXEC) {
287 			error = EACCES;
288 			goto out;
289 		}
290 	} else
291 		targpr = curpr;
292 	if (SESS_LEADER(targpr)) {
293 		error = EPERM;
294 		goto out;
295 	}
296 	if (pgid == 0)
297 		pgid = targpr->ps_pid;
298 	else if (pgid != targpr->ps_pid)
299 		if ((pgrp = pgfind(pgid)) == 0 ||
300 		    pgrp->pg_session != curpr->ps_session) {
301 			error = EPERM;
302 			goto out;
303 		}
304 	return (enterpgrp(targpr, pgid, newpgrp, NULL));
305 out:
306 	pool_put(&pgrp_pool, newpgrp);
307 	return (error);
308 }
309 
310 /* ARGSUSED */
311 int
312 sys_getresuid(struct proc *p, void *v, register_t *retval)
313 {
314 	struct sys_getresuid_args /* {
315 		syscallarg(uid_t *) ruid;
316 		syscallarg(uid_t *) euid;
317 		syscallarg(uid_t *) suid;
318 	} */ *uap = v;
319 	struct pcred *pc = p->p_cred;
320 	uid_t *ruid, *euid, *suid;
321 	int error1 = 0, error2 = 0, error3 = 0;
322 
323 	ruid = SCARG(uap, ruid);
324 	euid = SCARG(uap, euid);
325 	suid = SCARG(uap, suid);
326 
327 	if (ruid != NULL)
328 		error1 = copyout(&pc->p_ruid, ruid, sizeof(*ruid));
329 	if (euid != NULL)
330 		error2 = copyout(&pc->pc_ucred->cr_uid, euid, sizeof(*euid));
331 	if (suid != NULL)
332 		error3 = copyout(&pc->p_svuid, suid, sizeof(*suid));
333 
334 	return (error1 ? error1 : error2 ? error2 : error3);
335 }
336 
337 /* ARGSUSED */
338 int
339 sys_setresuid(struct proc *p, void *v, register_t *retval)
340 {
341 	struct sys_setresuid_args /* {
342 		syscallarg(uid_t) ruid;
343 		syscallarg(uid_t) euid;
344 		syscallarg(uid_t) suid;
345 	} */ *uap = v;
346 	struct pcred *pc = p->p_cred;
347 	uid_t ruid, euid, suid;
348 	int error;
349 
350 	ruid = SCARG(uap, ruid);
351 	euid = SCARG(uap, euid);
352 	suid = SCARG(uap, suid);
353 
354 	if ((ruid == -1 || ruid == pc->p_ruid) &&
355 	    (euid == -1 || euid == pc->pc_ucred->cr_uid) &&
356 	    (suid == -1 || suid == pc->p_svuid))
357 		return (0);			/* no change */
358 
359 	/*
360 	 * Any of the real, effective, and saved uids may be changed
361 	 * to the current value of one of the three (root is not limited).
362 	 */
363 	if (ruid != (uid_t)-1 &&
364 	    ruid != pc->p_ruid &&
365 	    ruid != pc->pc_ucred->cr_uid &&
366 	    ruid != pc->p_svuid &&
367 	    (error = suser(p, 0)))
368 		return (error);
369 
370 	if (euid != (uid_t)-1 &&
371 	    euid != pc->p_ruid &&
372 	    euid != pc->pc_ucred->cr_uid &&
373 	    euid != pc->p_svuid &&
374 	    (error = suser(p, 0)))
375 		return (error);
376 
377 	if (suid != (uid_t)-1 &&
378 	    suid != pc->p_ruid &&
379 	    suid != pc->pc_ucred->cr_uid &&
380 	    suid != pc->p_svuid &&
381 	    (error = suser(p, 0)))
382 		return (error);
383 
384 	/*
385 	 * Note that unlike the other set*uid() calls, each
386 	 * uid type is set independently of the others.
387 	 */
388 	if (ruid != (uid_t)-1 && ruid != pc->p_ruid) {
389 		/*
390 		 * Transfer proc count to new user.
391 		 */
392 		(void)chgproccnt(pc->p_ruid, -1);
393 		(void)chgproccnt(ruid, 1);
394 		pc->p_ruid = ruid;
395 	}
396 	if (euid != (uid_t)-1 && euid != pc->pc_ucred->cr_uid) {
397 		/*
398 		 * Copy credentials so other references do not see our changes.
399 		 */
400 		pc->pc_ucred = crcopy(pc->pc_ucred);
401 		pc->pc_ucred->cr_uid = euid;
402 	}
403 	if (suid != (uid_t)-1 && suid != pc->p_svuid)
404 		pc->p_svuid = suid;
405 
406 	atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
407 	return (0);
408 }
409 
410 /* ARGSUSED */
411 int
412 sys_getresgid(struct proc *p, void *v, register_t *retval)
413 {
414 	struct sys_getresgid_args /* {
415 		syscallarg(gid_t *) rgid;
416 		syscallarg(gid_t *) egid;
417 		syscallarg(gid_t *) sgid;
418 	} */ *uap = v;
419 	struct pcred *pc = p->p_cred;
420 	gid_t *rgid, *egid, *sgid;
421 	int error1 = 0, error2 = 0, error3 = 0;
422 
423 	rgid = SCARG(uap, rgid);
424 	egid = SCARG(uap, egid);
425 	sgid = SCARG(uap, sgid);
426 
427 	if (rgid != NULL)
428 		error1 = copyout(&pc->p_rgid, rgid, sizeof(*rgid));
429 	if (egid != NULL)
430 		error2 = copyout(&pc->pc_ucred->cr_gid, egid, sizeof(*egid));
431 	if (sgid != NULL)
432 		error3 = copyout(&pc->p_svgid, sgid, sizeof(*sgid));
433 
434 	return (error1 ? error1 : error2 ? error2 : error3);
435 }
436 
437 /* ARGSUSED */
438 int
439 sys_setresgid(struct proc *p, void *v, register_t *retval)
440 {
441 	struct sys_setresgid_args /* {
442 		syscallarg(gid_t) rgid;
443 		syscallarg(gid_t) egid;
444 		syscallarg(gid_t) sgid;
445 	} */ *uap = v;
446 	struct pcred *pc = p->p_cred;
447 	gid_t rgid, egid, sgid;
448 	int error;
449 
450 	rgid = SCARG(uap, rgid);
451 	egid = SCARG(uap, egid);
452 	sgid = SCARG(uap, sgid);
453 
454 	if ((rgid == -1 || rgid == pc->p_rgid) &&
455 	    (egid == -1 || egid == pc->pc_ucred->cr_gid) &&
456 	    (sgid == -1 || sgid == pc->p_svgid))
457 		return (0);			/* no change */
458 
459 	/*
460 	 * Any of the real, effective, and saved gids may be changed
461 	 * to the current value of one of the three (root is not limited).
462 	 */
463 	if (rgid != (gid_t)-1 &&
464 	    rgid != pc->p_rgid &&
465 	    rgid != pc->pc_ucred->cr_gid &&
466 	    rgid != pc->p_svgid &&
467 	    (error = suser(p, 0)))
468 		return (error);
469 
470 	if (egid != (gid_t)-1 &&
471 	    egid != pc->p_rgid &&
472 	    egid != pc->pc_ucred->cr_gid &&
473 	    egid != pc->p_svgid &&
474 	    (error = suser(p, 0)))
475 		return (error);
476 
477 	if (sgid != (gid_t)-1 &&
478 	    sgid != pc->p_rgid &&
479 	    sgid != pc->pc_ucred->cr_gid &&
480 	    sgid != pc->p_svgid &&
481 	    (error = suser(p, 0)))
482 		return (error);
483 
484 	/*
485 	 * Note that unlike the other set*gid() calls, each
486 	 * gid type is set independently of the others.
487 	 */
488 	if (rgid != (gid_t)-1)
489 		pc->p_rgid = rgid;
490 	if (egid != (gid_t)-1) {
491 		/*
492 		 * Copy credentials so other references do not see our changes.
493 		 */
494 		pc->pc_ucred = crcopy(pc->pc_ucred);
495 		pc->pc_ucred->cr_gid = egid;
496 	}
497 	if (sgid != (gid_t)-1)
498 		pc->p_svgid = sgid;
499 
500 	atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
501 	return (0);
502 }
503 
504 /* ARGSUSED */
505 int
506 sys_setregid(struct proc *p, void *v, register_t *retval)
507 {
508 	struct sys_setregid_args /* {
509 		syscallarg(gid_t) rgid;
510 		syscallarg(gid_t) egid;
511 	} */ *uap = v;
512 	struct pcred *pc = p->p_cred;
513 	struct sys_setresgid_args sresgidargs;
514 	gid_t rgid, egid;
515 
516 	rgid = SCARG(&sresgidargs, rgid) = SCARG(uap, rgid);
517 	egid = SCARG(&sresgidargs, egid) = SCARG(uap, egid);
518 
519 	/*
520 	 * The saved gid presents a bit of a dilemma, as it did not
521 	 * exist when setregid(2) was conceived.  We only set the saved
522 	 * gid when the real gid is specified and either its value would
523 	 * change, or where the saved and effective gids are different.
524 	 */
525 	if (rgid != (gid_t)-1 && (rgid != pc->p_rgid ||
526 	    pc->p_svgid != (egid != (gid_t)-1 ? egid : pc->pc_ucred->cr_gid)))
527 		SCARG(&sresgidargs, sgid) = rgid;
528 	else
529 		SCARG(&sresgidargs, sgid) = (gid_t)-1;
530 
531 	return (sys_setresgid(p, &sresgidargs, retval));
532 }
533 
534 /* ARGSUSED */
535 int
536 sys_setreuid(struct proc *p, void *v, register_t *retval)
537 {
538 	struct sys_setreuid_args /* {
539 		syscallarg(uid_t) ruid;
540 		syscallarg(uid_t) euid;
541 	} */ *uap = v;
542 	struct pcred *pc = p->p_cred;
543 	struct sys_setresuid_args sresuidargs;
544 	uid_t ruid, euid;
545 
546 	ruid = SCARG(&sresuidargs, ruid) = SCARG(uap, ruid);
547 	euid = SCARG(&sresuidargs, euid) = SCARG(uap, euid);
548 
549 	/*
550 	 * The saved uid presents a bit of a dilemma, as it did not
551 	 * exist when setreuid(2) was conceived.  We only set the saved
552 	 * uid when the real uid is specified and either its value would
553 	 * change, or where the saved and effective uids are different.
554 	 */
555 	if (ruid != (uid_t)-1 && (ruid != pc->p_ruid ||
556 	    pc->p_svuid != (euid != (uid_t)-1 ? euid : pc->pc_ucred->cr_uid)))
557 		SCARG(&sresuidargs, suid) = ruid;
558 	else
559 		SCARG(&sresuidargs, suid) = (uid_t)-1;
560 
561 	return (sys_setresuid(p, &sresuidargs, retval));
562 }
563 
564 /* ARGSUSED */
565 int
566 sys_setuid(struct proc *p, void *v, register_t *retval)
567 {
568 	struct sys_setuid_args /* {
569 		syscallarg(uid_t) uid;
570 	} */ *uap = v;
571 	struct pcred *pc = p->p_cred;
572 	uid_t uid;
573 	int error;
574 
575 	uid = SCARG(uap, uid);
576 
577 	if (pc->pc_ucred->cr_uid == uid &&
578 	    pc->p_ruid == uid &&
579 	    pc->p_svuid == uid)
580 		return (0);
581 
582 	if (uid != pc->p_ruid &&
583 	    uid != pc->p_svuid &&
584 	    uid != pc->pc_ucred->cr_uid &&
585 	    (error = suser(p, 0)))
586 		return (error);
587 
588 	/*
589 	 * Everything's okay, do it.
590 	 */
591 	if (uid == pc->pc_ucred->cr_uid ||
592 	    suser(p, 0) == 0) {
593 		/*
594 		 * Transfer proc count to new user.
595 		 */
596 		if (uid != pc->p_ruid) {
597 			(void)chgproccnt(pc->p_ruid, -1);
598 			(void)chgproccnt(uid, 1);
599 		}
600 		pc->p_ruid = uid;
601 		pc->p_svuid = uid;
602 	}
603 
604 	/*
605 	 * Copy credentials so other references do not see our changes.
606 	 */
607 	pc->pc_ucred = crcopy(pc->pc_ucred);
608 	pc->pc_ucred->cr_uid = uid;
609 	atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
610 	return (0);
611 }
612 
613 /* ARGSUSED */
614 int
615 sys_seteuid(struct proc *p, void *v, register_t *retval)
616 {
617 	struct sys_seteuid_args /* {
618 		syscallarg(uid_t) euid;
619 	} */ *uap = v;
620 	struct pcred *pc = p->p_cred;
621 	uid_t euid;
622 	int error;
623 
624 	euid = SCARG(uap, euid);
625 
626 	if (pc->pc_ucred->cr_uid == euid)
627 		return (0);
628 
629 	if (euid != pc->p_ruid && euid != pc->p_svuid &&
630 	    (error = suser(p, 0)))
631 		return (error);
632 
633 	/*
634 	 * Copy credentials so other references do not see our changes.
635 	 */
636 	pc->pc_ucred = crcopy(pc->pc_ucred);
637 	pc->pc_ucred->cr_uid = euid;
638 	atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
639 	return (0);
640 }
641 
642 /* ARGSUSED */
643 int
644 sys_setgid(struct proc *p, void *v, register_t *retval)
645 {
646 	struct sys_setgid_args /* {
647 		syscallarg(gid_t) gid;
648 	} */ *uap = v;
649 	struct pcred *pc = p->p_cred;
650 	gid_t gid;
651 	int error;
652 
653 	gid = SCARG(uap, gid);
654 
655 	if (pc->pc_ucred->cr_gid == gid &&
656 	    pc->p_rgid == gid &&
657 	    pc->p_svgid == gid)
658 		return (0);
659 
660 	if (gid != pc->p_rgid &&
661 	    gid != pc->p_svgid &&
662 	    gid != pc->pc_ucred->cr_gid &&
663 	    (error = suser(p, 0)))
664 		return (error);
665 
666 	if (gid == pc->pc_ucred->cr_gid ||
667 	    suser(p, 0) == 0) {
668 		pc->p_rgid = gid;
669 		pc->p_svgid = gid;
670 	}
671 
672 	/*
673 	 * Copy credentials so other references do not see our changes.
674 	 */
675 	pc->pc_ucred = crcopy(pc->pc_ucred);
676 	pc->pc_ucred->cr_gid = gid;
677 	atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
678 	return (0);
679 }
680 
681 /* ARGSUSED */
682 int
683 sys_setegid(struct proc *p, void *v, register_t *retval)
684 {
685 	struct sys_setegid_args /* {
686 		syscallarg(gid_t) egid;
687 	} */ *uap = v;
688 	struct pcred *pc = p->p_cred;
689 	gid_t egid;
690 	int error;
691 
692 	egid = SCARG(uap, egid);
693 
694 	if (pc->pc_ucred->cr_gid == egid)
695 		return (0);
696 
697 	if (egid != pc->p_rgid && egid != pc->p_svgid &&
698 	    (error = suser(p, 0)))
699 		return (error);
700 
701 	/*
702 	 * Copy credentials so other references do not see our changes.
703 	 */
704 	pc->pc_ucred = crcopy(pc->pc_ucred);
705 	pc->pc_ucred->cr_gid = egid;
706 	atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
707 	return (0);
708 }
709 
710 /* ARGSUSED */
711 int
712 sys_setgroups(struct proc *p, void *v, register_t *retval)
713 {
714 	struct sys_setgroups_args /* {
715 		syscallarg(int) gidsetsize;
716 		syscallarg(const gid_t *) gidset;
717 	} */ *uap = v;
718 	struct pcred *pc = p->p_cred;
719 	u_int ngrp;
720 	int error;
721 
722 	if ((error = suser(p, 0)) != 0)
723 		return (error);
724 	ngrp = SCARG(uap, gidsetsize);
725 	if (ngrp > NGROUPS)
726 		return (EINVAL);
727 	pc->pc_ucred = crcopy(pc->pc_ucred);
728 	error = copyin((caddr_t)SCARG(uap, gidset),
729 	    (caddr_t)pc->pc_ucred->cr_groups, ngrp * sizeof(gid_t));
730 	if (error)
731 		return (error);
732 	pc->pc_ucred->cr_ngroups = ngrp;
733 	atomic_setbits_int(&p->p_p->ps_flags, PS_SUGID);
734 	return (0);
735 }
736 
737 /*
738  * Check if gid is a member of the group set.
739  */
740 int
741 groupmember(gid_t gid, struct ucred *cred)
742 {
743 	gid_t *gp;
744 	gid_t *egp;
745 
746 	if (cred->cr_gid == gid)
747 		return (1);
748 	egp = &(cred->cr_groups[cred->cr_ngroups]);
749 	for (gp = cred->cr_groups; gp < egp; gp++)
750 		if (*gp == gid)
751 			return (1);
752 	return (0);
753 }
754 
755 /*
756  * Test whether this process has special user powers.
757  * Returns 0 or error.
758  */
759 int
760 suser(struct proc *p, u_int flags)
761 {
762 	struct ucred *cred = p->p_ucred;
763 
764 	if (cred->cr_uid == 0) {
765 		if (!(flags & SUSER_NOACCT))
766 			p->p_p->ps_acflag |= ASU;
767 		return (0);
768 	}
769 	return (EPERM);
770 }
771 
772 /*
773  * replacement for old suser, for callers who don't have a process
774  */
775 int
776 suser_ucred(struct ucred *cred)
777 {
778 	if (cred->cr_uid == 0)
779 		return (0);
780 	return (EPERM);
781 }
782 
783 /*
784  * Allocate a zeroed cred structure.
785  */
786 struct ucred *
787 crget(void)
788 {
789 	struct ucred *cr;
790 
791 	cr = pool_get(&ucred_pool, PR_WAITOK|PR_ZERO);
792 	cr->cr_ref = 1;
793 	return (cr);
794 }
795 
796 /*
797  * Free a cred structure.
798  * Throws away space when ref count gets to 0.
799  */
800 void
801 crfree(struct ucred *cr)
802 {
803 
804 	if (--cr->cr_ref == 0)
805 		pool_put(&ucred_pool, cr);
806 }
807 
808 /*
809  * Copy cred structure to a new one and free the old one.
810  */
811 struct ucred *
812 crcopy(struct ucred *cr)
813 {
814 	struct ucred *newcr;
815 
816 	if (cr->cr_ref == 1)
817 		return (cr);
818 	newcr = crget();
819 	*newcr = *cr;
820 	crfree(cr);
821 	newcr->cr_ref = 1;
822 	return (newcr);
823 }
824 
825 /*
826  * Dup cred struct to a new held one.
827  */
828 struct ucred *
829 crdup(struct ucred *cr)
830 {
831 	struct ucred *newcr;
832 
833 	newcr = crget();
834 	*newcr = *cr;
835 	newcr->cr_ref = 1;
836 	return (newcr);
837 }
838 
839 /*
840  * Convert the userspace xucred to a kernel ucred
841  */
842 void
843 crfromxucred(struct ucred *cr, const struct xucred *xcr)
844 {
845 	cr->cr_ref = 1;
846 	cr->cr_uid = xcr->cr_uid;
847 	cr->cr_gid = xcr->cr_gid;
848 	cr->cr_ngroups = xcr->cr_ngroups;
849 	memcpy(cr->cr_groups, xcr->cr_groups,
850 	    sizeof(cr->cr_groups[0]) * xcr->cr_ngroups);
851 }
852 
853 /*
854  * Get login name, if available.
855  */
856 /* ARGSUSED */
857 int
858 sys_getlogin(struct proc *p, void *v, register_t *retval)
859 {
860 	struct sys_getlogin_args /* {
861 		syscallarg(char *) namebuf;
862 		syscallarg(u_int) namelen;
863 	} */ *uap = v;
864 	struct session *s = p->p_p->ps_pgrp->pg_session;
865 
866 	if (SCARG(uap, namelen) > sizeof(s->s_login))
867 		SCARG(uap, namelen) = sizeof(s->s_login);
868 	return (copyout((caddr_t)s->s_login,
869 	    (caddr_t)SCARG(uap, namebuf), SCARG(uap, namelen)));
870 }
871 
872 /*
873  * Set login name.
874  */
875 /* ARGSUSED */
876 int
877 sys_setlogin(struct proc *p, void *v, register_t *retval)
878 {
879 	struct sys_setlogin_args /* {
880 		syscallarg(const char *) namebuf;
881 	} */ *uap = v;
882 	struct session *s = p->p_p->ps_pgrp->pg_session;
883 	int error;
884 
885 	if ((error = suser(p, 0)) != 0)
886 		return (error);
887 	error = copyinstr((caddr_t)SCARG(uap, namebuf), (caddr_t)s->s_login,
888 	    sizeof(s->s_login), NULL);
889 	if (error == ENAMETOOLONG)
890 		error = EINVAL;
891 	return (error);
892 }
893 
894 /*
895  * Check if a process is allowed to raise its privileges.
896  */
897 int
898 proc_cansugid(struct proc *p)
899 {
900 	/* ptrace(2)d processes shouldn't. */
901 	if ((p->p_p->ps_flags & PS_TRACED) != 0)
902 		return (0);
903 
904 	/* processes with shared filedescriptors shouldn't. */
905 	if (p->p_fd->fd_refcnt > 1)
906 		return (0);
907 
908 	/* Allow. */
909 	return (1);
910 }
911 
912 /*
913  * Set address of the proc's thread-control-block
914  */
915 int
916 sys___set_tcb(struct proc *p, void *v, register_t *retval)
917 {
918 	struct sys___set_tcb_args /* {
919 		syscallarg(void *) tcb;
920 	} */ *uap = v;
921 
922 	TCB_SET(p, SCARG(uap, tcb));
923 	return (0);
924 }
925 
926 /*
927  * Get address of the proc's thread-control-block
928  */
929 int
930 sys___get_tcb(struct proc *p, void *v, register_t *retval)
931 {
932 	*retval = (register_t)TCB_GET(p);
933 	return (0);
934 }
935