xref: /netbsd-src/sys/kern/kern_prot.c (revision ce0bb6e8d2e560ecacbe865a848624f94498063b)
1 /*	$NetBSD: kern_prot.c,v 1.25 1995/03/09 12:05:43 mycroft 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.6 (Berkeley) 1/21/94
41  */
42 
43 /*
44  * System calls related to processes and protection
45  */
46 
47 #include <sys/param.h>
48 #include <sys/acct.h>
49 #include <sys/systm.h>
50 #include <sys/ucred.h>
51 #include <sys/proc.h>
52 #include <sys/timeb.h>
53 #include <sys/times.h>
54 #include <sys/malloc.h>
55 
56 #include <sys/mount.h>
57 #include <sys/syscallargs.h>
58 
59 /* ARGSUSED */
60 getpid(p, uap, retval)
61 	struct proc *p;
62 	void *uap;
63 	register_t *retval;
64 {
65 
66 	*retval = p->p_pid;
67 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) || defined(COMPAT_IBCS2)
68 	retval[1] = p->p_pptr->p_pid;
69 #endif
70 	return (0);
71 }
72 
73 /* ARGSUSED */
74 getppid(p, uap, retval)
75 	struct proc *p;
76 	void *uap;
77 	register_t *retval;
78 {
79 
80 	*retval = p->p_pptr->p_pid;
81 	return (0);
82 }
83 
84 /* Get process group ID; note that POSIX getpgrp takes no parameter */
85 getpgrp(p, uap, retval)
86 	struct proc *p;
87 	void *uap;
88 	register_t *retval;
89 {
90 
91 	*retval = p->p_pgrp->pg_id;
92 	return (0);
93 }
94 
95 /* ARGSUSED */
96 getuid(p, uap, retval)
97 	struct proc *p;
98 	void *uap;
99 	register_t *retval;
100 {
101 
102 	*retval = p->p_cred->p_ruid;
103 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) || defined(COMPAT_IBCS2)
104 	retval[1] = p->p_ucred->cr_uid;
105 #endif
106 	return (0);
107 }
108 
109 /* ARGSUSED */
110 geteuid(p, uap, retval)
111 	struct proc *p;
112 	void *uap;
113 	register_t *retval;
114 {
115 
116 	*retval = p->p_ucred->cr_uid;
117 	return (0);
118 }
119 
120 /* ARGSUSED */
121 getgid(p, uap, retval)
122 	struct proc *p;
123 	void *uap;
124 	register_t *retval;
125 {
126 
127 	*retval = p->p_cred->p_rgid;
128 #if defined(COMPAT_43) || defined(COMPAT_SUNOS)
129 	retval[1] = p->p_ucred->cr_groups[0];
130 #endif
131 	return (0);
132 }
133 
134 /*
135  * Get effective group ID.  The "egid" is groups[0], and could be obtained
136  * via getgroups.  This syscall exists because it is somewhat painful to do
137  * correctly in a library function.
138  */
139 /* ARGSUSED */
140 getegid(p, uap, retval)
141 	struct proc *p;
142 	void *uap;
143 	register_t *retval;
144 {
145 
146 	*retval = p->p_ucred->cr_groups[0];
147 	return (0);
148 }
149 
150 getgroups(p, uap, retval)
151 	struct proc *p;
152 	register struct	getgroups_args /* {
153 		syscallarg(u_int) gidsetsize;
154 		syscallarg(gid_t *) gidset;
155 	} */ *uap;
156 	register_t *retval;
157 {
158 	register struct pcred *pc = p->p_cred;
159 	register u_int ngrp;
160 	int error;
161 
162 	if ((ngrp = SCARG(uap, gidsetsize)) == 0) {
163 		*retval = pc->pc_ucred->cr_ngroups;
164 		return (0);
165 	}
166 	if (ngrp < pc->pc_ucred->cr_ngroups)
167 		return (EINVAL);
168 	ngrp = pc->pc_ucred->cr_ngroups;
169 	if (error = copyout((caddr_t)pc->pc_ucred->cr_groups,
170 	    (caddr_t)SCARG(uap, gidset), ngrp * sizeof(gid_t)))
171 		return (error);
172 	*retval = ngrp;
173 	return (0);
174 }
175 
176 /* ARGSUSED */
177 setsid(p, uap, retval)
178 	register struct proc *p;
179 	void *uap;
180 	register_t *retval;
181 {
182 
183 	if (p->p_pgid == p->p_pid || pgfind(p->p_pid)) {
184 		return (EPERM);
185 	} else {
186 		(void)enterpgrp(p, p->p_pid, 1);
187 		*retval = p->p_pid;
188 		return (0);
189 	}
190 }
191 
192 /*
193  * set process group (setpgid/old setpgrp)
194  *
195  * caller does setpgid(targpid, targpgid)
196  *
197  * pid must be caller or child of caller (ESRCH)
198  * if a child
199  *	pid must be in same session (EPERM)
200  *	pid can't have done an exec (EACCES)
201  * if pgid != pid
202  * 	there must exist some pid in same session having pgid (EPERM)
203  * pid must not be session leader (EPERM)
204  */
205 /* ARGSUSED */
206 setpgid(curp, uap, retval)
207 	struct proc *curp;
208 	register struct setpgid_args /* {
209 		syscallarg(int) pid;
210 		syscallarg(int) pgid;
211 	} */ *uap;
212 	register_t *retval;
213 {
214 	register struct proc *targp;		/* target process */
215 	register struct pgrp *pgrp;		/* target pgrp */
216 
217 #ifdef COMPAT_09
218 	SCARG(uap, pid)  = (short) SCARG(uap, pid);		/* XXX */
219 	SCARG(uap, pgid) = (short) SCARG(uap, pgid);		/* XXX */
220 #endif
221 
222 	if (SCARG(uap, pid) != 0 && SCARG(uap, pid) != curp->p_pid) {
223 		if ((targp = pfind(SCARG(uap, pid))) == 0 || !inferior(targp))
224 			return (ESRCH);
225 		if (targp->p_session != curp->p_session)
226 			return (EPERM);
227 		if (targp->p_flag & P_EXEC)
228 			return (EACCES);
229 	} else
230 		targp = curp;
231 	if (SESS_LEADER(targp))
232 		return (EPERM);
233 	if (SCARG(uap, pgid) == 0)
234 		SCARG(uap, pgid) = targp->p_pid;
235 	else if (SCARG(uap, pgid) != targp->p_pid)
236 		if ((pgrp = pgfind(SCARG(uap, pgid))) == 0 ||
237 	            pgrp->pg_session != curp->p_session)
238 			return (EPERM);
239 	return (enterpgrp(targp, SCARG(uap, pgid), 0));
240 }
241 
242 /* ARGSUSED */
243 setuid(p, uap, retval)
244 	struct proc *p;
245 	struct setuid_args /* {
246 		syscallarg(uid_t) uid;
247 	} */ *uap;
248 	register_t *retval;
249 {
250 	register struct pcred *pc = p->p_cred;
251 	register uid_t uid;
252 	int error;
253 
254 #ifdef COMPAT_09				/* XXX */
255 	uid = (u_short)SCARG(uap, uid);
256 #else
257 	uid = SCARG(uap, uid);
258 #endif
259 	if (uid != pc->p_ruid &&
260 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
261 		return (error);
262 	/*
263 	 * Everything's okay, do it.
264 	 * Transfer proc count to new user.
265 	 * Copy credentials so other references do not see our changes.
266 	 */
267 	(void)chgproccnt(pc->p_ruid, -1);
268 	(void)chgproccnt(uid, 1);
269 	pc->pc_ucred = crcopy(pc->pc_ucred);
270 	pc->pc_ucred->cr_uid = uid;
271 	pc->p_ruid = uid;
272 	pc->p_svuid = uid;
273 	p->p_flag |= P_SUGID;
274 	return (0);
275 }
276 
277 /* ARGSUSED */
278 seteuid(p, uap, retval)
279 	struct proc *p;
280 	struct seteuid_args /* {
281 		syscallarg(uid_t) euid;
282 	} */ *uap;
283 	register_t *retval;
284 {
285 	register struct pcred *pc = p->p_cred;
286 	register uid_t euid;
287 	int error;
288 
289 #ifdef COMPAT_09				/* XXX */
290 	euid = (u_short)SCARG(uap, euid);
291 #else
292 	euid = SCARG(uap, euid);
293 #endif
294 	if (euid != pc->p_ruid && euid != pc->p_svuid &&
295 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
296 		return (error);
297 	/*
298 	 * Everything's okay, do it.  Copy credentials so other references do
299 	 * not see our changes.
300 	 */
301 	pc->pc_ucred = crcopy(pc->pc_ucred);
302 	pc->pc_ucred->cr_uid = euid;
303 	p->p_flag |= P_SUGID;
304 	return (0);
305 }
306 
307 /* ARGSUSED */
308 setgid(p, uap, retval)
309 	struct proc *p;
310 	struct setgid_args /* {
311 		syscallarg(gid_t) gid;
312 	} */ *uap;
313 	register_t *retval;
314 {
315 	register struct pcred *pc = p->p_cred;
316 	register gid_t gid;
317 	int error;
318 
319 #ifdef COMPAT_09				/* XXX */
320 	gid = (u_short)SCARG(uap, gid);
321 #else
322 	gid = SCARG(uap, gid);
323 #endif
324 	if (gid != pc->p_rgid && (error = suser(pc->pc_ucred, &p->p_acflag)))
325 		return (error);
326 	pc->pc_ucred = crcopy(pc->pc_ucred);
327 	pc->pc_ucred->cr_groups[0] = gid;
328 	pc->p_rgid = gid;
329 	pc->p_svgid = gid;		/* ??? */
330 	p->p_flag |= P_SUGID;
331 	return (0);
332 }
333 
334 /* ARGSUSED */
335 setegid(p, uap, retval)
336 	struct proc *p;
337 	struct setegid_args /* {
338 		syscallarg(gid_t) egid;
339 	} */ *uap;
340 	register_t *retval;
341 {
342 	register struct pcred *pc = p->p_cred;
343 	register gid_t egid;
344 	int error;
345 
346 #ifdef COMPAT_09				/* XXX */
347 	egid = (u_short)SCARG(uap, egid);
348 #else
349 	egid = SCARG(uap, egid);
350 #endif
351 	if (egid != pc->p_rgid && egid != pc->p_svgid &&
352 	    (error = suser(pc->pc_ucred, &p->p_acflag)))
353 		return (error);
354 	pc->pc_ucred = crcopy(pc->pc_ucred);
355 	pc->pc_ucred->cr_groups[0] = egid;
356 	p->p_flag |= P_SUGID;
357 	return (0);
358 }
359 
360 /* ARGSUSED */
361 setgroups(p, uap, retval)
362 	struct proc *p;
363 	struct setgroups_args /* {
364 		syscallarg(u_int) gidsetsize;
365 		syscallarg(gid_t *) gidset;
366 	} */ *uap;
367 	register_t *retval;
368 {
369 	register struct pcred *pc = p->p_cred;
370 	register u_int ngrp;
371 	int error;
372 
373 	if (error = suser(pc->pc_ucred, &p->p_acflag))
374 		return (error);
375 	ngrp = SCARG(uap, gidsetsize);
376 	if (ngrp < 1 || ngrp > NGROUPS)
377 		return (EINVAL);
378 	pc->pc_ucred = crcopy(pc->pc_ucred);
379 	if (error = copyin((caddr_t)SCARG(uap, gidset),
380 	    (caddr_t)pc->pc_ucred->cr_groups, ngrp * sizeof(gid_t)))
381 		return (error);
382 	pc->pc_ucred->cr_ngroups = ngrp;
383 	p->p_flag |= P_SUGID;
384 	return (0);
385 }
386 
387 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) || defined(COMPAT_LINUX)
388 /* ARGSUSED */
389 compat_43_setreuid(p, uap, retval)
390 	register struct proc *p;
391 	struct compat_43_setreuid_args /* {
392 		syscallarg(int) ruid;
393 		syscallarg(int) euid;
394 	} */ *uap;
395 	register_t *retval;
396 {
397 	struct seteuid_args seuidargs;
398 	struct setuid_args suidargs;
399 
400 	/*
401 	 * There are five cases, and we attempt to emulate them in
402 	 * the following fashion:
403 	 * -1, -1: return 0. This is correct emulation.
404 	 * -1,  N: call seteuid(N). This is correct emulation.
405 	 *  N, -1: if we called setuid(N), our euid would be changed
406 	 *         to N as well. the theory is that we don't want to
407 	 * 	   revoke root access yet, so we call seteuid(N)
408 	 * 	   instead. This is incorrect emulation, but often
409 	 *	   suffices enough for binary compatibility.
410 	 *  N,  N: call setuid(N). This is correct emulation.
411 	 *  N,  M: call setuid(N). This is close to correct emulation.
412 	 */
413 	if (SCARG(uap, ruid) == (uid_t)-1) {
414 		if (SCARG(uap, euid) == (uid_t)-1)
415 			return (0);				/* -1, -1 */
416 		SCARG(&seuidargs, euid) = SCARG(uap, euid);	/* -1,  N */
417 		return (seteuid(p, &seuidargs, retval));
418 	}
419 	if (SCARG(uap, euid) == (uid_t)-1) {
420 		SCARG(&seuidargs, euid) = SCARG(uap, ruid);	/* N, -1 */
421 		return (seteuid(p, &seuidargs, retval));
422 	}
423 	SCARG(&suidargs, uid) = SCARG(uap, ruid);	/* N, N and N, M */
424 	return (setuid(p, &suidargs, retval));
425 }
426 
427 /* ARGSUSED */
428 compat_43_setregid(p, uap, retval)
429 	register struct proc *p;
430 	struct compat_43_setregid_args /* {
431 		syscallarg(int) rgid;
432 		syscallarg(int) egid;
433 	} */ *uap;
434 	register_t *retval;
435 {
436 	struct setegid_args segidargs;
437 	struct setgid_args sgidargs;
438 
439 	/*
440 	 * There are five cases, described above in osetreuid()
441 	 */
442 	if (SCARG(uap, rgid) == (gid_t)-1) {
443 		if (SCARG(uap, egid) == (gid_t)-1)
444 			return (0);				/* -1, -1 */
445 		SCARG(&segidargs, egid) = SCARG(uap, egid);	/* -1,  N */
446 		return (setegid(p, &segidargs, retval));
447 	}
448 	if (SCARG(uap, egid) == (gid_t)-1) {
449 		SCARG(&segidargs, egid) = SCARG(uap, rgid);	/* N, -1 */
450 		return (setegid(p, &segidargs, retval));
451 	}
452 	SCARG(&sgidargs, gid) = SCARG(uap, rgid);	/* N, N and N, M */
453 	return (setgid(p, &sgidargs, retval));
454 }
455 #endif /* COMPAT_43 || COMPAT_SUNOS || COMPAT_LINUX */
456 
457 /*
458  * Check if gid is a member of the group set.
459  */
460 groupmember(gid, cred)
461 	gid_t gid;
462 	register struct ucred *cred;
463 {
464 	register gid_t *gp;
465 	gid_t *egp;
466 
467 	egp = &(cred->cr_groups[cred->cr_ngroups]);
468 	for (gp = cred->cr_groups; gp < egp; gp++)
469 		if (*gp == gid)
470 			return (1);
471 	return (0);
472 }
473 
474 /*
475  * Test whether the specified credentials imply "super-user"
476  * privilege; if so, and we have accounting info, set the flag
477  * indicating use of super-powers.
478  * Returns 0 or error.
479  */
480 suser(cred, acflag)
481 	struct ucred *cred;
482 	u_short *acflag;
483 {
484 	if (cred->cr_uid == 0) {
485 		if (acflag)
486 			*acflag |= ASU;
487 		return (0);
488 	}
489 	return (EPERM);
490 }
491 
492 /*
493  * Allocate a zeroed cred structure.
494  */
495 struct ucred *
496 crget()
497 {
498 	register struct ucred *cr;
499 
500 	MALLOC(cr, struct ucred *, sizeof(*cr), M_CRED, M_WAITOK);
501 	bzero((caddr_t)cr, sizeof(*cr));
502 	cr->cr_ref = 1;
503 	return (cr);
504 }
505 
506 /*
507  * Free a cred structure.
508  * Throws away space when ref count gets to 0.
509  */
510 void
511 crfree(cr)
512 	struct ucred *cr;
513 {
514 	int s;
515 
516 	s = splimp();				/* ??? */
517 	if (--cr->cr_ref == 0)
518 		FREE((caddr_t)cr, M_CRED);
519 	(void) splx(s);
520 }
521 
522 /*
523  * Copy cred structure to a new one and free the old one.
524  */
525 struct ucred *
526 crcopy(cr)
527 	struct ucred *cr;
528 {
529 	struct ucred *newcr;
530 
531 	if (cr->cr_ref == 1)
532 		return (cr);
533 	newcr = crget();
534 	*newcr = *cr;
535 	crfree(cr);
536 	newcr->cr_ref = 1;
537 	return (newcr);
538 }
539 
540 /*
541  * Dup cred struct to a new held one.
542  */
543 struct ucred *
544 crdup(cr)
545 	struct ucred *cr;
546 {
547 	struct ucred *newcr;
548 
549 	newcr = crget();
550 	*newcr = *cr;
551 	newcr->cr_ref = 1;
552 	return (newcr);
553 }
554 
555 /*
556  * Get login name, if available.
557  */
558 /* ARGSUSED */
559 getlogin(p, uap, retval)
560 	struct proc *p;
561 	struct getlogin_args /* {
562 		syscallarg(char *) namebuf;
563 		syscallarg(u_int) namelen;
564 	} */ *uap;
565 	register_t *retval;
566 {
567 
568 	if (SCARG(uap, namelen) > sizeof (p->p_pgrp->pg_session->s_login))
569 		SCARG(uap, namelen) = sizeof (p->p_pgrp->pg_session->s_login);
570 	return (copyout((caddr_t) p->p_pgrp->pg_session->s_login,
571 	    (caddr_t) SCARG(uap, namebuf), SCARG(uap, namelen)));
572 }
573 
574 /*
575  * Set login name.
576  */
577 /* ARGSUSED */
578 setlogin(p, uap, retval)
579 	struct proc *p;
580 	struct setlogin_args /* {
581 		syscallarg(char *) namebuf;
582 	} */ *uap;
583 	register_t *retval;
584 {
585 	int error;
586 
587 	if (error = suser(p->p_ucred, &p->p_acflag))
588 		return (error);
589 	error = copyinstr((caddr_t) SCARG(uap, namebuf),
590 	    (caddr_t) p->p_pgrp->pg_session->s_login,
591 	    sizeof (p->p_pgrp->pg_session->s_login) - 1, (size_t *)0);
592 	if (error == ENAMETOOLONG)
593 		error = EINVAL;
594 	return (error);
595 }
596