xref: /netbsd-src/sys/kern/kern_prot.c (revision e4d7c2e329d54c97e0c0bd3016bbe74f550c3d5e)
1 /*	$NetBSD: kern_prot.c,v 1.55 1999/09/28 14:47:03 bouyer 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 	register 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 	register struct sys_getgroups_args /* {
218 		syscallarg(int) gidsetsize;
219 		syscallarg(gid_t *) gidset;
220 	} */ *uap = v;
221 	register struct pcred *pc = p->p_cred;
222 	register 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 	register 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 	register struct sys_setpgid_args /* {
280 		syscallarg(int) pid;
281 		syscallarg(int) pgid;
282 	} */ *uap = v;
283 	register struct proc *targp;		/* target process */
284 	register 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 || !inferior(targp))
291 			return (ESRCH);
292 		if (targp->p_session != curp->p_session)
293 			return (EPERM);
294 		if (targp->p_flag & P_EXEC)
295 			return (EACCES);
296 	} else
297 		targp = curp;
298 	if (SESS_LEADER(targp))
299 		return (EPERM);
300 	if (SCARG(uap, pgid) == 0)
301 		SCARG(uap, pgid) = targp->p_pid;
302 	else if (SCARG(uap, pgid) != targp->p_pid)
303 		if ((pgrp = pgfind(SCARG(uap, pgid))) == 0 ||
304 	            pgrp->pg_session != curp->p_session)
305 			return (EPERM);
306 	return (enterpgrp(targp, SCARG(uap, pgid), 0));
307 }
308 
309 /* ARGSUSED */
310 int
311 sys_setuid(p, v, retval)
312 	struct proc *p;
313 	void *v;
314 	register_t *retval;
315 {
316 	struct sys_setuid_args /* {
317 		syscallarg(uid_t) uid;
318 	} */ *uap = v;
319 	register struct pcred *pc = p->p_cred;
320 	register uid_t uid;
321 	int error;
322 
323 	uid = SCARG(uap, uid);
324 	if (uid != pc->p_ruid &&
325 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
326 		return (error);
327 	/*
328 	 * Everything's okay, do it.
329 	 * Transfer proc count to new user.
330 	 * Copy credentials so other references do not see our changes.
331 	 */
332 	(void)chgproccnt(pc->p_ruid, -1);
333 	(void)chgproccnt(uid, 1);
334 	pc->pc_ucred = crcopy(pc->pc_ucred);
335 	pc->pc_ucred->cr_uid = uid;
336 	pc->p_ruid = uid;
337 	pc->p_svuid = uid;
338 	p_sugid(p);
339 	return (0);
340 }
341 
342 /* ARGSUSED */
343 int
344 sys_seteuid(p, v, retval)
345 	struct proc *p;
346 	void *v;
347 	register_t *retval;
348 {
349 	struct sys_seteuid_args /* {
350 		syscallarg(uid_t) euid;
351 	} */ *uap = v;
352 	register struct pcred *pc = p->p_cred;
353 	register uid_t euid;
354 	int error;
355 
356 	euid = SCARG(uap, euid);
357 	if (euid != pc->p_ruid && euid != pc->p_svuid &&
358 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
359 		return (error);
360 	/*
361 	 * Everything's okay, do it.  Copy credentials so other references do
362 	 * not see our changes.
363 	 */
364 	pc->pc_ucred = crcopy(pc->pc_ucred);
365 	pc->pc_ucred->cr_uid = euid;
366 	p_sugid(p);
367 	return (0);
368 }
369 
370 int
371 sys_setreuid(p, v, retval)
372 	struct proc *p;
373 	void *v;
374 	register_t *retval;
375 {
376 	struct sys_setreuid_args /* {
377 		syscallarg(uid_t) ruid;
378 		syscallarg(uid_t) euid;
379 	} */ *uap = v;
380 	register struct pcred *pc = p->p_cred;
381 	register uid_t ruid, euid;
382 	int error;
383 
384 	ruid = SCARG(uap, ruid);
385 	euid = SCARG(uap, euid);
386 
387 	if (ruid != (uid_t)-1 &&
388 	    ruid != pc->p_ruid && ruid != pc->pc_ucred->cr_uid &&
389 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
390 		return (error);
391 
392 	if (euid != (uid_t)-1 &&
393 	    euid != pc->p_ruid && euid != pc->pc_ucred->cr_uid &&
394 	    euid != pc->p_svuid &&
395 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
396 		return (error);
397 
398 	if (euid != (uid_t)-1) {
399 		pc->pc_ucred = crcopy(pc->pc_ucred);
400 		pc->pc_ucred->cr_uid = euid;
401 	}
402 
403 	if (ruid != (uid_t)-1) {
404 		(void)chgproccnt(pc->p_ruid, -1);
405 		(void)chgproccnt(ruid, 1);
406 		pc->p_ruid = ruid;
407 		pc->p_svuid = pc->pc_ucred->cr_uid;
408 	}
409 
410 	if (euid != (uid_t)-1 && ruid != (uid_t)-1)
411 		p_sugid(p);
412 	return (0);
413 }
414 
415 /* ARGSUSED */
416 int
417 sys_setgid(p, v, retval)
418 	struct proc *p;
419 	void *v;
420 	register_t *retval;
421 {
422 	struct sys_setgid_args /* {
423 		syscallarg(gid_t) gid;
424 	} */ *uap = v;
425 	register struct pcred *pc = p->p_cred;
426 	register gid_t gid;
427 	int error;
428 
429 	gid = SCARG(uap, gid);
430 	if (gid != pc->p_rgid &&
431 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
432 		return (error);
433 	pc->pc_ucred = crcopy(pc->pc_ucred);
434 	pc->pc_ucred->cr_gid = gid;
435 	pc->p_rgid = gid;
436 	pc->p_svgid = gid;
437 	p_sugid(p);
438 	return (0);
439 }
440 
441 /* ARGSUSED */
442 int
443 sys_setegid(p, v, retval)
444 	struct proc *p;
445 	void *v;
446 	register_t *retval;
447 {
448 	struct sys_setegid_args /* {
449 		syscallarg(gid_t) egid;
450 	} */ *uap = v;
451 	register struct pcred *pc = p->p_cred;
452 	register gid_t egid;
453 	int error;
454 
455 	egid = SCARG(uap, egid);
456 	if (egid != pc->p_rgid && egid != pc->p_svgid &&
457 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
458 		return (error);
459 	pc->pc_ucred = crcopy(pc->pc_ucred);
460 	pc->pc_ucred->cr_gid = egid;
461 	p_sugid(p);
462 	return (0);
463 }
464 
465 int
466 sys_setregid(p, v, retval)
467 	struct proc *p;
468 	void *v;
469 	register_t *retval;
470 {
471 	struct sys_setregid_args /* {
472 		syscallarg(gid_t) rgid;
473 		syscallarg(gid_t) egid;
474 	} */ *uap = v;
475 	register struct pcred *pc = p->p_cred;
476 	register gid_t rgid, egid;
477 	int error;
478 
479 	rgid = SCARG(uap, rgid);
480 	egid = SCARG(uap, egid);
481 
482 	if (rgid != (gid_t)-1 &&
483 	    rgid != pc->p_rgid && rgid != pc->pc_ucred->cr_gid &&
484 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
485 		return (error);
486 
487 	if (egid != (gid_t)-1 &&
488 	    egid != pc->p_rgid && egid != pc->pc_ucred->cr_gid &&
489 	    egid != pc->p_svgid &&
490 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
491 		return (error);
492 
493 	if (egid != (gid_t)-1) {
494 		pc->pc_ucred = crcopy(pc->pc_ucred);
495 		pc->pc_ucred->cr_gid = egid;
496 	}
497 
498 	if (rgid != (gid_t)-1) {
499 		pc->p_rgid = rgid;
500 		pc->p_svgid = pc->pc_ucred->cr_gid;
501 	}
502 
503 	if (egid != (gid_t)-1 && rgid != (gid_t)-1)
504 		p_sugid(p);
505 	return (0);
506 }
507 
508 /* ARGSUSED */
509 int
510 sys_setgroups(p, v, retval)
511 	struct proc *p;
512 	void *v;
513 	register_t *retval;
514 {
515 	struct sys_setgroups_args /* {
516 		syscallarg(int) gidsetsize;
517 		syscallarg(const gid_t *) gidset;
518 	} */ *uap = v;
519 	register struct pcred *pc = p->p_cred;
520 	register int ngrp;
521 	int error;
522 
523 	if ((error = suser(pc->pc_ucred, &p->p_acflag)) != 0)
524 		return (error);
525 	ngrp = SCARG(uap, gidsetsize);
526 	if ((u_int)ngrp > NGROUPS)
527 		return (EINVAL);
528 	pc->pc_ucred = crcopy(pc->pc_ucred);
529 	error = copyin(SCARG(uap, gidset), pc->pc_ucred->cr_groups,
530 	    ngrp * sizeof(gid_t));
531 	if (error)
532 		return (error);
533 	pc->pc_ucred->cr_ngroups = ngrp;
534 	p_sugid(p);
535 	return (0);
536 }
537 
538 /*
539  * Check if gid is a member of the group set.
540  */
541 int
542 groupmember(gid, cred)
543 	gid_t gid;
544 	register struct ucred *cred;
545 {
546 	register gid_t *gp;
547 	gid_t *egp;
548 
549 	egp = &(cred->cr_groups[cred->cr_ngroups]);
550 	for (gp = cred->cr_groups; gp < egp; gp++)
551 		if (*gp == gid)
552 			return (1);
553 	return (0);
554 }
555 
556 /*
557  * Test whether the specified credentials imply "super-user"
558  * privilege; if so, and we have accounting info, set the flag
559  * indicating use of super-powers.
560  * Returns 0 or error.
561  */
562 int
563 suser(cred, acflag)
564 	struct ucred *cred;
565 	u_short *acflag;
566 {
567 	if (cred->cr_uid == 0) {
568 		if (acflag)
569 			*acflag |= ASU;
570 		return (0);
571 	}
572 	return (EPERM);
573 }
574 
575 /*
576  * Allocate a zeroed cred structure.
577  */
578 struct ucred *
579 crget()
580 {
581 	register struct ucred *cr;
582 
583 	MALLOC(cr, struct ucred *, sizeof(*cr), M_CRED, M_WAITOK);
584 	memset((caddr_t)cr, 0, sizeof(*cr));
585 	cr->cr_ref = 1;
586 	return (cr);
587 }
588 
589 /*
590  * Free a cred structure.
591  * Throws away space when ref count gets to 0.
592  */
593 void
594 crfree(cr)
595 	struct ucred *cr;
596 {
597 	int s;
598 
599 	s = splimp();				/* ??? */
600 	if (--cr->cr_ref == 0)
601 		FREE((caddr_t)cr, M_CRED);
602 	(void) splx(s);
603 }
604 
605 /*
606  * Copy cred structure to a new one and free the old one.
607  */
608 struct ucred *
609 crcopy(cr)
610 	struct ucred *cr;
611 {
612 	struct ucred *newcr;
613 
614 	if (cr->cr_ref == 1)
615 		return (cr);
616 	newcr = crget();
617 	*newcr = *cr;
618 	crfree(cr);
619 	newcr->cr_ref = 1;
620 	return (newcr);
621 }
622 
623 /*
624  * Dup cred struct to a new held one.
625  */
626 struct ucred *
627 crdup(cr)
628 	struct ucred *cr;
629 {
630 	struct ucred *newcr;
631 
632 	newcr = crget();
633 	*newcr = *cr;
634 	newcr->cr_ref = 1;
635 	return (newcr);
636 }
637 
638 /*
639  * Get login name, if available.
640  */
641 /* ARGSUSED */
642 int
643 sys___getlogin(p, v, retval)
644 	struct proc *p;
645 	void *v;
646 	register_t *retval;
647 {
648 	struct sys___getlogin_args /* {
649 		syscallarg(char *) namebuf;
650 		syscallarg(size_t) namelen;
651 	} */ *uap = v;
652 
653 	if (SCARG(uap, namelen) > sizeof(p->p_pgrp->pg_session->s_login))
654 		SCARG(uap, namelen) = sizeof(p->p_pgrp->pg_session->s_login);
655 	return (copyout((caddr_t) p->p_pgrp->pg_session->s_login,
656 	    (caddr_t) SCARG(uap, namebuf), SCARG(uap, namelen)));
657 }
658 
659 /*
660  * Set login name.
661  */
662 /* ARGSUSED */
663 int
664 sys_setlogin(p, v, retval)
665 	struct proc *p;
666 	void *v;
667 	register_t *retval;
668 {
669 	struct sys_setlogin_args /* {
670 		syscallarg(const char *) namebuf;
671 	} */ *uap = v;
672 	int error;
673 
674 	if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
675 		return (error);
676 	error = copyinstr(SCARG(uap, namebuf), p->p_pgrp->pg_session->s_login,
677 	    sizeof(p->p_pgrp->pg_session->s_login) - 1, (size_t *)0);
678 	if (error == ENAMETOOLONG)
679 		error = EINVAL;
680 	return (error);
681 }
682