1 /* $NetBSD: kern_prot.c,v 1.17 1994/08/25 07:13:55 deraadt 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 /* ARGSUSED */ 57 getpid(p, uap, retval) 58 struct proc *p; 59 void *uap; 60 int *retval; 61 { 62 63 *retval = p->p_pid; 64 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) 65 retval[1] = p->p_pptr->p_pid; 66 #endif 67 return (0); 68 } 69 70 /* ARGSUSED */ 71 getppid(p, uap, retval) 72 struct proc *p; 73 void *uap; 74 int *retval; 75 { 76 77 *retval = p->p_pptr->p_pid; 78 return (0); 79 } 80 81 /* Get process group ID; note that POSIX getpgrp takes no parameter */ 82 getpgrp(p, uap, retval) 83 struct proc *p; 84 void *uap; 85 int *retval; 86 { 87 88 *retval = p->p_pgrp->pg_id; 89 return (0); 90 } 91 92 /* ARGSUSED */ 93 getuid(p, uap, retval) 94 struct proc *p; 95 void *uap; 96 int *retval; 97 { 98 99 *retval = p->p_cred->p_ruid; 100 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) 101 retval[1] = p->p_ucred->cr_uid; 102 #endif 103 return (0); 104 } 105 106 /* ARGSUSED */ 107 geteuid(p, uap, retval) 108 struct proc *p; 109 void *uap; 110 int *retval; 111 { 112 113 *retval = p->p_ucred->cr_uid; 114 return (0); 115 } 116 117 /* ARGSUSED */ 118 getgid(p, uap, retval) 119 struct proc *p; 120 void *uap; 121 int *retval; 122 { 123 124 *retval = p->p_cred->p_rgid; 125 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) 126 retval[1] = p->p_ucred->cr_groups[0]; 127 #endif 128 return (0); 129 } 130 131 /* 132 * Get effective group ID. The "egid" is groups[0], and could be obtained 133 * via getgroups. This syscall exists because it is somewhat painful to do 134 * correctly in a library function. 135 */ 136 /* ARGSUSED */ 137 getegid(p, uap, retval) 138 struct proc *p; 139 void *uap; 140 int *retval; 141 { 142 143 *retval = p->p_ucred->cr_groups[0]; 144 return (0); 145 } 146 147 struct getgroups_args { 148 u_int gidsetsize; 149 gid_t *gidset; 150 }; 151 getgroups(p, uap, retval) 152 struct proc *p; 153 register struct getgroups_args *uap; 154 int *retval; 155 { 156 register struct pcred *pc = p->p_cred; 157 register u_int ngrp; 158 int error; 159 160 if ((ngrp = uap->gidsetsize) == 0) { 161 *retval = pc->pc_ucred->cr_ngroups; 162 return (0); 163 } 164 if (ngrp < pc->pc_ucred->cr_ngroups) 165 return (EINVAL); 166 ngrp = pc->pc_ucred->cr_ngroups; 167 if (error = copyout((caddr_t)pc->pc_ucred->cr_groups, 168 (caddr_t)uap->gidset, ngrp * sizeof(gid_t))) 169 return (error); 170 *retval = ngrp; 171 return (0); 172 } 173 174 /* ARGSUSED */ 175 setsid(p, uap, retval) 176 register struct proc *p; 177 void *uap; 178 int *retval; 179 { 180 181 if (p->p_pgid == p->p_pid || pgfind(p->p_pid)) { 182 return (EPERM); 183 } else { 184 (void)enterpgrp(p, p->p_pid, 1); 185 *retval = p->p_pid; 186 return (0); 187 } 188 } 189 190 /* 191 * set process group (setpgid/old setpgrp) 192 * 193 * caller does setpgid(targpid, targpgid) 194 * 195 * pid must be caller or child of caller (ESRCH) 196 * if a child 197 * pid must be in same session (EPERM) 198 * pid can't have done an exec (EACCES) 199 * if pgid != pid 200 * there must exist some pid in same session having pgid (EPERM) 201 * pid must not be session leader (EPERM) 202 */ 203 struct setpgid_args { 204 int pid; /* target process id */ 205 int pgid; /* target pgrp id */ 206 }; 207 /* ARGSUSED */ 208 setpgid(curp, uap, retval) 209 struct proc *curp; 210 register struct setpgid_args *uap; 211 int *retval; 212 { 213 register struct proc *targp; /* target process */ 214 register struct pgrp *pgrp; /* target pgrp */ 215 216 #ifdef COMPAT_09 217 uap->pid = (short) uap->pid; /* XXX */ 218 uap->pgid = (short) uap->pgid; /* XXX */ 219 #endif 220 221 if (uap->pid != 0 && uap->pid != curp->p_pid) { 222 if ((targp = pfind(uap->pid)) == 0 || !inferior(targp)) 223 return (ESRCH); 224 if (targp->p_session != curp->p_session) 225 return (EPERM); 226 if (targp->p_flag & P_EXEC) 227 return (EACCES); 228 } else 229 targp = curp; 230 if (SESS_LEADER(targp)) 231 return (EPERM); 232 if (uap->pgid == 0) 233 uap->pgid = targp->p_pid; 234 else if (uap->pgid != targp->p_pid) 235 if ((pgrp = pgfind(uap->pgid)) == 0 || 236 pgrp->pg_session != curp->p_session) 237 return (EPERM); 238 return (enterpgrp(targp, uap->pgid, 0)); 239 } 240 241 struct setuid_args { 242 uid_t uid; 243 }; 244 /* ARGSUSED */ 245 setuid(p, uap, retval) 246 struct proc *p; 247 struct setuid_args *uap; 248 int *retval; 249 { 250 register struct pcred *pc = p->p_cred; 251 register uid_t uid; 252 int error; 253 254 #ifdef COMPAT_09 /* XXX */ 255 uid = (u_short)uap->uid; 256 #else 257 uid = uap->uid; 258 #endif 259 if (uid != pc->p_ruid && 260 (error = suser(pc->pc_ucred, &p->p_acflag))) 261 return (error); 262 /* 263 * Everything's okay, do it. 264 * Transfer proc count to new user. 265 * Copy credentials so other references do not see our changes. 266 */ 267 (void)chgproccnt(pc->p_ruid, -1); 268 (void)chgproccnt(uid, 1); 269 pc->pc_ucred = crcopy(pc->pc_ucred); 270 pc->pc_ucred->cr_uid = uid; 271 pc->p_ruid = uid; 272 pc->p_svuid = uid; 273 p->p_flag |= P_SUGID; 274 return (0); 275 } 276 277 struct seteuid_args { 278 uid_t euid; 279 }; 280 /* ARGSUSED */ 281 seteuid(p, uap, retval) 282 struct proc *p; 283 struct seteuid_args *uap; 284 int *retval; 285 { 286 register struct pcred *pc = p->p_cred; 287 register uid_t euid; 288 int error; 289 290 #ifdef COMPAT_09 /* XXX */ 291 euid = (u_short)uap->euid; 292 #else 293 euid = uap->euid; 294 #endif 295 if (euid != pc->p_ruid && euid != pc->p_svuid && 296 (error = suser(pc->pc_ucred, &p->p_acflag))) 297 return (error); 298 /* 299 * Everything's okay, do it. Copy credentials so other references do 300 * not see our changes. 301 */ 302 pc->pc_ucred = crcopy(pc->pc_ucred); 303 pc->pc_ucred->cr_uid = euid; 304 p->p_flag |= P_SUGID; 305 return (0); 306 } 307 308 struct setgid_args { 309 gid_t gid; 310 }; 311 /* ARGSUSED */ 312 setgid(p, uap, retval) 313 struct proc *p; 314 struct setgid_args *uap; 315 int *retval; 316 { 317 register struct pcred *pc = p->p_cred; 318 register gid_t gid; 319 int error; 320 321 #ifdef COMPAT_09 /* XXX */ 322 gid = (u_short)uap->gid; 323 #else 324 gid = uap->gid; 325 #endif 326 if (gid != pc->p_rgid && (error = suser(pc->pc_ucred, &p->p_acflag))) 327 return (error); 328 pc->pc_ucred = crcopy(pc->pc_ucred); 329 pc->pc_ucred->cr_groups[0] = gid; 330 pc->p_rgid = gid; 331 pc->p_svgid = gid; /* ??? */ 332 p->p_flag |= P_SUGID; 333 return (0); 334 } 335 336 struct setegid_args { 337 gid_t egid; 338 }; 339 /* ARGSUSED */ 340 setegid(p, uap, retval) 341 struct proc *p; 342 struct setegid_args *uap; 343 int *retval; 344 { 345 register struct pcred *pc = p->p_cred; 346 register gid_t egid; 347 int error; 348 349 #ifdef COMPAT_09 /* XXX */ 350 egid = (u_short)uap->egid; 351 #else 352 egid = uap->egid; 353 #endif 354 if (egid != pc->p_rgid && egid != pc->p_svgid && 355 (error = suser(pc->pc_ucred, &p->p_acflag))) 356 return (error); 357 pc->pc_ucred = crcopy(pc->pc_ucred); 358 pc->pc_ucred->cr_groups[0] = egid; 359 p->p_flag |= P_SUGID; 360 return (0); 361 } 362 363 struct setgroups_args { 364 u_int gidsetsize; 365 gid_t *gidset; 366 }; 367 /* ARGSUSED */ 368 setgroups(p, uap, retval) 369 struct proc *p; 370 struct setgroups_args *uap; 371 int *retval; 372 { 373 register struct pcred *pc = p->p_cred; 374 register u_int ngrp; 375 int error; 376 377 if (error = suser(pc->pc_ucred, &p->p_acflag)) 378 return (error); 379 if ((ngrp = uap->gidsetsize) > NGROUPS) 380 return (EINVAL); 381 pc->pc_ucred = crcopy(pc->pc_ucred); 382 if (error = copyin((caddr_t)uap->gidset, 383 (caddr_t)pc->pc_ucred->cr_groups, ngrp * sizeof(gid_t))) 384 return (error); 385 pc->pc_ucred->cr_ngroups = ngrp; 386 p->p_flag |= P_SUGID; 387 return (0); 388 } 389 390 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) 391 struct setreuid_args { 392 int ruid; 393 int euid; 394 }; 395 /* ARGSUSED */ 396 osetreuid(p, uap, retval) 397 register struct proc *p; 398 struct setreuid_args *uap; 399 int *retval; 400 { 401 struct seteuid_args seuidargs; 402 struct setuid_args suidargs; 403 404 /* 405 * There are five cases, and we attempt to emulate them in 406 * the following fashion: 407 * -1, -1: return 0. This is correct emulation. 408 * -1, N: call seteuid(N). This is correct emulation. 409 * N, -1: if we called setuid(N), our euid would be changed 410 * to N as well. the theory is that we don't want to 411 * revoke root access yet, so we call seteuid(N) 412 * instead. This is incorrect emulation, but often 413 * suffices enough for binary compatibility. 414 * N, N: call setuid(N). This is correct emulation. 415 * N, M: call setuid(N). This is close to correct emulation. 416 */ 417 if (uap->ruid == (uid_t)-1) { 418 if (uap->euid == (uid_t)-1) 419 return (0); /* -1, -1 */ 420 seuidargs.euid = uap->euid; /* -1, N */ 421 return (seteuid(p, &seuidargs, retval)); 422 } 423 if (uap->euid == (uid_t)-1) { 424 seuidargs.euid = uap->ruid; /* N, -1 */ 425 return (seteuid(p, &seuidargs, retval)); 426 } 427 suidargs.uid = uap->ruid; /* N, N and N, M */ 428 return (setuid(p, &suidargs, retval)); 429 } 430 431 struct setregid_args { 432 int rgid; 433 int egid; 434 }; 435 /* ARGSUSED */ 436 osetregid(p, uap, retval) 437 register struct proc *p; 438 struct setregid_args *uap; 439 int *retval; 440 { 441 struct setegid_args segidargs; 442 struct setgid_args sgidargs; 443 444 /* 445 * There are five cases, described above in osetreuid() 446 */ 447 if (uap->rgid == (gid_t)-1) { 448 if (uap->egid == (gid_t)-1) 449 return (0); /* -1, -1 */ 450 segidargs.egid = uap->egid; /* -1, N */ 451 return (setegid(p, &segidargs, retval)); 452 } 453 if (uap->egid == (gid_t)-1) { 454 segidargs.egid = uap->rgid; /* N, -1 */ 455 return (setegid(p, &segidargs, retval)); 456 } 457 sgidargs.gid = uap->rgid; /* N, N and N, M */ 458 return (setgid(p, &sgidargs, retval)); 459 } 460 #endif /* defined(COMPAT_43) || defined(COMPAT_SUNOS) */ 461 462 /* 463 * Check if gid is a member of the group set. 464 */ 465 groupmember(gid, cred) 466 gid_t gid; 467 register struct ucred *cred; 468 { 469 register gid_t *gp; 470 gid_t *egp; 471 472 egp = &(cred->cr_groups[cred->cr_ngroups]); 473 for (gp = cred->cr_groups; gp < egp; gp++) 474 if (*gp == gid) 475 return (1); 476 return (0); 477 } 478 479 /* 480 * Test whether the specified credentials imply "super-user" 481 * privilege; if so, and we have accounting info, set the flag 482 * indicating use of super-powers. 483 * Returns 0 or error. 484 */ 485 suser(cred, acflag) 486 struct ucred *cred; 487 u_short *acflag; 488 { 489 if (cred->cr_uid == 0) { 490 if (acflag) 491 *acflag |= ASU; 492 return (0); 493 } 494 return (EPERM); 495 } 496 497 /* 498 * Allocate a zeroed cred structure. 499 */ 500 struct ucred * 501 crget() 502 { 503 register struct ucred *cr; 504 505 MALLOC(cr, struct ucred *, sizeof(*cr), M_CRED, M_WAITOK); 506 bzero((caddr_t)cr, sizeof(*cr)); 507 cr->cr_ref = 1; 508 return (cr); 509 } 510 511 /* 512 * Free a cred structure. 513 * Throws away space when ref count gets to 0. 514 */ 515 crfree(cr) 516 struct ucred *cr; 517 { 518 int s; 519 520 s = splimp(); /* ??? */ 521 if (--cr->cr_ref == 0) 522 FREE((caddr_t)cr, M_CRED); 523 (void) splx(s); 524 } 525 526 /* 527 * Copy cred structure to a new one and free the old one. 528 */ 529 struct ucred * 530 crcopy(cr) 531 struct ucred *cr; 532 { 533 struct ucred *newcr; 534 535 if (cr->cr_ref == 1) 536 return (cr); 537 newcr = crget(); 538 *newcr = *cr; 539 crfree(cr); 540 newcr->cr_ref = 1; 541 return (newcr); 542 } 543 544 /* 545 * Dup cred struct to a new held one. 546 */ 547 struct ucred * 548 crdup(cr) 549 struct ucred *cr; 550 { 551 struct ucred *newcr; 552 553 newcr = crget(); 554 *newcr = *cr; 555 newcr->cr_ref = 1; 556 return (newcr); 557 } 558 559 /* 560 * Get login name, if available. 561 */ 562 struct getlogin_args { 563 char *namebuf; 564 u_int namelen; 565 }; 566 /* ARGSUSED */ 567 getlogin(p, uap, retval) 568 struct proc *p; 569 struct getlogin_args *uap; 570 int *retval; 571 { 572 573 if (uap->namelen > sizeof (p->p_pgrp->pg_session->s_login)) 574 uap->namelen = sizeof (p->p_pgrp->pg_session->s_login); 575 return (copyout((caddr_t) p->p_pgrp->pg_session->s_login, 576 (caddr_t) uap->namebuf, uap->namelen)); 577 } 578 579 /* 580 * Set login name. 581 */ 582 struct setlogin_args { 583 char *namebuf; 584 }; 585 /* ARGSUSED */ 586 setlogin(p, uap, retval) 587 struct proc *p; 588 struct setlogin_args *uap; 589 int *retval; 590 { 591 int error; 592 593 if (error = suser(p->p_ucred, &p->p_acflag)) 594 return (error); 595 error = copyinstr((caddr_t) uap->namebuf, 596 (caddr_t) p->p_pgrp->pg_session->s_login, 597 sizeof (p->p_pgrp->pg_session->s_login) - 1, (u_int *)0); 598 if (error == ENAMETOOLONG) 599 error = EINVAL; 600 return (error); 601 } 602