1 /* $OpenBSD: kern_prot.c,v 1.4 1996/08/25 09:51:37 deraadt 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, pid) != 0 && SCARG(uap, pid) != curp->p_pid) { 251 if ((targp = pfind(SCARG(uap, pid))) == 0 || !inferior(targp)) 252 return (ESRCH); 253 if (targp->p_session != curp->p_session) 254 return (EPERM); 255 if (targp->p_flag & P_EXEC) 256 return (EACCES); 257 } else 258 targp = curp; 259 if (SESS_LEADER(targp)) 260 return (EPERM); 261 if (SCARG(uap, pgid) == 0) 262 SCARG(uap, pgid) = targp->p_pid; 263 else if (SCARG(uap, pgid) != targp->p_pid) 264 if ((pgrp = pgfind(SCARG(uap, pgid))) == 0 || 265 pgrp->pg_session != curp->p_session) 266 return (EPERM); 267 return (enterpgrp(targp, SCARG(uap, pgid), 0)); 268 } 269 270 /* ARGSUSED */ 271 int 272 sys_setuid(p, v, retval) 273 struct proc *p; 274 void *v; 275 register_t *retval; 276 { 277 struct sys_setuid_args /* { 278 syscallarg(uid_t) uid; 279 } */ *uap = v; 280 register struct pcred *pc = p->p_cred; 281 register uid_t uid; 282 int error; 283 284 #ifdef COMPAT_09 /* XXX */ 285 uid = (u_short)SCARG(uap, uid); 286 #else 287 uid = SCARG(uap, uid); 288 #endif 289 if (uid != pc->p_ruid && 290 uid != pc->pc_ucred->cr_uid && 291 (error = suser(pc->pc_ucred, &p->p_acflag))) 292 return (error); 293 /* 294 * Everything's okay, do it. 295 */ 296 if (uid == pc->pc_ucred->cr_uid || 297 suser(pc->pc_ucred, &p->p_acflag) == 0) { 298 /* 299 * Transfer proc count to new user. 300 */ 301 if (uid != pc->p_ruid) { 302 (void)chgproccnt(pc->p_ruid, -1); 303 (void)chgproccnt(uid, 1); 304 } 305 pc->p_ruid = uid; 306 pc->p_svuid = uid; 307 } 308 /* 309 * Copy credentials so other references do not see our changes. 310 */ 311 pc->pc_ucred = crcopy(pc->pc_ucred); 312 pc->pc_ucred->cr_uid = uid; 313 p->p_flag |= P_SUGID; 314 return (0); 315 } 316 317 /* ARGSUSED */ 318 int 319 sys_seteuid(p, v, retval) 320 struct proc *p; 321 void *v; 322 register_t *retval; 323 { 324 struct sys_seteuid_args /* { 325 syscallarg(uid_t) euid; 326 } */ *uap = v; 327 register struct pcred *pc = p->p_cred; 328 register uid_t euid; 329 int error; 330 331 #ifdef COMPAT_09 /* XXX */ 332 euid = (u_short)SCARG(uap, euid); 333 #else 334 euid = SCARG(uap, euid); 335 #endif 336 if (euid != pc->p_ruid && euid != pc->p_svuid && 337 (error = suser(pc->pc_ucred, &p->p_acflag))) 338 return (error); 339 /* 340 * Everything's okay, do it. Copy credentials so other references do 341 * not see our changes. 342 */ 343 pc->pc_ucred = crcopy(pc->pc_ucred); 344 pc->pc_ucred->cr_uid = euid; 345 p->p_flag |= P_SUGID; 346 return (0); 347 } 348 349 /* ARGSUSED */ 350 int 351 sys_setgid(p, v, retval) 352 struct proc *p; 353 void *v; 354 register_t *retval; 355 { 356 struct sys_setgid_args /* { 357 syscallarg(gid_t) gid; 358 } */ *uap = v; 359 register struct pcred *pc = p->p_cred; 360 register gid_t gid; 361 int error; 362 363 #ifdef COMPAT_09 /* XXX */ 364 gid = (u_short)SCARG(uap, gid); 365 #else 366 gid = SCARG(uap, gid); 367 #endif 368 if (gid != pc->p_rgid && 369 gid != pc->pc_ucred->cr_gid && 370 (error = suser(pc->pc_ucred, &p->p_acflag))) 371 return (error); 372 if (gid == pc->pc_ucred->cr_gid || 373 suser(pc->pc_ucred, &p->p_acflag) == 0) { 374 pc->p_rgid = gid; 375 pc->p_svgid = gid; 376 } 377 pc->pc_ucred = crcopy(pc->pc_ucred); 378 pc->pc_ucred->cr_gid = gid; 379 p->p_flag |= P_SUGID; 380 return (0); 381 } 382 383 /* ARGSUSED */ 384 int 385 sys_setegid(p, v, retval) 386 struct proc *p; 387 void *v; 388 register_t *retval; 389 { 390 struct sys_setegid_args /* { 391 syscallarg(gid_t) egid; 392 } */ *uap = v; 393 register struct pcred *pc = p->p_cred; 394 register gid_t egid; 395 int error; 396 397 #ifdef COMPAT_09 /* XXX */ 398 egid = (u_short)SCARG(uap, egid); 399 #else 400 egid = SCARG(uap, egid); 401 #endif 402 if (egid != pc->p_rgid && egid != pc->p_svgid && 403 (error = suser(pc->pc_ucred, &p->p_acflag))) 404 return (error); 405 pc->pc_ucred = crcopy(pc->pc_ucred); 406 pc->pc_ucred->cr_gid = egid; 407 p->p_flag |= P_SUGID; 408 return (0); 409 } 410 411 /* ARGSUSED */ 412 int 413 sys_setgroups(p, v, retval) 414 struct proc *p; 415 void *v; 416 register_t *retval; 417 { 418 struct sys_setgroups_args /* { 419 syscallarg(u_int) gidsetsize; 420 syscallarg(gid_t *) gidset; 421 } */ *uap = v; 422 register struct pcred *pc = p->p_cred; 423 register u_int ngrp; 424 int error; 425 426 if ((error = suser(pc->pc_ucred, &p->p_acflag)) != 0) 427 return (error); 428 ngrp = SCARG(uap, gidsetsize); 429 if (ngrp > NGROUPS) 430 return (EINVAL); 431 pc->pc_ucred = crcopy(pc->pc_ucred); 432 error = copyin((caddr_t)SCARG(uap, gidset), 433 (caddr_t)pc->pc_ucred->cr_groups, ngrp * sizeof(gid_t)); 434 if (error) 435 return (error); 436 pc->pc_ucred->cr_ngroups = ngrp; 437 p->p_flag |= P_SUGID; 438 return (0); 439 } 440 441 /* 442 * Check if gid is a member of the group set. 443 */ 444 int 445 groupmember(gid, cred) 446 gid_t gid; 447 register struct ucred *cred; 448 { 449 register gid_t *gp; 450 gid_t *egp; 451 452 egp = &(cred->cr_groups[cred->cr_ngroups]); 453 for (gp = cred->cr_groups; gp < egp; gp++) 454 if (*gp == gid) 455 return (1); 456 return (0); 457 } 458 459 /* 460 * Test whether the specified credentials imply "super-user" 461 * privilege; if so, and we have accounting info, set the flag 462 * indicating use of super-powers. 463 * Returns 0 or error. 464 */ 465 int 466 suser(cred, acflag) 467 struct ucred *cred; 468 u_short *acflag; 469 { 470 if (cred->cr_uid == 0) { 471 if (acflag) 472 *acflag |= ASU; 473 return (0); 474 } 475 return (EPERM); 476 } 477 478 /* 479 * Allocate a zeroed cred structure. 480 */ 481 struct ucred * 482 crget() 483 { 484 register struct ucred *cr; 485 486 MALLOC(cr, struct ucred *, sizeof(*cr), M_CRED, M_WAITOK); 487 bzero((caddr_t)cr, sizeof(*cr)); 488 cr->cr_ref = 1; 489 return (cr); 490 } 491 492 /* 493 * Free a cred structure. 494 * Throws away space when ref count gets to 0. 495 */ 496 void 497 crfree(cr) 498 struct ucred *cr; 499 { 500 int s; 501 502 s = splimp(); /* ??? */ 503 if (--cr->cr_ref == 0) 504 FREE((caddr_t)cr, M_CRED); 505 (void) splx(s); 506 } 507 508 /* 509 * Copy cred structure to a new one and free the old one. 510 */ 511 struct ucred * 512 crcopy(cr) 513 struct ucred *cr; 514 { 515 struct ucred *newcr; 516 517 if (cr->cr_ref == 1) 518 return (cr); 519 newcr = crget(); 520 *newcr = *cr; 521 crfree(cr); 522 newcr->cr_ref = 1; 523 return (newcr); 524 } 525 526 /* 527 * Dup cred struct to a new held one. 528 */ 529 struct ucred * 530 crdup(cr) 531 struct ucred *cr; 532 { 533 struct ucred *newcr; 534 535 newcr = crget(); 536 *newcr = *cr; 537 newcr->cr_ref = 1; 538 return (newcr); 539 } 540 541 /* 542 * Get login name, if available. 543 */ 544 /* ARGSUSED */ 545 int 546 sys_getlogin(p, v, retval) 547 struct proc *p; 548 void *v; 549 register_t *retval; 550 { 551 struct sys_getlogin_args /* { 552 syscallarg(char *) namebuf; 553 syscallarg(u_int) namelen; 554 } */ *uap = v; 555 556 if (SCARG(uap, namelen) > sizeof (p->p_pgrp->pg_session->s_login)) 557 SCARG(uap, namelen) = sizeof (p->p_pgrp->pg_session->s_login); 558 return (copyout((caddr_t) p->p_pgrp->pg_session->s_login, 559 (caddr_t) SCARG(uap, namebuf), SCARG(uap, namelen))); 560 } 561 562 /* 563 * Set login name. 564 */ 565 /* ARGSUSED */ 566 int 567 sys_setlogin(p, v, retval) 568 struct proc *p; 569 void *v; 570 register_t *retval; 571 { 572 struct sys_setlogin_args /* { 573 syscallarg(char *) namebuf; 574 } */ *uap = v; 575 int error; 576 577 if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) 578 return (error); 579 error = copyinstr((caddr_t) SCARG(uap, namebuf), 580 (caddr_t) p->p_pgrp->pg_session->s_login, 581 sizeof (p->p_pgrp->pg_session->s_login) - 1, (size_t *)0); 582 if (error == ENAMETOOLONG) 583 error = EINVAL; 584 return (error); 585 } 586