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