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