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