xref: /openbsd-src/sys/kern/kern_prot.c (revision 62a742911104f98b9185b2c6b6007d9b1c36396c)
1 /*	$OpenBSD: kern_prot.c,v 1.12 1997/11/17 05:57:45 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. 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 
57 #include <sys/mount.h>
58 #include <sys/syscallargs.h>
59 
60 /* ARGSUSED */
61 int
62 sys_getpid(p, v, retval)
63 	struct proc *p;
64 	void *v;
65 	register_t *retval;
66 {
67 
68 	*retval = p->p_pid;
69 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) || defined(COMPAT_IBCS2) || \
70     defined(COMPAT_FREEBSD) || defined(COMPAT_BSDOS)
71 	retval[1] = p->p_pptr->p_pid;
72 #endif
73 	return (0);
74 }
75 
76 /* ARGSUSED */
77 int
78 sys_getppid(p, v, retval)
79 	struct proc *p;
80 	void *v;
81 	register_t *retval;
82 {
83 
84 	*retval = p->p_pptr->p_pid;
85 	return (0);
86 }
87 
88 /* Get process group ID; note that POSIX getpgrp takes no parameter */
89 int
90 sys_getpgrp(p, v, retval)
91 	struct proc *p;
92 	void *v;
93 	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 int
104 sys_getpgid(p, v, retval)
105 	struct proc *p;
106 	void *v;
107 	register_t *retval;
108 {
109 	register struct sys_getpgid_args /* {
110 		syscallarg(pid_t) pid;
111 	} */ *uap = v;
112 
113 	if (SCARG(uap, pid) == 0)
114 		goto found;
115 	if ((p = pfind(SCARG(uap, pid))) == 0)
116 		return (ESRCH);
117 found:
118 	*retval = p->p_pgid;
119 	return 0;
120 }
121 
122 int
123 sys_getsid(p, v, retval)
124         struct proc *p;
125         void *v;
126         register_t *retval;
127 {
128         register struct sys_getsid_args /* {
129                 syscallarg(pid_t) pid;
130         } */ *uap = v;
131 
132 	if (SCARG(uap, pid) == 0)
133 		goto found;
134 	if ((p == pfind(SCARG(uap, pid))) == 0)
135 		return (ESRCH);
136 found:
137 	*retval = p->p_pgrp->pg_session->s_leader->p_pid;
138 	return 0;
139 }
140 
141 /* ARGSUSED */
142 int
143 sys_getuid(p, v, retval)
144 	struct proc *p;
145 	void *v;
146 	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(p, v, retval)
160 	struct proc *p;
161 	void *v;
162 	register_t *retval;
163 {
164 
165 	*retval = p->p_ucred->cr_uid;
166 	return (0);
167 }
168 
169 /* ARGSUSED */
170 int
171 sys_issetugid(p, v, retval)
172 	struct proc *p;
173 	void *v;
174 	register_t *retval;
175 {
176 	if (p->p_flag & P_SUGIDEXEC)
177 		*retval = 1;
178 	else
179 		*retval = 0;
180 	return (0);
181 }
182 
183 /* ARGSUSED */
184 int
185 sys_getgid(p, v, retval)
186 	struct proc *p;
187 	void *v;
188 	register_t *retval;
189 {
190 
191 	*retval = p->p_cred->p_rgid;
192 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) || defined(COMPAT_FREEBSD) || defined(COMPAT_BSDOS)
193 	retval[1] = p->p_ucred->cr_gid;
194 #endif
195 	return (0);
196 }
197 
198 /*
199  * Get effective group ID.  The "egid" is groups[0], and could be obtained
200  * via getgroups.  This syscall exists because it is somewhat painful to do
201  * correctly in a library function.
202  */
203 /* ARGSUSED */
204 int
205 sys_getegid(p, v, retval)
206 	struct proc *p;
207 	void *v;
208 	register_t *retval;
209 {
210 
211 	*retval = p->p_ucred->cr_gid;
212 	return (0);
213 }
214 
215 int
216 sys_getgroups(p, v, retval)
217 	struct proc *p;
218 	void *v;
219 	register_t *retval;
220 {
221 	register struct sys_getgroups_args /* {
222 		syscallarg(u_int) gidsetsize;
223 		syscallarg(gid_t *) gidset;
224 	} */ *uap = v;
225 	register struct pcred *pc = p->p_cred;
226 	register u_int ngrp;
227 	int error;
228 
229 	if ((ngrp = SCARG(uap, gidsetsize)) == 0) {
230 		*retval = pc->pc_ucred->cr_ngroups;
231 		return (0);
232 	}
233 	if (ngrp < pc->pc_ucred->cr_ngroups)
234 		return (EINVAL);
235 	ngrp = pc->pc_ucred->cr_ngroups;
236 	error = copyout((caddr_t)pc->pc_ucred->cr_groups,
237 	    (caddr_t)SCARG(uap, gidset), ngrp * sizeof(gid_t));
238 	if (error)
239 		return (error);
240 	*retval = ngrp;
241 	return (0);
242 }
243 
244 /* ARGSUSED */
245 int
246 sys_setsid(p, v, retval)
247 	register struct proc *p;
248 	void *v;
249 	register_t *retval;
250 {
251 
252 	if (p->p_pgid == p->p_pid || pgfind(p->p_pid)) {
253 		return (EPERM);
254 	} else {
255 		(void)enterpgrp(p, p->p_pid, 1);
256 		*retval = p->p_pid;
257 		return (0);
258 	}
259 }
260 
261 /*
262  * set process group (setpgid/old setpgrp)
263  *
264  * caller does setpgid(targpid, targpgid)
265  *
266  * pid must be caller or child of caller (ESRCH)
267  * if a child
268  *	pid must be in same session (EPERM)
269  *	pid can't have done an exec (EACCES)
270  * if pgid != pid
271  * 	there must exist some pid in same session having pgid (EPERM)
272  * pid must not be session leader (EPERM)
273  */
274 /* ARGSUSED */
275 int
276 sys_setpgid(curp, v, retval)
277 	struct proc *curp;
278 	void *v;
279 	register_t *retval;
280 {
281 	register struct sys_setpgid_args /* {
282 		syscallarg(int) pid;
283 		syscallarg(int) pgid;
284 	} */ *uap = v;
285 	register struct proc *targp;		/* target process */
286 	register struct pgrp *pgrp;		/* target pgrp */
287 
288 #ifdef COMPAT_09
289 	SCARG(uap, pid)  = (short) SCARG(uap, pid);		/* XXX */
290 	SCARG(uap, pgid) = (short) SCARG(uap, pgid);		/* XXX */
291 #endif
292 
293 	if (SCARG(uap, pgid) < 0)
294 		return (EINVAL);
295 
296 	if (SCARG(uap, pid) != 0 && SCARG(uap, pid) != curp->p_pid) {
297 		if ((targp = pfind(SCARG(uap, pid))) == 0 || !inferior(targp))
298 			return (ESRCH);
299 		if (targp->p_session != curp->p_session)
300 			return (EPERM);
301 		if (targp->p_flag & P_EXEC)
302 			return (EACCES);
303 	} else
304 		targp = curp;
305 	if (SESS_LEADER(targp))
306 		return (EPERM);
307 	if (SCARG(uap, pgid) == 0)
308 		SCARG(uap, pgid) = targp->p_pid;
309 	else if (SCARG(uap, pgid) != targp->p_pid)
310 		if ((pgrp = pgfind(SCARG(uap, pgid))) == 0 ||
311 		    pgrp->pg_session != curp->p_session)
312 			return (EPERM);
313 	return (enterpgrp(targp, SCARG(uap, pgid), 0));
314 }
315 
316 /* ARGSUSED */
317 int
318 sys_setuid(p, v, retval)
319 	struct proc *p;
320 	void *v;
321 	register_t *retval;
322 {
323 	struct sys_setuid_args /* {
324 		syscallarg(uid_t) uid;
325 	} */ *uap = v;
326 	register struct pcred *pc = p->p_cred;
327 	register uid_t uid;
328 	int error;
329 
330 #ifdef COMPAT_09				/* XXX */
331 	uid = (u_short)SCARG(uap, uid);
332 #else
333 	uid = SCARG(uap, uid);
334 #endif
335 	if (uid != pc->p_ruid &&
336 	    uid != pc->p_svuid &&
337 	    uid != pc->pc_ucred->cr_uid &&
338 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
339 		return (error);
340 	/*
341 	 * Everything's okay, do it.
342 	 */
343 	if (uid == pc->pc_ucred->cr_uid ||
344 	    suser(pc->pc_ucred, &p->p_acflag) == 0) {
345 		/*
346 		 * Transfer proc count to new user.
347 		 */
348 		if (uid != pc->p_ruid) {
349 			(void)chgproccnt(pc->p_ruid, -1);
350 			(void)chgproccnt(uid, 1);
351 		}
352 		pc->p_ruid = uid;
353 		pc->p_svuid = uid;
354 	}
355 	/*
356 	 * Copy credentials so other references do not see our changes.
357 	 */
358 	pc->pc_ucred = crcopy(pc->pc_ucred);
359 	pc->pc_ucred->cr_uid = uid;
360 	p->p_flag |= P_SUGID;
361 	return (0);
362 }
363 
364 /* ARGSUSED */
365 int
366 sys_seteuid(p, v, retval)
367 	struct proc *p;
368 	void *v;
369 	register_t *retval;
370 {
371 	struct sys_seteuid_args /* {
372 		syscallarg(uid_t) euid;
373 	} */ *uap = v;
374 	register struct pcred *pc = p->p_cred;
375 	register uid_t euid;
376 	int error;
377 
378 #ifdef COMPAT_09				/* XXX */
379 	euid = (u_short)SCARG(uap, euid);
380 #else
381 	euid = SCARG(uap, euid);
382 #endif
383 	if (euid != pc->p_ruid && euid != pc->p_svuid &&
384 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
385 		return (error);
386 	/*
387 	 * Everything's okay, do it.  Copy credentials so other references do
388 	 * not see our changes.
389 	 */
390 	pc->pc_ucred = crcopy(pc->pc_ucred);
391 	pc->pc_ucred->cr_uid = euid;
392 	p->p_flag |= P_SUGID;
393 	return (0);
394 }
395 
396 /* ARGSUSED */
397 int
398 sys_setgid(p, v, retval)
399 	struct proc *p;
400 	void *v;
401 	register_t *retval;
402 {
403 	struct sys_setgid_args /* {
404 		syscallarg(gid_t) gid;
405 	} */ *uap = v;
406 	register struct pcred *pc = p->p_cred;
407 	register gid_t gid;
408 	int error;
409 
410 #ifdef COMPAT_09				/* XXX */
411 	gid = (u_short)SCARG(uap, gid);
412 #else
413 	gid = SCARG(uap, gid);
414 #endif
415 	if (gid != pc->p_rgid &&
416 	    gid != pc->p_svgid &&
417 	    gid != pc->pc_ucred->cr_gid &&
418 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
419 		return (error);
420 	if (gid == pc->pc_ucred->cr_gid ||
421 	    suser(pc->pc_ucred, &p->p_acflag) == 0) {
422 		pc->p_rgid = gid;
423 		pc->p_svgid = gid;
424 	}
425 	pc->pc_ucred = crcopy(pc->pc_ucred);
426 	pc->pc_ucred->cr_gid = gid;
427 	p->p_flag |= P_SUGID;
428 	return (0);
429 }
430 
431 /* ARGSUSED */
432 int
433 sys_setegid(p, v, retval)
434 	struct proc *p;
435 	void *v;
436 	register_t *retval;
437 {
438 	struct sys_setegid_args /* {
439 		syscallarg(gid_t) egid;
440 	} */ *uap = v;
441 	register struct pcred *pc = p->p_cred;
442 	register gid_t egid;
443 	int error;
444 
445 #ifdef COMPAT_09				/* XXX */
446 	egid = (u_short)SCARG(uap, egid);
447 #else
448 	egid = SCARG(uap, egid);
449 #endif
450 	if (egid != pc->p_rgid && egid != pc->p_svgid &&
451 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
452 		return (error);
453 	pc->pc_ucred = crcopy(pc->pc_ucred);
454 	pc->pc_ucred->cr_gid = egid;
455 	p->p_flag |= P_SUGID;
456 	return (0);
457 }
458 
459 /* ARGSUSED */
460 int
461 sys_setgroups(p, v, retval)
462 	struct proc *p;
463 	void *v;
464 	register_t *retval;
465 {
466 	struct sys_setgroups_args /* {
467 		syscallarg(u_int) gidsetsize;
468 		syscallarg(gid_t *) gidset;
469 	} */ *uap = v;
470 	register struct pcred *pc = p->p_cred;
471 	register u_int ngrp;
472 	int error;
473 
474 	if ((error = suser(pc->pc_ucred, &p->p_acflag)) != 0)
475 		return (error);
476 	ngrp = SCARG(uap, gidsetsize);
477 	if (ngrp > NGROUPS)
478 		return (EINVAL);
479 	pc->pc_ucred = crcopy(pc->pc_ucred);
480 	error = copyin((caddr_t)SCARG(uap, gidset),
481 		       (caddr_t)pc->pc_ucred->cr_groups, ngrp * sizeof(gid_t));
482 	if (error)
483 		return (error);
484 	pc->pc_ucred->cr_ngroups = ngrp;
485 	p->p_flag |= P_SUGID;
486 	return (0);
487 }
488 
489 /*
490  * Check if gid is a member of the group set.
491  */
492 int
493 groupmember(gid, cred)
494 	gid_t gid;
495 	register struct ucred *cred;
496 {
497 	register gid_t *gp;
498 	gid_t *egp;
499 
500 	egp = &(cred->cr_groups[cred->cr_ngroups]);
501 	for (gp = cred->cr_groups; gp < egp; gp++)
502 		if (*gp == gid)
503 			return (1);
504 	return (0);
505 }
506 
507 /*
508  * Test whether the specified credentials imply "super-user"
509  * privilege; if so, and we have accounting info, set the flag
510  * indicating use of super-powers.
511  * Returns 0 or error.
512  */
513 int
514 suser(cred, acflag)
515 	struct ucred *cred;
516 	u_short *acflag;
517 {
518 	if (cred->cr_uid == 0) {
519 		if (acflag)
520 			*acflag |= ASU;
521 		return (0);
522 	}
523 	return (EPERM);
524 }
525 
526 /*
527  * Allocate a zeroed cred structure.
528  */
529 struct ucred *
530 crget()
531 {
532 	register struct ucred *cr;
533 
534 	MALLOC(cr, struct ucred *, sizeof(*cr), M_CRED, M_WAITOK);
535 	bzero((caddr_t)cr, sizeof(*cr));
536 	cr->cr_ref = 1;
537 	return (cr);
538 }
539 
540 /*
541  * Free a cred structure.
542  * Throws away space when ref count gets to 0.
543  */
544 void
545 crfree(cr)
546 	struct ucred *cr;
547 {
548 	int s;
549 
550 	s = splimp();				/* ??? */
551 	if (--cr->cr_ref == 0)
552 		FREE((caddr_t)cr, M_CRED);
553 	(void) splx(s);
554 }
555 
556 /*
557  * Copy cred structure to a new one and free the old one.
558  */
559 struct ucred *
560 crcopy(cr)
561 	struct ucred *cr;
562 {
563 	struct ucred *newcr;
564 
565 	if (cr->cr_ref == 1)
566 		return (cr);
567 	newcr = crget();
568 	*newcr = *cr;
569 	crfree(cr);
570 	newcr->cr_ref = 1;
571 	return (newcr);
572 }
573 
574 /*
575  * Dup cred struct to a new held one.
576  */
577 struct ucred *
578 crdup(cr)
579 	struct ucred *cr;
580 {
581 	struct ucred *newcr;
582 
583 	newcr = crget();
584 	*newcr = *cr;
585 	newcr->cr_ref = 1;
586 	return (newcr);
587 }
588 
589 /*
590  * Get login name, if available.
591  */
592 /* ARGSUSED */
593 int
594 sys_getlogin(p, v, retval)
595 	struct proc *p;
596 	void *v;
597 	register_t *retval;
598 {
599 	struct sys_getlogin_args /* {
600 		syscallarg(char *) namebuf;
601 		syscallarg(u_int) namelen;
602 	} */ *uap = v;
603 
604 	if (SCARG(uap, namelen) > sizeof (p->p_pgrp->pg_session->s_login))
605 		SCARG(uap, namelen) = sizeof (p->p_pgrp->pg_session->s_login);
606 	return (copyout((caddr_t) p->p_pgrp->pg_session->s_login,
607 	    (caddr_t) SCARG(uap, namebuf), SCARG(uap, namelen)));
608 }
609 
610 /*
611  * Set login name.
612  */
613 /* ARGSUSED */
614 int
615 sys_setlogin(p, v, retval)
616 	struct proc *p;
617 	void *v;
618 	register_t *retval;
619 {
620 	struct sys_setlogin_args /* {
621 		syscallarg(char *) namebuf;
622 	} */ *uap = v;
623 	int error;
624 
625 	if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
626 		return (error);
627 	error = copyinstr((caddr_t) SCARG(uap, namebuf),
628 	    (caddr_t) p->p_pgrp->pg_session->s_login,
629 	    sizeof (p->p_pgrp->pg_session->s_login), (size_t *)0);
630 	if (error == ENAMETOOLONG)
631 		error = EINVAL;
632 	return (error);
633 }
634