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