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