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