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