1 /* $NetBSD: kern_prot.c,v 1.69 2002/08/25 21:30:40 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 <sys/cdefs.h> 48 __KERNEL_RCSID(0, "$NetBSD: kern_prot.c,v 1.69 2002/08/25 21:30:40 thorpej 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 u_int ngrp; 253 int error; 254 255 if (SCARG(uap, gidsetsize) == 0) { 256 *retval = pc->pc_ucred->cr_ngroups; 257 return (0); 258 } else if (SCARG(uap, gidsetsize) < 0) 259 return (EINVAL); 260 ngrp = SCARG(uap, gidsetsize); 261 if (ngrp < pc->pc_ucred->cr_ngroups) 262 return (EINVAL); 263 ngrp = pc->pc_ucred->cr_ngroups; 264 error = copyout((caddr_t)pc->pc_ucred->cr_groups, 265 (caddr_t)SCARG(uap, gidset), ngrp * sizeof(gid_t)); 266 if (error) 267 return (error); 268 *retval = ngrp; 269 return (0); 270 } 271 272 /* ARGSUSED */ 273 int 274 sys_setsid(p, v, retval) 275 struct proc *p; 276 void *v; 277 register_t *retval; 278 { 279 280 if (p->p_pgid == p->p_pid || pgfind(p->p_pid)) { 281 return (EPERM); 282 } else { 283 (void)enterpgrp(p, p->p_pid, 1); 284 *retval = p->p_pid; 285 return (0); 286 } 287 } 288 289 /* 290 * set process group (setpgid/old setpgrp) 291 * 292 * caller does setpgid(targpid, targpgid) 293 * 294 * pgid must be in valid range (EINVAL) 295 * pid must be caller or child of caller (ESRCH) 296 * if a child 297 * pid must be in same session (EPERM) 298 * pid can't have done an exec (EACCES) 299 * if pgid != pid 300 * there must exist some pid in same session having pgid (EPERM) 301 * pid must not be session leader (EPERM) 302 */ 303 /* ARGSUSED */ 304 int 305 sys_setpgid(curp, v, retval) 306 struct proc *curp; 307 void *v; 308 register_t *retval; 309 { 310 struct sys_setpgid_args /* { 311 syscallarg(int) pid; 312 syscallarg(int) pgid; 313 } */ *uap = v; 314 struct proc *targp; /* target process */ 315 struct pgrp *pgrp; /* target pgrp */ 316 317 if (SCARG(uap, pgid) < 0) 318 return (EINVAL); 319 320 if (SCARG(uap, pid) != 0 && SCARG(uap, pid) != curp->p_pid) { 321 if ((targp = pfind(SCARG(uap, pid))) == 0 322 || !inferior(targp, curp)) 323 return (ESRCH); 324 if (targp->p_session != curp->p_session) 325 return (EPERM); 326 if (targp->p_flag & P_EXEC) 327 return (EACCES); 328 } else 329 targp = curp; 330 if (SESS_LEADER(targp)) 331 return (EPERM); 332 if (SCARG(uap, pgid) == 0) 333 SCARG(uap, pgid) = targp->p_pid; 334 else if (SCARG(uap, pgid) != targp->p_pid) 335 if ((pgrp = pgfind(SCARG(uap, pgid))) == 0 || 336 pgrp->pg_session != curp->p_session) 337 return (EPERM); 338 return (enterpgrp(targp, SCARG(uap, pgid), 0)); 339 } 340 341 /* ARGSUSED */ 342 int 343 sys_setuid(p, v, retval) 344 struct proc *p; 345 void *v; 346 register_t *retval; 347 { 348 struct sys_setuid_args /* { 349 syscallarg(uid_t) uid; 350 } */ *uap = v; 351 struct pcred *pc = p->p_cred; 352 uid_t uid; 353 int error; 354 355 uid = SCARG(uap, uid); 356 if (uid != pc->p_ruid && 357 (error = suser(pc->pc_ucred, &p->p_acflag))) 358 return (error); 359 /* 360 * Check if we are all set, and this is a no-op. 361 */ 362 if (pc->p_ruid == uid && pc->p_svuid == uid && 363 pc->pc_ucred->cr_uid == uid) 364 return (0); 365 /* 366 * Everything's okay, do it. 367 * Transfer proc count to new user. 368 * Copy credentials so other references do not see our changes. 369 */ 370 (void)chgproccnt(pc->p_ruid, -1); 371 (void)chgproccnt(uid, 1); 372 pc->pc_ucred = crcopy(pc->pc_ucred); 373 pc->pc_ucred->cr_uid = uid; 374 pc->p_ruid = uid; 375 pc->p_svuid = uid; 376 p_sugid(p); 377 return (0); 378 } 379 380 /* ARGSUSED */ 381 int 382 sys_seteuid(p, v, retval) 383 struct proc *p; 384 void *v; 385 register_t *retval; 386 { 387 struct sys_seteuid_args /* { 388 syscallarg(uid_t) euid; 389 } */ *uap = v; 390 struct pcred *pc = p->p_cred; 391 uid_t euid; 392 int error; 393 394 euid = SCARG(uap, euid); 395 if (euid != pc->p_ruid && euid != pc->p_svuid && 396 (error = suser(pc->pc_ucred, &p->p_acflag))) 397 return (error); 398 /* 399 * Check if we are all set, and this is a no-op. 400 */ 401 if (pc->pc_ucred->cr_uid == euid) 402 return (0); 403 404 /* 405 * Everything's okay, do it. Copy credentials so other references do 406 * not see our changes. 407 */ 408 pc->pc_ucred = crcopy(pc->pc_ucred); 409 pc->pc_ucred->cr_uid = euid; 410 p_sugid(p); 411 return (0); 412 } 413 414 int 415 sys_setreuid(p, v, retval) 416 struct proc *p; 417 void *v; 418 register_t *retval; 419 { 420 struct sys_setreuid_args /* { 421 syscallarg(uid_t) ruid; 422 syscallarg(uid_t) euid; 423 } */ *uap = v; 424 struct pcred *pc = p->p_cred; 425 uid_t ruid, euid; 426 int error, changed = 0; 427 428 ruid = SCARG(uap, ruid); 429 euid = SCARG(uap, euid); 430 431 if (ruid != (uid_t)-1 && 432 ruid != pc->p_ruid && ruid != pc->pc_ucred->cr_uid && 433 (error = suser(pc->pc_ucred, &p->p_acflag))) 434 return (error); 435 436 if (euid != (uid_t)-1 && 437 euid != pc->p_ruid && euid != pc->pc_ucred->cr_uid && 438 euid != pc->p_svuid && 439 (error = suser(pc->pc_ucred, &p->p_acflag))) 440 return (error); 441 442 if (euid != (uid_t)-1 && euid != pc->pc_ucred->cr_uid) { 443 pc->pc_ucred = crcopy(pc->pc_ucred); 444 pc->pc_ucred->cr_uid = euid; 445 changed++; 446 } 447 448 if (ruid != (uid_t)-1 && 449 (pc->p_ruid != ruid || pc->p_svuid != pc->pc_ucred->cr_uid)) { 450 (void)chgproccnt(pc->p_ruid, -1); 451 (void)chgproccnt(ruid, 1); 452 pc->p_ruid = ruid; 453 pc->p_svuid = pc->pc_ucred->cr_uid; 454 changed++; 455 } 456 457 if (changed) 458 p_sugid(p); 459 return (0); 460 } 461 462 /* ARGSUSED */ 463 int 464 sys_setgid(p, v, retval) 465 struct proc *p; 466 void *v; 467 register_t *retval; 468 { 469 struct sys_setgid_args /* { 470 syscallarg(gid_t) gid; 471 } */ *uap = v; 472 struct pcred *pc = p->p_cred; 473 gid_t gid; 474 int error; 475 476 gid = SCARG(uap, gid); 477 if (gid != pc->p_rgid && 478 (error = suser(pc->pc_ucred, &p->p_acflag))) 479 return (error); 480 /* 481 * Check if we are all set, and this is a no-op. 482 */ 483 if (pc->pc_ucred->cr_gid == gid && pc->p_rgid == gid && 484 pc->p_svgid == gid) 485 return (0); 486 487 pc->pc_ucred = crcopy(pc->pc_ucred); 488 pc->pc_ucred->cr_gid = gid; 489 pc->p_rgid = gid; 490 pc->p_svgid = gid; 491 p_sugid(p); 492 return (0); 493 } 494 495 /* ARGSUSED */ 496 int 497 sys_setegid(p, v, retval) 498 struct proc *p; 499 void *v; 500 register_t *retval; 501 { 502 struct sys_setegid_args /* { 503 syscallarg(gid_t) egid; 504 } */ *uap = v; 505 struct pcred *pc = p->p_cred; 506 gid_t egid; 507 int error; 508 509 egid = SCARG(uap, egid); 510 if (egid != pc->p_rgid && egid != pc->p_svgid && 511 (error = suser(pc->pc_ucred, &p->p_acflag))) 512 return (error); 513 /* 514 * Check if we are all set, and this is a no-op. 515 */ 516 if (pc->pc_ucred->cr_gid == egid) 517 return (0); 518 519 pc->pc_ucred = crcopy(pc->pc_ucred); 520 pc->pc_ucred->cr_gid = egid; 521 p_sugid(p); 522 return (0); 523 } 524 525 int 526 sys_setregid(p, v, retval) 527 struct proc *p; 528 void *v; 529 register_t *retval; 530 { 531 struct sys_setregid_args /* { 532 syscallarg(gid_t) rgid; 533 syscallarg(gid_t) egid; 534 } */ *uap = v; 535 struct pcred *pc = p->p_cred; 536 gid_t rgid, egid; 537 int error, changed = 0; 538 539 rgid = SCARG(uap, rgid); 540 egid = SCARG(uap, egid); 541 542 if (rgid != (gid_t)-1 && 543 rgid != pc->p_rgid && rgid != pc->pc_ucred->cr_gid && 544 (error = suser(pc->pc_ucred, &p->p_acflag))) 545 return (error); 546 547 if (egid != (gid_t)-1 && 548 egid != pc->p_rgid && egid != pc->pc_ucred->cr_gid && 549 egid != pc->p_svgid && 550 (error = suser(pc->pc_ucred, &p->p_acflag))) 551 return (error); 552 553 if (egid != (gid_t)-1 && pc->pc_ucred->cr_gid != egid) { 554 pc->pc_ucred = crcopy(pc->pc_ucred); 555 pc->pc_ucred->cr_gid = egid; 556 changed++; 557 } 558 559 if (rgid != (gid_t)-1 && 560 (pc->p_rgid != rgid || pc->p_svgid != pc->pc_ucred->cr_gid)) { 561 pc->p_rgid = rgid; 562 pc->p_svgid = pc->pc_ucred->cr_gid; 563 changed++; 564 } 565 566 if (changed) 567 p_sugid(p); 568 return (0); 569 } 570 571 int 572 sys_issetugid(p, v, retval) 573 struct proc *p; 574 void *v; 575 register_t *retval; 576 { 577 /* 578 * Note: OpenBSD sets a P_SUGIDEXEC flag set at execve() time, 579 * we use P_SUGID because we consider changing the owners as 580 * "tainting" as well. 581 * This is significant for procs that start as root and "become" 582 * a user without an exec - programs cannot know *everything* 583 * that libc *might* have put in their data segment. 584 */ 585 *retval = (p->p_flag & P_SUGID) != 0; 586 return (0); 587 } 588 589 /* ARGSUSED */ 590 int 591 sys_setgroups(p, v, retval) 592 struct proc *p; 593 void *v; 594 register_t *retval; 595 { 596 struct sys_setgroups_args /* { 597 syscallarg(int) gidsetsize; 598 syscallarg(const gid_t *) gidset; 599 } */ *uap = v; 600 struct pcred *pc = p->p_cred; 601 int ngrp; 602 int error; 603 gid_t grp[NGROUPS]; 604 size_t grsize; 605 606 if ((error = suser(pc->pc_ucred, &p->p_acflag)) != 0) 607 return (error); 608 609 ngrp = SCARG(uap, gidsetsize); 610 if ((u_int)ngrp > NGROUPS) 611 return (EINVAL); 612 613 grsize = ngrp * sizeof(gid_t); 614 error = copyin(SCARG(uap, gidset), grp, grsize); 615 if (error) 616 return (error); 617 /* 618 * Check if this is a no-op. 619 */ 620 if (pc->pc_ucred->cr_ngroups == (u_int) ngrp && 621 memcmp(grp, pc->pc_ucred->cr_groups, grsize) == 0) 622 return (0); 623 624 pc->pc_ucred = crcopy(pc->pc_ucred); 625 (void)memcpy(pc->pc_ucred->cr_groups, grp, grsize); 626 pc->pc_ucred->cr_ngroups = ngrp; 627 p_sugid(p); 628 return (0); 629 } 630 631 /* 632 * Check if gid is a member of the group set. 633 */ 634 int 635 groupmember(gid, cred) 636 gid_t gid; 637 struct ucred *cred; 638 { 639 gid_t *gp; 640 gid_t *egp; 641 642 egp = &(cred->cr_groups[cred->cr_ngroups]); 643 for (gp = cred->cr_groups; gp < egp; gp++) 644 if (*gp == gid) 645 return (1); 646 return (0); 647 } 648 649 /* 650 * Test whether the specified credentials imply "super-user" 651 * privilege; if so, and we have accounting info, set the flag 652 * indicating use of super-powers. 653 * Returns 0 or error. 654 */ 655 int 656 suser(cred, acflag) 657 struct ucred *cred; 658 u_short *acflag; 659 { 660 if (cred->cr_uid == 0) { 661 if (acflag) 662 *acflag |= ASU; 663 return (0); 664 } 665 return (EPERM); 666 } 667 668 /* 669 * Allocate a zeroed cred structure. 670 */ 671 struct ucred * 672 crget() 673 { 674 struct ucred *cr; 675 676 MALLOC(cr, struct ucred *, sizeof(*cr), M_CRED, M_WAITOK); 677 memset((caddr_t)cr, 0, sizeof(*cr)); 678 cr->cr_ref = 1; 679 return (cr); 680 } 681 682 /* 683 * Free a cred structure. 684 * Throws away space when ref count gets to 0. 685 */ 686 void 687 crfree(cr) 688 struct ucred *cr; 689 { 690 691 if (--cr->cr_ref == 0) 692 FREE((caddr_t)cr, M_CRED); 693 } 694 695 /* 696 * Copy cred structure to a new one and free the old one. 697 */ 698 struct ucred * 699 crcopy(cr) 700 struct ucred *cr; 701 { 702 struct ucred *newcr; 703 704 if (cr->cr_ref == 1) 705 return (cr); 706 newcr = crget(); 707 *newcr = *cr; 708 crfree(cr); 709 newcr->cr_ref = 1; 710 return (newcr); 711 } 712 713 /* 714 * Dup cred struct to a new held one. 715 */ 716 struct ucred * 717 crdup(cr) 718 struct ucred *cr; 719 { 720 struct ucred *newcr; 721 722 newcr = crget(); 723 *newcr = *cr; 724 newcr->cr_ref = 1; 725 return (newcr); 726 } 727 728 /* 729 * convert from userland credentials to kernel one 730 */ 731 void 732 crcvt(uc, uuc) 733 struct ucred *uc; 734 const struct uucred *uuc; 735 { 736 uc->cr_ref = 0; 737 uc->cr_uid = uuc->cr_uid; 738 uc->cr_gid = uuc->cr_gid; 739 uc->cr_ngroups = uuc->cr_ngroups; 740 (void)memcpy(uc->cr_groups, uuc->cr_groups, sizeof(uuc->cr_groups)); 741 } 742 743 /* 744 * Get login name, if available. 745 */ 746 /* ARGSUSED */ 747 int 748 sys___getlogin(p, v, retval) 749 struct proc *p; 750 void *v; 751 register_t *retval; 752 { 753 struct sys___getlogin_args /* { 754 syscallarg(char *) namebuf; 755 syscallarg(size_t) namelen; 756 } */ *uap = v; 757 758 if (SCARG(uap, namelen) > sizeof(p->p_pgrp->pg_session->s_login)) 759 SCARG(uap, namelen) = sizeof(p->p_pgrp->pg_session->s_login); 760 return (copyout((caddr_t) p->p_pgrp->pg_session->s_login, 761 (caddr_t) SCARG(uap, namebuf), SCARG(uap, namelen))); 762 } 763 764 /* 765 * Set login name. 766 */ 767 /* ARGSUSED */ 768 int 769 sys_setlogin(p, v, retval) 770 struct proc *p; 771 void *v; 772 register_t *retval; 773 { 774 struct sys_setlogin_args /* { 775 syscallarg(const char *) namebuf; 776 } */ *uap = v; 777 int error; 778 779 if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) 780 return (error); 781 error = copyinstr(SCARG(uap, namebuf), p->p_pgrp->pg_session->s_login, 782 sizeof(p->p_pgrp->pg_session->s_login) - 1, (size_t *)0); 783 if (error == ENAMETOOLONG) 784 error = EINVAL; 785 return (error); 786 } 787