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