xref: /openbsd-src/sys/kern/kern_prot.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: kern_prot.c,v 1.18 2001/06/22 23:55:24 art 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. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgement:
23  *	This product includes software developed by the University of
24  *	California, Berkeley and its contributors.
25  * 4. Neither the name of the University nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  *
41  *	@(#)kern_prot.c	8.6 (Berkeley) 1/21/94
42  */
43 
44 /*
45  * System calls related to processes and protection
46  */
47 
48 #include <sys/param.h>
49 #include <sys/acct.h>
50 #include <sys/systm.h>
51 #include <sys/ucred.h>
52 #include <sys/proc.h>
53 #include <sys/timeb.h>
54 #include <sys/times.h>
55 #include <sys/malloc.h>
56 #include <sys/filedesc.h>
57 
58 #include <sys/mount.h>
59 #include <sys/syscallargs.h>
60 
61 /* ARGSUSED */
62 int
63 sys_getpid(p, v, retval)
64 	struct proc *p;
65 	void *v;
66 	register_t *retval;
67 {
68 
69 	*retval = p->p_pid;
70 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) || defined(COMPAT_IBCS2) || \
71     defined(COMPAT_FREEBSD) || defined(COMPAT_BSDOS)
72 	retval[1] = p->p_pptr->p_pid;
73 #endif
74 	return (0);
75 }
76 
77 /* ARGSUSED */
78 int
79 sys_getppid(p, v, retval)
80 	struct proc *p;
81 	void *v;
82 	register_t *retval;
83 {
84 
85 	*retval = p->p_pptr->p_pid;
86 	return (0);
87 }
88 
89 /* Get process group ID; note that POSIX getpgrp takes no parameter */
90 int
91 sys_getpgrp(p, v, retval)
92 	struct proc *p;
93 	void *v;
94 	register_t *retval;
95 {
96 
97 	*retval = p->p_pgrp->pg_id;
98 	return (0);
99 }
100 
101 /*
102  * SysVR.4 compatible getpgid()
103  */
104 pid_t
105 sys_getpgid(curp, v, retval)
106 	struct proc *curp;
107 	void *v;
108 	register_t *retval;
109 {
110 	struct sys_getpgid_args /* {
111 		syscallarg(pid_t) pid;
112 	} */ *uap = v;
113 	struct proc *targp = curp;
114 
115 	if (SCARG(uap, pid) == 0 || SCARG(uap, pid) == curp->p_pid)
116 		goto found;
117 	if ((targp = pfind(SCARG(uap, pid))) == NULL)
118 		return (ESRCH);
119 	if (targp->p_session != curp->p_session)
120 		return (EPERM);
121 found:
122 	*retval = targp->p_pgid;
123 	return (0);
124 }
125 
126 pid_t
127 sys_getsid(curp, v, retval)
128 	struct proc *curp;
129 	void *v;
130 	register_t *retval;
131 {
132 	struct sys_getsid_args /* {
133 		syscallarg(pid_t) pid;
134 	} */ *uap = v;
135 	struct proc *targp = curp;
136 
137 	if (SCARG(uap, pid) == 0 || SCARG(uap, pid) == curp->p_pid)
138 		goto found;
139 	if ((targp = pfind(SCARG(uap, pid))) == NULL)
140 		return (ESRCH);
141 	if (targp->p_session != curp->p_session)
142 		return (EPERM);
143 found:
144 	/* Skip exiting processes */
145 	if (targp->p_pgrp->pg_session->s_leader == NULL)
146 		return (ESRCH);
147 	*retval = targp->p_pgrp->pg_session->s_leader->p_pid;
148 	return (0);
149 }
150 
151 /* ARGSUSED */
152 int
153 sys_getuid(p, v, retval)
154 	struct proc *p;
155 	void *v;
156 	register_t *retval;
157 {
158 
159 	*retval = p->p_cred->p_ruid;
160 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) || defined(COMPAT_IBCS2) || \
161     defined(COMPAT_FREEBSD) || defined(COMPAT_BSDOS)
162 	retval[1] = p->p_ucred->cr_uid;
163 #endif
164 	return (0);
165 }
166 
167 /* ARGSUSED */
168 int
169 sys_geteuid(p, v, retval)
170 	struct proc *p;
171 	void *v;
172 	register_t *retval;
173 {
174 
175 	*retval = p->p_ucred->cr_uid;
176 	return (0);
177 }
178 
179 /* ARGSUSED */
180 int
181 sys_issetugid(p, v, retval)
182 	struct proc *p;
183 	void *v;
184 	register_t *retval;
185 {
186 	if (p->p_flag & P_SUGIDEXEC)
187 		*retval = 1;
188 	else
189 		*retval = 0;
190 	return (0);
191 }
192 
193 /* ARGSUSED */
194 int
195 sys_getgid(p, v, retval)
196 	struct proc *p;
197 	void *v;
198 	register_t *retval;
199 {
200 
201 	*retval = p->p_cred->p_rgid;
202 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) || defined(COMPAT_FREEBSD) || defined(COMPAT_BSDOS)
203 	retval[1] = p->p_ucred->cr_gid;
204 #endif
205 	return (0);
206 }
207 
208 /*
209  * Get effective group ID.  The "egid" is groups[0], and could be obtained
210  * via getgroups.  This syscall exists because it is somewhat painful to do
211  * correctly in a library function.
212  */
213 /* ARGSUSED */
214 int
215 sys_getegid(p, v, retval)
216 	struct proc *p;
217 	void *v;
218 	register_t *retval;
219 {
220 
221 	*retval = p->p_ucred->cr_gid;
222 	return (0);
223 }
224 
225 int
226 sys_getgroups(p, v, retval)
227 	struct proc *p;
228 	void *v;
229 	register_t *retval;
230 {
231 	struct sys_getgroups_args /* {
232 		syscallarg(u_int) gidsetsize;
233 		syscallarg(gid_t *) gidset;
234 	} */ *uap = v;
235 	struct pcred *pc = p->p_cred;
236 	u_int ngrp;
237 	int error;
238 
239 	if ((ngrp = SCARG(uap, gidsetsize)) == 0) {
240 		*retval = pc->pc_ucred->cr_ngroups;
241 		return (0);
242 	}
243 	if (ngrp < pc->pc_ucred->cr_ngroups)
244 		return (EINVAL);
245 	ngrp = pc->pc_ucred->cr_ngroups;
246 	error = copyout((caddr_t)pc->pc_ucred->cr_groups,
247 	    (caddr_t)SCARG(uap, gidset), ngrp * sizeof(gid_t));
248 	if (error)
249 		return (error);
250 	*retval = ngrp;
251 	return (0);
252 }
253 
254 /* ARGSUSED */
255 int
256 sys_setsid(p, v, retval)
257 	struct proc *p;
258 	void *v;
259 	register_t *retval;
260 {
261 
262 	if (p->p_pgid == p->p_pid || pgfind(p->p_pid)) {
263 		return (EPERM);
264 	} else {
265 		(void)enterpgrp(p, p->p_pid, 1);
266 		*retval = p->p_pid;
267 		return (0);
268 	}
269 }
270 
271 /*
272  * set process group (setpgid/old setpgrp)
273  *
274  * caller does setpgid(targpid, targpgid)
275  *
276  * pid must be caller or child of caller (ESRCH)
277  * if a child
278  *	pid must be in same session (EPERM)
279  *	pid can't have done an exec (EACCES)
280  * if pgid != pid
281  * 	there must exist some pid in same session having pgid (EPERM)
282  * pid must not be session leader (EPERM)
283  */
284 /* ARGSUSED */
285 int
286 sys_setpgid(curp, v, retval)
287 	struct proc *curp;
288 	void *v;
289 	register_t *retval;
290 {
291 	struct sys_setpgid_args /* {
292 		syscallarg(pid_t) pid;
293 		syscallarg(int) pgid;
294 	} */ *uap = v;
295 	struct proc *targp;		/* target process */
296 	struct pgrp *pgrp;		/* target pgrp */
297 	pid_t pid;
298 	int pgid;
299 
300 	pid = SCARG(uap, pid);
301 	pgid = SCARG(uap, pgid);
302 
303 	if (pgid < 0)
304 		return (EINVAL);
305 
306 	if (pid != 0 && pid != curp->p_pid) {
307 		if ((targp = pfind(pid)) == 0 || !inferior(targp))
308 			return (ESRCH);
309 		if (targp->p_session != curp->p_session)
310 			return (EPERM);
311 		if (targp->p_flag & P_EXEC)
312 			return (EACCES);
313 	} else
314 		targp = curp;
315 	if (SESS_LEADER(targp))
316 		return (EPERM);
317 	if (pgid == 0)
318 		pgid = targp->p_pid;
319 	else if (pgid != targp->p_pid)
320 		if ((pgrp = pgfind(pgid)) == 0 ||
321 		    pgrp->pg_session != curp->p_session)
322 			return (EPERM);
323 	return (enterpgrp(targp, pgid, 0));
324 }
325 
326 /* ARGSUSED */
327 int
328 sys_setuid(p, v, retval)
329 	struct proc *p;
330 	void *v;
331 	register_t *retval;
332 {
333 	struct sys_setuid_args /* {
334 		syscallarg(uid_t) uid;
335 	} */ *uap = v;
336 	struct pcred *pc = p->p_cred;
337 	uid_t uid;
338 	int error;
339 
340 	uid = SCARG(uap, uid);
341 
342 	if (pc->pc_ucred->cr_uid == uid &&
343 	    pc->p_ruid == uid &&
344 	    pc->p_svuid == uid)
345 		return (0);
346 
347 	if (uid != pc->p_ruid &&
348 	    uid != pc->p_svuid &&
349 	    uid != pc->pc_ucred->cr_uid &&
350 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
351 		return (error);
352 
353 	/*
354 	 * Everything's okay, do it.
355 	 */
356 	if (uid == pc->pc_ucred->cr_uid ||
357 	    suser(pc->pc_ucred, &p->p_acflag) == 0) {
358 		/*
359 		 * Transfer proc count to new user.
360 		 */
361 		if (uid != pc->p_ruid) {
362 			(void)chgproccnt(pc->p_ruid, -1);
363 			(void)chgproccnt(uid, 1);
364 		}
365 		pc->p_ruid = uid;
366 		pc->p_svuid = uid;
367 	}
368 
369 	/*
370 	 * Copy credentials so other references do not see our changes.
371 	 */
372 	pc->pc_ucred = crcopy(pc->pc_ucred);
373 	pc->pc_ucred->cr_uid = uid;
374 	p->p_flag |= P_SUGID;
375 	return (0);
376 }
377 
378 /* ARGSUSED */
379 int
380 sys_seteuid(p, v, retval)
381 	struct proc *p;
382 	void *v;
383 	register_t *retval;
384 {
385 	struct sys_seteuid_args /* {
386 		syscallarg(uid_t) euid;
387 	} */ *uap = v;
388 	struct pcred *pc = p->p_cred;
389 	uid_t euid;
390 	int error;
391 
392 	euid = SCARG(uap, euid);
393 
394 	if (pc->pc_ucred->cr_uid == euid)
395 		return (0);
396 
397 	if (euid != pc->p_ruid && euid != pc->p_svuid &&
398 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
399 		return (error);
400 
401 	/*
402 	 * Copy credentials so other references do not see our changes.
403 	 */
404 	pc->pc_ucred = crcopy(pc->pc_ucred);
405 	pc->pc_ucred->cr_uid = euid;
406 	p->p_flag |= P_SUGID;
407 	return (0);
408 }
409 
410 /* ARGSUSED */
411 int
412 sys_setgid(p, v, retval)
413 	struct proc *p;
414 	void *v;
415 	register_t *retval;
416 {
417 	struct sys_setgid_args /* {
418 		syscallarg(gid_t) gid;
419 	} */ *uap = v;
420 	struct pcred *pc = p->p_cred;
421 	gid_t gid;
422 	int error;
423 
424 	gid = SCARG(uap, gid);
425 
426 	if (pc->pc_ucred->cr_gid == gid &&
427 	    pc->p_rgid == gid &&
428 	    pc->p_svgid == gid)
429 		return (0);
430 
431 	if (gid != pc->p_rgid &&
432 	    gid != pc->p_svgid &&
433 	    gid != pc->pc_ucred->cr_gid &&
434 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
435 		return (error);
436 
437 	if (gid == pc->pc_ucred->cr_gid ||
438 	    suser(pc->pc_ucred, &p->p_acflag) == 0) {
439 		pc->p_rgid = gid;
440 		pc->p_svgid = gid;
441 	}
442 
443 	/*
444 	 * Copy credentials so other references do not see our changes.
445 	 */
446 	pc->pc_ucred = crcopy(pc->pc_ucred);
447 	pc->pc_ucred->cr_gid = gid;
448 	p->p_flag |= P_SUGID;
449 	return (0);
450 }
451 
452 /* ARGSUSED */
453 int
454 sys_setegid(p, v, retval)
455 	struct proc *p;
456 	void *v;
457 	register_t *retval;
458 {
459 	struct sys_setegid_args /* {
460 		syscallarg(gid_t) egid;
461 	} */ *uap = v;
462 	struct pcred *pc = p->p_cred;
463 	gid_t egid;
464 	int error;
465 
466 	egid = SCARG(uap, egid);
467 
468 	if (pc->pc_ucred->cr_gid == egid)
469 		return (0);
470 
471 	if (egid != pc->p_rgid && egid != pc->p_svgid &&
472 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
473 		return (error);
474 
475 	/*
476 	 * Copy credentials so other references do not see our changes.
477 	 */
478 	pc->pc_ucred = crcopy(pc->pc_ucred);
479 	pc->pc_ucred->cr_gid = egid;
480 	p->p_flag |= P_SUGID;
481 	return (0);
482 }
483 
484 /* ARGSUSED */
485 int
486 sys_setgroups(p, v, retval)
487 	struct proc *p;
488 	void *v;
489 	register_t *retval;
490 {
491 	struct sys_setgroups_args /* {
492 		syscallarg(u_int) gidsetsize;
493 		syscallarg(gid_t *) gidset;
494 	} */ *uap = v;
495 	struct pcred *pc = p->p_cred;
496 	u_int ngrp;
497 	int error;
498 
499 	if ((error = suser(pc->pc_ucred, &p->p_acflag)) != 0)
500 		return (error);
501 	ngrp = SCARG(uap, gidsetsize);
502 	if (ngrp > NGROUPS)
503 		return (EINVAL);
504 	pc->pc_ucred = crcopy(pc->pc_ucred);
505 	error = copyin((caddr_t)SCARG(uap, gidset),
506 	    (caddr_t)pc->pc_ucred->cr_groups, ngrp * sizeof(gid_t));
507 	if (error)
508 		return (error);
509 	pc->pc_ucred->cr_ngroups = ngrp;
510 	p->p_flag |= P_SUGID;
511 	return (0);
512 }
513 
514 /*
515  * Check if gid is a member of the group set.
516  */
517 int
518 groupmember(gid, cred)
519 	gid_t gid;
520 	struct ucred *cred;
521 {
522 	gid_t *gp;
523 	gid_t *egp;
524 
525 	egp = &(cred->cr_groups[cred->cr_ngroups]);
526 	for (gp = cred->cr_groups; gp < egp; gp++)
527 		if (*gp == gid)
528 			return (1);
529 	return (0);
530 }
531 
532 /*
533  * Test whether the specified credentials imply "super-user"
534  * privilege; if so, and we have accounting info, set the flag
535  * indicating use of super-powers.
536  * Returns 0 or error.
537  */
538 int
539 suser(cred, acflag)
540 	struct ucred *cred;
541 	u_short *acflag;
542 {
543 	if (cred->cr_uid == 0) {
544 		if (acflag)
545 			*acflag |= ASU;
546 		return (0);
547 	}
548 	return (EPERM);
549 }
550 
551 /*
552  * Allocate a zeroed cred structure.
553  */
554 struct ucred *
555 crget()
556 {
557 	struct ucred *cr;
558 
559 	MALLOC(cr, struct ucred *, sizeof(*cr), M_CRED, M_WAITOK);
560 	bzero((caddr_t)cr, sizeof(*cr));
561 	cr->cr_ref = 1;
562 	return (cr);
563 }
564 
565 /*
566  * Free a cred structure.
567  * Throws away space when ref count gets to 0.
568  */
569 void
570 crfree(cr)
571 	struct ucred *cr;
572 {
573 	int s;
574 
575 	s = splimp();				/* ??? */
576 	if (--cr->cr_ref == 0)
577 		FREE((caddr_t)cr, M_CRED);
578 	(void) splx(s);
579 }
580 
581 /*
582  * Copy cred structure to a new one and free the old one.
583  */
584 struct ucred *
585 crcopy(cr)
586 	struct ucred *cr;
587 {
588 	struct ucred *newcr;
589 
590 	if (cr->cr_ref == 1)
591 		return (cr);
592 	newcr = crget();
593 	*newcr = *cr;
594 	crfree(cr);
595 	newcr->cr_ref = 1;
596 	return (newcr);
597 }
598 
599 /*
600  * Dup cred struct to a new held one.
601  */
602 struct ucred *
603 crdup(cr)
604 	struct ucred *cr;
605 {
606 	struct ucred *newcr;
607 
608 	newcr = crget();
609 	*newcr = *cr;
610 	newcr->cr_ref = 1;
611 	return (newcr);
612 }
613 
614 /*
615  * Get login name, if available.
616  */
617 /* ARGSUSED */
618 int
619 sys_getlogin(p, v, retval)
620 	struct proc *p;
621 	void *v;
622 	register_t *retval;
623 {
624 	struct sys_getlogin_args /* {
625 		syscallarg(char *) namebuf;
626 		syscallarg(u_int) namelen;
627 	} */ *uap = v;
628 
629 	if (SCARG(uap, namelen) > sizeof (p->p_pgrp->pg_session->s_login))
630 		SCARG(uap, namelen) = sizeof (p->p_pgrp->pg_session->s_login);
631 	return (copyout((caddr_t) p->p_pgrp->pg_session->s_login,
632 	    (caddr_t) SCARG(uap, namebuf), SCARG(uap, namelen)));
633 }
634 
635 /*
636  * Set login name.
637  */
638 /* ARGSUSED */
639 int
640 sys_setlogin(p, v, retval)
641 	struct proc *p;
642 	void *v;
643 	register_t *retval;
644 {
645 	struct sys_setlogin_args /* {
646 		syscallarg(char *) namebuf;
647 	} */ *uap = v;
648 	int error;
649 
650 	if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
651 		return (error);
652 	error = copyinstr((caddr_t) SCARG(uap, namebuf),
653 	    (caddr_t) p->p_pgrp->pg_session->s_login,
654 	    sizeof (p->p_pgrp->pg_session->s_login), (size_t *)0);
655 	if (error == ENAMETOOLONG)
656 		error = EINVAL;
657 	return (error);
658 }
659 
660 /*
661  * Check if a process is allowed to raise its privileges.
662  */
663 int
664 proc_cansugid(struct proc *p)
665 {
666 	/* ptrace(2)d processes shouldn't. */
667 	if ((p->p_flag & P_TRACED) != 0)
668 		return (0);
669 
670 	/* proceses with shared filedescriptors shouldn't. */
671 	if (p->p_fd->fd_refcnt > 1)
672 		return (0);
673 
674 	/* Allow. */
675 	return (1);
676 }
677