1 /* $OpenBSD: kern_prot.c,v 1.7 1996/10/27 04:51:37 tholo Exp $ */ 2 /* $NetBSD: kern_prot.c,v 1.33 1996/02/09 18:59:42 christos Exp $ */ 3 4 /* 5 * Copyright (c) 1982, 1986, 1989, 1990, 1991, 1993 6 * The Regents of the University of California. All rights reserved. 7 * (c) UNIX System Laboratories, Inc. 8 * All or some portions of this file are derived from material licensed 9 * to the University of California by American Telephone and Telegraph 10 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 11 * the permission of UNIX System Laboratories, Inc. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgement: 23 * This product includes software developed by the University of 24 * California, Berkeley and its contributors. 25 * 4. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 * 41 * @(#)kern_prot.c 8.6 (Berkeley) 1/21/94 42 */ 43 44 /* 45 * System calls related to processes and protection 46 */ 47 48 #include <sys/param.h> 49 #include <sys/acct.h> 50 #include <sys/systm.h> 51 #include <sys/ucred.h> 52 #include <sys/proc.h> 53 #include <sys/timeb.h> 54 #include <sys/times.h> 55 #include <sys/malloc.h> 56 57 #include <sys/mount.h> 58 #include <sys/syscallargs.h> 59 60 /* ARGSUSED */ 61 int 62 sys_getpid(p, v, retval) 63 struct proc *p; 64 void *v; 65 register_t *retval; 66 { 67 68 *retval = p->p_pid; 69 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) || defined(COMPAT_IBCS2) || \ 70 defined(COMPAT_FREEBSD) 71 retval[1] = p->p_pptr->p_pid; 72 #endif 73 return (0); 74 } 75 76 /* ARGSUSED */ 77 int 78 sys_getppid(p, v, retval) 79 struct proc *p; 80 void *v; 81 register_t *retval; 82 { 83 84 *retval = p->p_pptr->p_pid; 85 return (0); 86 } 87 88 /* Get process group ID; note that POSIX getpgrp takes no parameter */ 89 int 90 sys_getpgrp(p, v, retval) 91 struct proc *p; 92 void *v; 93 register_t *retval; 94 { 95 96 *retval = p->p_pgrp->pg_id; 97 return (0); 98 } 99 100 /* ARGSUSED */ 101 int 102 sys_getuid(p, v, retval) 103 struct proc *p; 104 void *v; 105 register_t *retval; 106 { 107 108 *retval = p->p_cred->p_ruid; 109 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) || defined(COMPAT_IBCS2) || \ 110 defined(COMPAT_FREEBSD) 111 retval[1] = p->p_ucred->cr_uid; 112 #endif 113 return (0); 114 } 115 116 /* ARGSUSED */ 117 int 118 sys_geteuid(p, v, retval) 119 struct proc *p; 120 void *v; 121 register_t *retval; 122 { 123 124 *retval = p->p_ucred->cr_uid; 125 return (0); 126 } 127 128 /* ARGSUSED */ 129 int 130 sys_issetugid(p, v, retval) 131 struct proc *p; 132 void *v; 133 register_t *retval; 134 { 135 if (p->p_flag & P_SUGIDEXEC) 136 return (1); 137 return (0); 138 } 139 140 /* ARGSUSED */ 141 int 142 sys_getgid(p, v, retval) 143 struct proc *p; 144 void *v; 145 register_t *retval; 146 { 147 148 *retval = p->p_cred->p_rgid; 149 #if defined(COMPAT_43) || defined(COMPAT_SUNOS) || defined(COMPAT_FREEBSD) 150 retval[1] = p->p_ucred->cr_gid; 151 #endif 152 return (0); 153 } 154 155 /* 156 * Get effective group ID. The "egid" is groups[0], and could be obtained 157 * via getgroups. This syscall exists because it is somewhat painful to do 158 * correctly in a library function. 159 */ 160 /* ARGSUSED */ 161 int 162 sys_getegid(p, v, retval) 163 struct proc *p; 164 void *v; 165 register_t *retval; 166 { 167 168 *retval = p->p_ucred->cr_gid; 169 return (0); 170 } 171 172 int 173 sys_getgroups(p, v, retval) 174 struct proc *p; 175 void *v; 176 register_t *retval; 177 { 178 register struct sys_getgroups_args /* { 179 syscallarg(u_int) gidsetsize; 180 syscallarg(gid_t *) gidset; 181 } */ *uap = v; 182 register struct pcred *pc = p->p_cred; 183 register u_int ngrp; 184 int error; 185 186 if ((ngrp = SCARG(uap, gidsetsize)) == 0) { 187 *retval = pc->pc_ucred->cr_ngroups; 188 return (0); 189 } 190 if (ngrp < pc->pc_ucred->cr_ngroups) 191 return (EINVAL); 192 ngrp = pc->pc_ucred->cr_ngroups; 193 error = copyout((caddr_t)pc->pc_ucred->cr_groups, 194 (caddr_t)SCARG(uap, gidset), ngrp * sizeof(gid_t)); 195 if (error) 196 return (error); 197 *retval = ngrp; 198 return (0); 199 } 200 201 /* ARGSUSED */ 202 int 203 sys_setsid(p, v, retval) 204 register struct proc *p; 205 void *v; 206 register_t *retval; 207 { 208 209 if (p->p_pgid == p->p_pid || pgfind(p->p_pid)) { 210 return (EPERM); 211 } else { 212 (void)enterpgrp(p, p->p_pid, 1); 213 *retval = p->p_pid; 214 return (0); 215 } 216 } 217 218 /* 219 * set process group (setpgid/old setpgrp) 220 * 221 * caller does setpgid(targpid, targpgid) 222 * 223 * pid must be caller or child of caller (ESRCH) 224 * if a child 225 * pid must be in same session (EPERM) 226 * pid can't have done an exec (EACCES) 227 * if pgid != pid 228 * there must exist some pid in same session having pgid (EPERM) 229 * pid must not be session leader (EPERM) 230 */ 231 /* ARGSUSED */ 232 int 233 sys_setpgid(curp, v, retval) 234 struct proc *curp; 235 void *v; 236 register_t *retval; 237 { 238 register struct sys_setpgid_args /* { 239 syscallarg(int) pid; 240 syscallarg(int) pgid; 241 } */ *uap = v; 242 register struct proc *targp; /* target process */ 243 register struct pgrp *pgrp; /* target pgrp */ 244 245 #ifdef COMPAT_09 246 SCARG(uap, pid) = (short) SCARG(uap, pid); /* XXX */ 247 SCARG(uap, pgid) = (short) SCARG(uap, pgid); /* XXX */ 248 #endif 249 250 if (SCARG(uap, pgid) < 0) 251 return (EINVAL); 252 253 if (SCARG(uap, pid) != 0 && SCARG(uap, pid) != curp->p_pid) { 254 if ((targp = pfind(SCARG(uap, pid))) == 0 || !inferior(targp)) 255 return (ESRCH); 256 if (targp->p_session != curp->p_session) 257 return (EPERM); 258 if (targp->p_flag & P_EXEC) 259 return (EACCES); 260 } else 261 targp = curp; 262 if (SESS_LEADER(targp)) 263 return (EPERM); 264 if (SCARG(uap, pgid) == 0) 265 SCARG(uap, pgid) = targp->p_pid; 266 else if (SCARG(uap, pgid) != targp->p_pid) 267 if ((pgrp = pgfind(SCARG(uap, pgid))) == 0 || 268 pgrp->pg_session != curp->p_session) 269 return (EPERM); 270 return (enterpgrp(targp, SCARG(uap, pgid), 0)); 271 } 272 273 /* ARGSUSED */ 274 int 275 sys_setuid(p, v, retval) 276 struct proc *p; 277 void *v; 278 register_t *retval; 279 { 280 struct sys_setuid_args /* { 281 syscallarg(uid_t) uid; 282 } */ *uap = v; 283 register struct pcred *pc = p->p_cred; 284 register uid_t uid; 285 int error; 286 287 #ifdef COMPAT_09 /* XXX */ 288 uid = (u_short)SCARG(uap, uid); 289 #else 290 uid = SCARG(uap, uid); 291 #endif 292 if (uid != pc->p_ruid && 293 uid != pc->p_svuid && 294 uid != pc->pc_ucred->cr_uid && 295 (error = suser(pc->pc_ucred, &p->p_acflag))) 296 return (error); 297 /* 298 * Everything's okay, do it. 299 */ 300 if (uid == pc->pc_ucred->cr_uid || 301 suser(pc->pc_ucred, &p->p_acflag) == 0) { 302 /* 303 * Transfer proc count to new user. 304 */ 305 if (uid != pc->p_ruid) { 306 (void)chgproccnt(pc->p_ruid, -1); 307 (void)chgproccnt(uid, 1); 308 } 309 pc->p_ruid = uid; 310 pc->p_svuid = uid; 311 } 312 /* 313 * Copy credentials so other references do not see our changes. 314 */ 315 pc->pc_ucred = crcopy(pc->pc_ucred); 316 pc->pc_ucred->cr_uid = uid; 317 p->p_flag |= P_SUGID; 318 return (0); 319 } 320 321 /* ARGSUSED */ 322 int 323 sys_seteuid(p, v, retval) 324 struct proc *p; 325 void *v; 326 register_t *retval; 327 { 328 struct sys_seteuid_args /* { 329 syscallarg(uid_t) euid; 330 } */ *uap = v; 331 register struct pcred *pc = p->p_cred; 332 register uid_t euid; 333 int error; 334 335 #ifdef COMPAT_09 /* XXX */ 336 euid = (u_short)SCARG(uap, euid); 337 #else 338 euid = SCARG(uap, euid); 339 #endif 340 if (euid != pc->p_ruid && euid != pc->p_svuid && 341 (error = suser(pc->pc_ucred, &p->p_acflag))) 342 return (error); 343 /* 344 * Everything's okay, do it. Copy credentials so other references do 345 * not see our changes. 346 */ 347 pc->pc_ucred = crcopy(pc->pc_ucred); 348 pc->pc_ucred->cr_uid = euid; 349 p->p_flag |= P_SUGID; 350 return (0); 351 } 352 353 /* ARGSUSED */ 354 int 355 sys_setgid(p, v, retval) 356 struct proc *p; 357 void *v; 358 register_t *retval; 359 { 360 struct sys_setgid_args /* { 361 syscallarg(gid_t) gid; 362 } */ *uap = v; 363 register struct pcred *pc = p->p_cred; 364 register gid_t gid; 365 int error; 366 367 #ifdef COMPAT_09 /* XXX */ 368 gid = (u_short)SCARG(uap, gid); 369 #else 370 gid = SCARG(uap, gid); 371 #endif 372 if (gid != pc->p_rgid && 373 gid != pc->p_svgid && 374 gid != pc->pc_ucred->cr_gid && 375 (error = suser(pc->pc_ucred, &p->p_acflag))) 376 return (error); 377 if (gid == pc->pc_ucred->cr_gid || 378 suser(pc->pc_ucred, &p->p_acflag) == 0) { 379 pc->p_rgid = gid; 380 pc->p_svgid = gid; 381 } 382 pc->pc_ucred = crcopy(pc->pc_ucred); 383 pc->pc_ucred->cr_gid = gid; 384 p->p_flag |= P_SUGID; 385 return (0); 386 } 387 388 /* ARGSUSED */ 389 int 390 sys_setegid(p, v, retval) 391 struct proc *p; 392 void *v; 393 register_t *retval; 394 { 395 struct sys_setegid_args /* { 396 syscallarg(gid_t) egid; 397 } */ *uap = v; 398 register struct pcred *pc = p->p_cred; 399 register gid_t egid; 400 int error; 401 402 #ifdef COMPAT_09 /* XXX */ 403 egid = (u_short)SCARG(uap, egid); 404 #else 405 egid = SCARG(uap, egid); 406 #endif 407 if (egid != pc->p_rgid && egid != pc->p_svgid && 408 (error = suser(pc->pc_ucred, &p->p_acflag))) 409 return (error); 410 pc->pc_ucred = crcopy(pc->pc_ucred); 411 pc->pc_ucred->cr_gid = egid; 412 p->p_flag |= P_SUGID; 413 return (0); 414 } 415 416 /* ARGSUSED */ 417 int 418 sys_setgroups(p, v, retval) 419 struct proc *p; 420 void *v; 421 register_t *retval; 422 { 423 struct sys_setgroups_args /* { 424 syscallarg(u_int) gidsetsize; 425 syscallarg(gid_t *) gidset; 426 } */ *uap = v; 427 register struct pcred *pc = p->p_cred; 428 register u_int ngrp; 429 int error; 430 431 if ((error = suser(pc->pc_ucred, &p->p_acflag)) != 0) 432 return (error); 433 ngrp = SCARG(uap, gidsetsize); 434 if (ngrp > NGROUPS) 435 return (EINVAL); 436 pc->pc_ucred = crcopy(pc->pc_ucred); 437 error = copyin((caddr_t)SCARG(uap, gidset), 438 (caddr_t)pc->pc_ucred->cr_groups, ngrp * sizeof(gid_t)); 439 if (error) 440 return (error); 441 pc->pc_ucred->cr_ngroups = ngrp; 442 p->p_flag |= P_SUGID; 443 return (0); 444 } 445 446 /* 447 * Check if gid is a member of the group set. 448 */ 449 int 450 groupmember(gid, cred) 451 gid_t gid; 452 register struct ucred *cred; 453 { 454 register gid_t *gp; 455 gid_t *egp; 456 457 egp = &(cred->cr_groups[cred->cr_ngroups]); 458 for (gp = cred->cr_groups; gp < egp; gp++) 459 if (*gp == gid) 460 return (1); 461 return (0); 462 } 463 464 /* 465 * Test whether the specified credentials imply "super-user" 466 * privilege; if so, and we have accounting info, set the flag 467 * indicating use of super-powers. 468 * Returns 0 or error. 469 */ 470 int 471 suser(cred, acflag) 472 struct ucred *cred; 473 u_short *acflag; 474 { 475 if (cred->cr_uid == 0) { 476 if (acflag) 477 *acflag |= ASU; 478 return (0); 479 } 480 return (EPERM); 481 } 482 483 /* 484 * Allocate a zeroed cred structure. 485 */ 486 struct ucred * 487 crget() 488 { 489 register struct ucred *cr; 490 491 MALLOC(cr, struct ucred *, sizeof(*cr), M_CRED, M_WAITOK); 492 bzero((caddr_t)cr, sizeof(*cr)); 493 cr->cr_ref = 1; 494 return (cr); 495 } 496 497 /* 498 * Free a cred structure. 499 * Throws away space when ref count gets to 0. 500 */ 501 void 502 crfree(cr) 503 struct ucred *cr; 504 { 505 int s; 506 507 s = splimp(); /* ??? */ 508 if (--cr->cr_ref == 0) 509 FREE((caddr_t)cr, M_CRED); 510 (void) splx(s); 511 } 512 513 /* 514 * Copy cred structure to a new one and free the old one. 515 */ 516 struct ucred * 517 crcopy(cr) 518 struct ucred *cr; 519 { 520 struct ucred *newcr; 521 522 if (cr->cr_ref == 1) 523 return (cr); 524 newcr = crget(); 525 *newcr = *cr; 526 crfree(cr); 527 newcr->cr_ref = 1; 528 return (newcr); 529 } 530 531 /* 532 * Dup cred struct to a new held one. 533 */ 534 struct ucred * 535 crdup(cr) 536 struct ucred *cr; 537 { 538 struct ucred *newcr; 539 540 newcr = crget(); 541 *newcr = *cr; 542 newcr->cr_ref = 1; 543 return (newcr); 544 } 545 546 /* 547 * Get login name, if available. 548 */ 549 /* ARGSUSED */ 550 int 551 sys_getlogin(p, v, retval) 552 struct proc *p; 553 void *v; 554 register_t *retval; 555 { 556 struct sys_getlogin_args /* { 557 syscallarg(char *) namebuf; 558 syscallarg(u_int) namelen; 559 } */ *uap = v; 560 561 if (SCARG(uap, namelen) > sizeof (p->p_pgrp->pg_session->s_login)) 562 SCARG(uap, namelen) = sizeof (p->p_pgrp->pg_session->s_login); 563 return (copyout((caddr_t) p->p_pgrp->pg_session->s_login, 564 (caddr_t) SCARG(uap, namebuf), SCARG(uap, namelen))); 565 } 566 567 /* 568 * Set login name. 569 */ 570 /* ARGSUSED */ 571 int 572 sys_setlogin(p, v, retval) 573 struct proc *p; 574 void *v; 575 register_t *retval; 576 { 577 struct sys_setlogin_args /* { 578 syscallarg(char *) namebuf; 579 } */ *uap = v; 580 int error; 581 582 if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) 583 return (error); 584 error = copyinstr((caddr_t) SCARG(uap, namebuf), 585 (caddr_t) p->p_pgrp->pg_session->s_login, 586 sizeof (p->p_pgrp->pg_session->s_login), (size_t *)0); 587 if (error == ENAMETOOLONG) 588 error = EINVAL; 589 return (error); 590 } 591