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