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