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