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