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