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