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