1/* 2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ 29/* 30 * Copyright (c) 1982, 1986, 1989, 1990, 1991, 1993 31 * The Regents of the University of California. All rights reserved. 32 * (c) UNIX System Laboratories, Inc. 33 * All or some portions of this file are derived from material licensed 34 * to the University of California by American Telephone and Telegraph 35 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 36 * the permission of UNIX System Laboratories, Inc. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 1. Redistributions of source code must retain the above copyright 42 * notice, this list of conditions and the following disclaimer. 43 * 2. Redistributions in binary form must reproduce the above copyright 44 * notice, this list of conditions and the following disclaimer in the 45 * documentation and/or other materials provided with the distribution. 46 * 3. All advertising materials mentioning features or use of this software 47 * must display the following acknowledgement: 48 * This product includes software developed by the University of 49 * California, Berkeley and its contributors. 50 * 4. Neither the name of the University nor the names of its contributors 51 * may be used to endorse or promote products derived from this software 52 * without specific prior written permission. 53 * 54 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 55 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 57 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 58 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 59 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 60 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 61 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 62 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 63 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 64 * SUCH DAMAGE. 65 * 66 * @(#)kern_prot.c 8.9 (Berkeley) 2/14/95 67 */ 68/* 69 * NOTICE: This file was modified by McAfee Research in 2004 to introduce 70 * support for mandatory and extensible security protections. This notice 71 * is included in support of clause 2.2 (b) of the Apple Public License, 72 * Version 2.0. 73 */ 74/* 75 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce 76 * support for mandatory and extensible security protections. This notice 77 * is included in support of clause 2.2 (b) of the Apple Public License, 78 * Version 2.0. 79 */ 80 81/* 82 * System calls related to processes and protection 83 */ 84 85#include <sys/param.h> 86#include <sys/acct.h> 87#include <sys/systm.h> 88#include <sys/ucred.h> 89#include <sys/proc_internal.h> 90#include <sys/user.h> 91#include <sys/kauth.h> 92#include <sys/timeb.h> 93#include <sys/times.h> 94#include <sys/malloc.h> 95 96#include <bsm/audit_kernel.h> 97 98#if CONFIG_LCTX 99#include <sys/lctx.h> 100#endif 101 102#if CONFIG_MACF 103#include <security/mac_framework.h> 104#if CONFIG_MACF_MACH 105#include <secuity/mac_mach_internal.h> 106#endif 107#endif 108 109#include <sys/mount_internal.h> 110#include <sys/sysproto.h> 111#include <mach/message.h> 112#include <mach/host_security.h> 113 114#include <kern/host.h> 115#include <kern/task.h> /* for current_task() */ 116#include <kern/assert.h> 117 118 119int groupmember(gid_t gid, kauth_cred_t cred); 120 121/* 122 * Credential debugging; we can track entry into a function that might 123 * change a credential, and we can track actual credential changes that 124 * result. 125 * 126 * Note: Does *NOT* currently include per-thread credential changes 127 * 128 * We don't use kauth_cred_print() in current debugging, but it 129 * can be used if needed when debugging is active. 130 */ 131#if DEBUG_CRED 132#define DEBUG_CRED_ENTER printf 133#define DEBUG_CRED_CHANGE printf 134extern void kauth_cred_print(kauth_cred_t cred); 135#else /* !DEBUG_CRED */ 136#define DEBUG_CRED_ENTER(fmt, ...) do {} while (0) 137#define DEBUG_CRED_CHANGE(fmt, ...) do {} while (0) 138#endif /* !DEBUG_CRED */ 139 140 141 142/* 143 * setprivexec 144 * 145 * Description: (dis)allow this process to hold task, thread, or execption 146 * ports of processes about to exec. 147 * 148 * Parameters: uap->flag New value for flag 149 * 150 * Returns: int Previous value of flag 151 * 152 * XXX: Belongs in kern_proc.c 153 */ 154int 155setprivexec(proc_t p, struct setprivexec_args *uap, register_t *retval) 156{ 157 AUDIT_ARG(value, uap->flag); 158 *retval = p->p_debugger; 159 p->p_debugger = (uap->flag != 0); 160 return(0); 161} 162 163 164/* 165 * getpid 166 * 167 * Description: get the process ID 168 * 169 * Parameters: (void) 170 * 171 * Returns: pid_t Current process ID 172 * 173 * XXX: Belongs in kern_proc.c 174 */ 175int 176getpid(proc_t p, __unused struct getpid_args *uap, register_t *retval) 177{ 178 179 *retval = p->p_pid; 180 return (0); 181} 182 183 184/* 185 * getppid 186 * 187 * Description: get the parent process ID 188 * 189 * Parameters: (void) 190 * 191 * Returns: pid_t Parent process ID 192 * 193 * XXX: Belongs in kern_proc.c 194 */ 195int 196getppid(proc_t p, __unused struct getppid_args *uap, register_t *retval) 197{ 198 199 *retval = p->p_ppid; 200 return (0); 201} 202 203 204/* 205 * getpgrp 206 * 207 * Description: get the process group ID of the calling process 208 * 209 * Parameters: (void) 210 * 211 * Returns: pid_t Process group ID 212 * 213 * XXX: Belongs in kern_proc.c 214 */ 215int 216getpgrp(proc_t p, __unused struct getpgrp_args *uap, register_t *retval) 217{ 218 219 *retval = p->p_pgrpid; 220 return (0); 221} 222 223 224/* 225 * getpgid 226 * 227 * Description: Get an arbitary pid's process group id 228 * 229 * Parameters: uap->pid The target pid 230 * 231 * Returns: 0 Success 232 * ESRCH No such process 233 * 234 * Notes: We are permitted to return EPERM in the case that the target 235 * process is not in the same session as the calling process, 236 * which could be a security consideration 237 * 238 * XXX: Belongs in kern_proc.c 239 */ 240int 241getpgid(proc_t p, struct getpgid_args *uap, register_t *retval) 242{ 243 proc_t pt; 244 int refheld = 0; 245 246 pt = p; 247 if (uap->pid == 0) 248 goto found; 249 250 if ((pt = proc_find(uap->pid)) == 0) 251 return (ESRCH); 252 refheld = 1; 253found: 254 *retval = pt->p_pgrpid; 255 if (refheld != 0) 256 proc_rele(pt); 257 return (0); 258} 259 260 261/* 262 * getsid 263 * 264 * Description: Get an arbitary pid's session leaders process group ID 265 * 266 * Parameters: uap->pid The target pid 267 * 268 * Returns: 0 Success 269 * ESRCH No such process 270 * 271 * Notes: We are permitted to return EPERM in the case that the target 272 * process is not in the same session as the calling process, 273 * which could be a security consideration 274 * 275 * XXX: Belongs in kern_proc.c 276 */ 277int 278getsid(proc_t p, struct getsid_args *uap, register_t *retval) 279{ 280 proc_t pt; 281 int refheld = 0; 282 struct session * sessp; 283 284 pt = p; 285 if (uap->pid == 0) 286 goto found; 287 288 if ((pt = proc_find(uap->pid)) == 0) 289 return (ESRCH); 290 refheld = 1; 291found: 292 sessp = proc_session(pt); 293 *retval = sessp->s_sid; 294 session_rele(sessp); 295 296 if (refheld != 0) 297 proc_rele(pt); 298 return (0); 299} 300 301 302/* 303 * getuid 304 * 305 * Description: get real user ID for caller 306 * 307 * Parameters: (void) 308 * 309 * Returns: uid_t The real uid of the caller 310 */ 311int 312getuid(__unused proc_t p, __unused struct getuid_args *uap, register_t *retval) 313{ 314 315 *retval = kauth_getruid(); 316 return (0); 317} 318 319 320/* 321 * geteuid 322 * 323 * Description: get effective user ID for caller 324 * 325 * Parameters: (void) 326 * 327 * Returns: uid_t The effective uid of the caller 328 */ 329int 330geteuid(__unused proc_t p, __unused struct geteuid_args *uap, register_t *retval) 331{ 332 333 *retval = kauth_getuid(); 334 return (0); 335} 336 337 338/* 339 * gettid 340 * 341 * Description: Return the per-thread override identity. 342 * 343 * Parameters: uap->uidp Address of uid_t to get uid 344 * uap->gidp Address of gid_t to get gid 345 * 346 * Returns: 0 Success 347 * ESRCH No per thread identity active 348 */ 349int 350gettid(__unused proc_t p, struct gettid_args *uap, register_t *retval) 351{ 352 struct uthread *uthread = get_bsdthread_info(current_thread()); 353 int error; 354 355 /* 356 * If this thread is not running with an override identity, we can't 357 * return one to the caller, so return an error instead. 358 */ 359 if (!(uthread->uu_flag & UT_SETUID)) 360 return (ESRCH); 361 362 if ((error = suword(uap->uidp, uthread->uu_ucred->cr_ruid))) 363 return (error); 364 if ((error = suword(uap->gidp, uthread->uu_ucred->cr_rgid))) 365 return (error); 366 367 *retval = 0; 368 return (0); 369} 370 371 372/* 373 * getgid 374 * 375 * Description: get the real group ID for the calling process 376 * 377 * Parameters: (void) 378 * 379 * Returns: gid_t The real gid of the caller 380 */ 381int 382getgid(__unused proc_t p, __unused struct getgid_args *uap, register_t *retval) 383{ 384 385 *retval = kauth_getrgid(); 386 return (0); 387} 388 389 390/* 391 * getegid 392 * 393 * Description: get the effective group ID for the calling process 394 * 395 * Parameters: (void) 396 * 397 * Returns: gid_t The effective gid of the caller 398 * 399 * Notes: As an implementation detail, the effective gid is stored as 400 * the first element of the supplementary group list. 401 * 402 * This could be implemented in Libc instead because of the above 403 * detail. 404 */ 405int 406getegid(__unused proc_t p, __unused struct getegid_args *uap, register_t *retval) 407{ 408 409 *retval = kauth_getgid(); 410 return (0); 411} 412 413 414/* 415 * getgroups 416 * 417 * Description: get the list of supplementary groups for the calling process 418 * 419 * Parameters: uap->gidsetsize # of gid_t's in user buffer 420 * uap->gidset Pointer to user buffer 421 * 422 * Returns: 0 Success 423 * EINVAL User buffer too small 424 * copyout:EFAULT User buffer invalid 425 * 426 * Retval: -1 Error 427 * !0 # of groups 428 * 429 * Notes: The caller may specify a 0 value for gidsetsize, and we will 430 * then return how large a buffer is required (in gid_t's) to 431 * contain the answer at the time of the call. Otherwise, we 432 * return the number of gid_t's catually copied to user space. 433 * 434 * When called with a 0 gidsetsize from a multithreaded program, 435 * there is no guarantee that another thread may not change the 436 * number of supplementary groups, and therefore a subsequent 437 * call could still fail, unless the maximum possible buffer 438 * size is supplied by the user. 439 * 440 * As an implementation detail, the effective gid is stored as 441 * the first element of the supplementary group list, and will 442 * be returned by this call. 443 */ 444int 445getgroups(__unused proc_t p, struct getgroups_args *uap, register_t *retval) 446{ 447 int ngrp; 448 int error; 449 kauth_cred_t cred; 450 451 /* grab reference while we muck around with the credential */ 452 cred = kauth_cred_get_with_ref(); 453 454 if ((ngrp = uap->gidsetsize) == 0) { 455 *retval = cred->cr_ngroups; 456 kauth_cred_unref(&cred); 457 return (0); 458 } 459 if (ngrp < cred->cr_ngroups) { 460 kauth_cred_unref(&cred); 461 return (EINVAL); 462 } 463 ngrp = cred->cr_ngroups; 464 if ((error = copyout((caddr_t)cred->cr_groups, 465 uap->gidset, 466 ngrp * sizeof(gid_t)))) { 467 kauth_cred_unref(&cred); 468 return (error); 469 } 470 kauth_cred_unref(&cred); 471 *retval = ngrp; 472 return (0); 473} 474 475 476/* 477 * Return the per-thread/per-process supplementary groups list. 478 */ 479#warning XXX implement getsgroups 480int 481getsgroups(__unused proc_t p, __unused struct getsgroups_args *uap, __unused register_t *retval) 482{ 483 /* XXX implement */ 484 return(ENOTSUP); 485} 486 487/* 488 * Return the per-thread/per-process whiteout groups list. 489 */ 490#warning XXX implement getwgroups 491int 492getwgroups(__unused proc_t p, __unused struct getwgroups_args *uap, __unused register_t *retval) 493{ 494 /* XXX implement */ 495 return(ENOTSUP); 496} 497 498 499/* 500 * setsid 501 * 502 * Description: Create a new session and set the process group ID to the 503 * session ID 504 * 505 * Parameters: (void) 506 * 507 * Returns: 0 Success 508 * EPERM Permission denied 509 * 510 * Notes: If the calling process is not the process group leader; there 511 * is no existing process group with its ID, and we are not 512 * currently in vfork, then this function will create a new 513 * session, a new process group, and put the caller in the 514 * process group (as the sole member) and make it the session 515 * leader (as the sole process in the session). 516 * 517 * The existing controlling tty (if any) will be dissociated 518 * from the process, and the next non-O_NOCTTY open of a tty 519 * will establish a new controlling tty. 520 * 521 * XXX: Belongs in kern_proc.c 522 */ 523int 524setsid(proc_t p, __unused struct setsid_args *uap, register_t *retval) 525{ 526 struct pgrp * pg = PGRP_NULL; 527 528 if (p->p_pgrpid == p->p_pid || (pg = pgfind(p->p_pid)) || p->p_lflag & P_LINVFORK) { 529 if (pg != PGRP_NULL) 530 pg_rele(pg); 531 return (EPERM); 532 } else { 533 /* enter pgrp works with its own pgrp refcount */ 534 (void)enterpgrp(p, p->p_pid, 1); 535 *retval = p->p_pid; 536 return (0); 537 } 538} 539 540 541/* 542 * setpgid 543 * 544 * Description: set process group ID for job control 545 * 546 * Parameters: uap->pid Process to change 547 * uap->pgid Process group to join or create 548 * 549 * Returns: 0 Success 550 * ESRCH pid is not the caller or a child of 551 * the caller 552 * enterpgrp:ESRCH No such process 553 * EACCES Permission denied due to exec 554 * EINVAL Invalid argument 555 * EPERM The target process is not in the same 556 * session as the calling process 557 * EPERM The target process is a session leader 558 * EPERM pid and pgid are not the same, and 559 * there is no process in the calling 560 * process whose process group ID matches 561 * pgid 562 * 563 * Notes: This function will cause the target process to either join 564 * an existing process process group, or create a new process 565 * group in the session of the calling process. It cannot be 566 * used to change the process group ID of a process which is 567 * already a session leader. 568 * 569 * If the target pid is 0, the pid of the calling process is 570 * substituted as the new target; if pgid is 0, the target pid 571 * is used as the target process group ID. 572 * 573 * Legacy: This system call entry point is also used to implement the 574 * legacy library routine setpgrp(), which under POSIX 575 * 576 * XXX: Belongs in kern_proc.c 577 */ 578int 579setpgid(proc_t curp, register struct setpgid_args *uap, __unused register_t *retval) 580{ 581 proc_t targp = PROC_NULL; /* target process */ 582 struct pgrp *pg = PGRP_NULL; /* target pgrp */ 583 int error = 0; 584 int refheld = 0; 585 int samesess = 0; 586 struct session * curp_sessp = SESSION_NULL; 587 struct session * targp_sessp = SESSION_NULL; 588 589 curp_sessp = proc_session(curp); 590 591 if (uap->pid != 0 && uap->pid != curp->p_pid) { 592 if ((targp = proc_find(uap->pid)) == 0 || !inferior(targp)) { 593 if (targp != PROC_NULL) 594 refheld = 1; 595 error = ESRCH; 596 goto out; 597 } 598 refheld = 1; 599 targp_sessp = proc_session(targp); 600 if (targp_sessp != curp_sessp) { 601 error = EPERM; 602 goto out; 603 } 604 if (targp->p_flag & P_EXEC) { 605 error = EACCES; 606 goto out; 607 } 608 } else { 609 targp = curp; 610 targp_sessp = proc_session(targp); 611 } 612 613 if (SESS_LEADER(targp, targp_sessp)) { 614 error = EPERM; 615 goto out; 616 } 617 if (targp_sessp != SESSION_NULL) { 618 session_rele(targp_sessp); 619 targp_sessp = SESSION_NULL; 620 } 621 622 if (uap->pgid < 0) { 623 error = EINVAL; 624 goto out; 625 } 626 if (uap->pgid == 0) 627 uap->pgid = targp->p_pid; 628 else if (uap->pgid != targp->p_pid) { 629 if ((pg = pgfind(uap->pgid)) == 0){ 630 error = EPERM; 631 goto out; 632 } 633 samesess = (pg->pg_session != curp_sessp); 634 pg_rele(pg); 635 if (samesess != 0) { 636 error = EPERM; 637 goto out; 638 } 639 } 640 error = enterpgrp(targp, uap->pgid, 0); 641out: 642 if (targp_sessp != SESSION_NULL) 643 session_rele(targp_sessp); 644 if (curp_sessp != SESSION_NULL) 645 session_rele(curp_sessp); 646 if (refheld != 0) 647 proc_rele(targp); 648 return(error); 649} 650 651 652/* 653 * issetugid 654 * 655 * Description: Is current process tainted by uid or gid changes system call 656 * 657 * Parameters: (void) 658 * 659 * Returns: 0 Not tainted 660 * 1 Tainted 661 * 662 * Notes: A process is considered tainted if it was created as a retult 663 * of an execve call from an imnage that had either the SUID or 664 * SGID bit set on the executable, or if it has changed any of its 665 * real, effective, or saved user or group IDs since beginning 666 * execution. 667 */ 668int 669issetugid(proc_t p, __unused struct issetugid_args *uap, register_t *retval) 670{ 671 /* 672 * Note: OpenBSD sets a P_SUGIDEXEC flag set at execve() time, 673 * we use P_SUGID because we consider changing the owners as 674 * "tainting" as well. 675 * This is significant for procs that start as root and "become" 676 * a user without an exec - programs cannot know *everything* 677 * that libc *might* have put in their data segment. 678 */ 679 680 *retval = (p->p_flag & P_SUGID) ? 1 : 0; 681 return (0); 682} 683 684 685/* 686 * setuid 687 * 688 * Description: Set user ID system call 689 * 690 * Parameters: uap->uid uid to set 691 * 692 * Returns: 0 Success 693 * suser:EPERM Permission denied 694 * 695 * Notes: If called by a privileged process, this function will set the 696 * real, effective, and saved uid to the requested value. 697 * 698 * If called from an unprivileged process, but uid is equal to the 699 * real or saved uid, then the effective uid will be set to the 700 * requested value, but the real and saved uid will not change. 701 * 702 * If the credential is changed as a result of this call, then we 703 * flag the process as having set privilege since the last exec. 704 */ 705int 706setuid(proc_t p, struct setuid_args *uap, __unused register_t *retval) 707{ 708 uid_t uid; 709 uid_t svuid = KAUTH_UID_NONE; 710 uid_t ruid = KAUTH_UID_NONE; 711 uid_t gmuid = KAUTH_UID_NONE; 712 int error; 713 kauth_cred_t my_cred, my_new_cred; 714 715 716 uid = uap->uid; 717 718 my_cred = kauth_cred_proc_ref(p); 719 720 DEBUG_CRED_ENTER("setuid (%d/%d): %p %d\n", p->p_pid, (p->p_pptr ? p->p_pptr->p_pid : 0), my_cred, uap->uid); 721 AUDIT_ARG(uid, uid, 0, 0, 0); 722 723 if (uid != my_cred->cr_ruid && /* allow setuid(getuid()) */ 724 uid != my_cred->cr_svuid && /* allow setuid(saved uid) */ 725 (error = suser(my_cred, &p->p_acflag))) { 726 kauth_cred_unref(&my_cred); 727 return (error); 728 } 729 /* 730 * Everything's okay, do it. 731 */ 732 733 /* 734 * If we are priviledged, then set the saved and real UID too; 735 * otherwise, just set the effective UID 736 */ 737 if (suser(my_cred, &p->p_acflag) == 0) { 738 svuid = uid; 739 ruid = uid; 740 /* 741 * Transfer proc count to new user. 742 * chgproccnt uses list lock for protection 743 */ 744 (void)chgproccnt(uid, 1); 745 (void)chgproccnt(kauth_getruid(), -1); 746 } 747 748 /* get current credential and take a reference while we muck with it */ 749 for (;;) { 750 /* 751 * Only set the gmuid if the current cred has not opt'ed out; 752 * this normally only happens when calling setgroups() instead 753 * of initgroups() to set an explicit group list, or one of the 754 * other group manipulation functions is invoked and results in 755 * a dislocation (i.e. the credential group membership changes 756 * to something other than the default list for the user, as 757 * in entering a group or leaving an exclusion group). 758 */ 759 if (!(my_cred->cr_flags & CRF_NOMEMBERD)) 760 gmuid = uid; 761 762 /* 763 * Set the credential with new info. If there is no change, 764 * we get back the same credential we passed in; if there is 765 * a change, we drop the reference on the credential we 766 * passed in. The subsequent compare is safe, because it is 767 * a pointer compare rather than a contents compare. 768 */ 769 my_new_cred = kauth_cred_setresuid(my_cred, ruid, uid, svuid, gmuid); 770 if (my_cred != my_new_cred) { 771 772 DEBUG_CRED_CHANGE("setuid CH(%d): %p/0x%08x -> %p/0x%08x\n", p->p_pid, my_cred, my_cred->cr_flags, my_new_cred, my_new_cred->cr_flags); 773 774 proc_lock(p); 775 /* 776 * We need to protect for a race where another thread 777 * also changed the credential after we took our 778 * reference. If p_ucred has changed then we should 779 * restart this again with the new cred. 780 */ 781 if (p->p_ucred != my_cred) { 782 proc_unlock(p); 783 kauth_cred_unref(&my_new_cred); 784 my_cred = kauth_cred_proc_ref(p); 785 /* try again */ 786 continue; 787 } 788 p->p_ucred = my_new_cred; 789 OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag); 790 proc_unlock(p); 791 } 792 break; 793 } 794 /* Drop old proc reference or our extra reference */ 795 kauth_cred_unref(&my_cred); 796 797 set_security_token(p); 798 return (0); 799} 800 801 802/* 803 * seteuid 804 * 805 * Description: Set effective user ID system call 806 * 807 * Parameters: uap->euid effective uid to set 808 * 809 * Returns: 0 Success 810 * suser:EPERM Permission denied 811 * 812 * Notes: If called by a privileged process, or called from an 813 * unprivileged process but euid is equal to the real or saved 814 * uid, then the effective uid will be set to the requested 815 * value, but the real and saved uid will not change. 816 * 817 * If the credential is changed as a result of this call, then we 818 * flag the process as having set privilege since the last exec. 819 */ 820int 821seteuid(proc_t p, struct seteuid_args *uap, __unused register_t *retval) 822{ 823 uid_t euid; 824 int error; 825 kauth_cred_t my_cred, my_new_cred; 826 827 DEBUG_CRED_ENTER("seteuid: %d\n", uap->euid); 828 829 euid = uap->euid; 830 AUDIT_ARG(uid, 0, euid, 0, 0); 831 832 my_cred = kauth_cred_proc_ref(p); 833 834 if (euid != my_cred->cr_ruid && euid != my_cred->cr_svuid && 835 (error = suser(my_cred, &p->p_acflag))) { 836 kauth_cred_unref(&my_cred); 837 return (error); 838 } 839 840 /* 841 * Everything's okay, do it. Copy credentials so other references do 842 * not see our changes. get current credential and take a reference 843 * while we muck with it 844 */ 845 for (;;) { 846 /* 847 * Set the credential with new info. If there is no change, 848 * we get back the same credential we passed in; if there is 849 * a change, we drop the reference on the credential we 850 * passed in. The subsequent compare is safe, because it is 851 * a pointer compare rather than a contents compare. 852 */ 853 my_new_cred = kauth_cred_setresuid(my_cred, KAUTH_UID_NONE, euid, KAUTH_UID_NONE, my_cred->cr_gmuid); 854 855 if (my_cred != my_new_cred) { 856 857 DEBUG_CRED_CHANGE("seteuid CH(%d): %p/0x%08x -> %p/0x%08x\n", p->p_pid, my_cred, my_cred->cr_flags, my_new_cred, my_new_cred->cr_flags); 858 859 proc_lock(p); 860 /* 861 * We need to protect for a race where another thread 862 * also changed the credential after we took our 863 * reference. If p_ucred has changed then we 864 * should restart this again with the new cred. 865 */ 866 if (p->p_ucred != my_cred) { 867 proc_unlock(p); 868 kauth_cred_unref(&my_new_cred); 869 my_cred = kauth_cred_proc_ref(p); 870 /* try again */ 871 continue; 872 } 873 p->p_ucred = my_new_cred; 874 OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag); 875 proc_unlock(p); 876 } 877 break; 878 } 879 /* drop old proc reference or our extra reference */ 880 kauth_cred_unref(&my_cred); 881 882 set_security_token(p); 883 return (0); 884} 885 886 887/* 888 * setreuid 889 * 890 * Description: Set real and effective user ID system call 891 * 892 * Parameters: uap->ruid real uid to set 893 * uap->euid effective uid to set 894 * 895 * Returns: 0 Success 896 * suser:EPERM Permission denied 897 * 898 * Notes: A value of -1 is a special case indicating that the uid for 899 * which that value is specified not be changed. If both values 900 * are specified as -1, no action is taken. 901 * 902 * If called by a privileged process, the real and effective uid 903 * will be set to the new value(s) specified. 904 * 905 * If called from an unprivileged process, the real uid may be 906 * set to the current value of the real uid, or to the current 907 * value of the saved uid. The effective uid may be set to the 908 * current value of any of the effective, real, or saved uid. 909 * 910 * If the newly requested real uid or effective uid does not 911 * match the saved uid, then set the saved uid to the new 912 * effective uid (potentially unrecoverably dropping saved 913 * privilege). 914 * 915 * If the credential is changed as a result of this call, then we 916 * flag the process as having set privilege since the last exec. 917 */ 918int 919setreuid(proc_t p, struct setreuid_args *uap, __unused register_t *retval) 920{ 921 uid_t ruid, euid; 922 int error; 923 kauth_cred_t my_cred, my_new_cred; 924 925 DEBUG_CRED_ENTER("setreuid %d %d\n", uap->ruid, uap->euid); 926 927 ruid = uap->ruid; 928 euid = uap->euid; 929 if (ruid == (uid_t)-1) 930 ruid = KAUTH_UID_NONE; 931 if (euid == (uid_t)-1) 932 euid = KAUTH_UID_NONE; 933 AUDIT_ARG(uid, euid, ruid, 0, 0); 934 935 my_cred = kauth_cred_proc_ref(p); 936 937 if (((ruid != KAUTH_UID_NONE && /* allow no change of ruid */ 938 ruid != my_cred->cr_ruid && /* allow ruid = ruid */ 939 ruid != my_cred->cr_uid && /* allow ruid = euid */ 940 ruid != my_cred->cr_svuid) || /* allow ruid = svuid */ 941 (euid != KAUTH_UID_NONE && /* allow no change of euid */ 942 euid != my_cred->cr_uid && /* allow euid = euid */ 943 euid != my_cred->cr_ruid && /* allow euid = ruid */ 944 euid != my_cred->cr_svuid)) && /* allow euid = svui */ 945 (error = suser(my_cred, &p->p_acflag))) { /* allow root user any */ 946 kauth_cred_unref(&my_cred); 947 return (error); 948 } 949 950 /* 951 * Everything's okay, do it. Copy credentials so other references do 952 * not see our changes. get current credential and take a reference 953 * while we muck with it 954 */ 955 for (;;) { 956 uid_t new_euid; 957 uid_t new_ruid; 958 uid_t svuid = KAUTH_UID_NONE; 959 960 new_euid = my_cred->cr_uid; 961 new_ruid = my_cred->cr_ruid; 962 963 /* 964 * Set the credential with new info. If there is no change, 965 * we get back the same credential we passed in; if there is 966 * a change, we drop the reference on the credential we 967 * passed in. The subsequent compare is safe, because it is 968 * a pointer compare rather than a contents compare. 969 */ 970 if (euid == KAUTH_UID_NONE && my_cred->cr_uid != euid) { 971 /* changing the effective UID */ 972 new_euid = euid; 973 OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag); 974 } 975 if (ruid != KAUTH_UID_NONE && my_cred->cr_ruid != ruid) { 976 /* changing the real UID; must do user accounting */ 977 /* chgproccnt uses list lock for protection */ 978 (void)chgproccnt(ruid, 1); 979 (void)chgproccnt(my_cred->cr_ruid, -1); 980 new_ruid = ruid; 981 OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag); 982 } 983 /* 984 * If the newly requested real uid or effective uid does 985 * not match the saved uid, then set the saved uid to the 986 * new effective uid. We are protected from escalation 987 * by the prechecking. 988 */ 989 if (my_cred->cr_svuid != uap->ruid && 990 my_cred->cr_svuid != uap->euid) { 991 svuid = new_euid; 992 OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag); 993 } 994 995 my_new_cred = kauth_cred_setresuid(my_cred, ruid, euid, svuid, my_cred->cr_gmuid); 996 997 if (my_cred != my_new_cred) { 998 999 DEBUG_CRED_CHANGE("setreuid CH(%d): %p/0x%08x -> %p/0x%08x\n", p->p_pid, my_cred, my_cred->cr_flags, my_new_cred, my_new_cred->cr_flags); 1000 1001 proc_lock(p); 1002 /* 1003 * We need to protect for a race where another thread 1004 * also changed the credential after we took our 1005 * reference. If p_ucred has changed then we should 1006 * restart this again with the new cred. 1007 */ 1008 if (p->p_ucred != my_cred) { 1009 proc_unlock(p); 1010 kauth_cred_unref(&my_new_cred); 1011 my_cred = kauth_cred_proc_ref(p); 1012 /* try again */ 1013 continue; 1014 } 1015 p->p_ucred = my_new_cred; 1016 OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag); /* XXX redundant? */ 1017 proc_unlock(p); 1018 } 1019 break; 1020 } 1021 /* drop old proc reference or our extra reference */ 1022 kauth_cred_unref(&my_cred); 1023 1024 set_security_token(p); 1025 return (0); 1026} 1027 1028 1029/* 1030 * setgid 1031 * 1032 * Description: Set group ID system call 1033 * 1034 * Parameters: uap->gid gid to set 1035 * 1036 * Returns: 0 Success 1037 * suser:EPERM Permission denied 1038 * 1039 * Notes: If called by a privileged process, this function will set the 1040 * real, effective, and saved gid to the requested value. 1041 * 1042 * If called from an unprivileged process, but gid is equal to the 1043 * real or saved gid, then the effective gid will be set to the 1044 * requested value, but the real and saved gid will not change. 1045 * 1046 * If the credential is changed as a result of this call, then we 1047 * flag the process as having set privilege since the last exec. 1048 * 1049 * As an implementation detail, the effective gid is stored as 1050 * the first element of the supplementary group list, and 1051 * therefore the effective group list may be reordered to keep 1052 * the supplementary group list unchanged. 1053 */ 1054int 1055setgid(proc_t p, struct setgid_args *uap, __unused register_t *retval) 1056{ 1057 gid_t gid; 1058 gid_t rgid = KAUTH_GID_NONE; 1059 gid_t svgid = KAUTH_GID_NONE; 1060 int error; 1061 kauth_cred_t my_cred, my_new_cred; 1062 1063 DEBUG_CRED_ENTER("setgid(%d/%d): %d\n", p->p_pid, (p->p_pptr ? p->p_pptr->p_pid : 0), uap->gid); 1064 1065 gid = uap->gid; 1066 AUDIT_ARG(gid, gid, 0, 0, 0); 1067 1068 my_cred = kauth_cred_proc_ref(p); 1069 1070 if (gid != my_cred->cr_rgid && /* allow setgid(getgid()) */ 1071 gid != my_cred->cr_svgid && /* allow setgid(saved gid) */ 1072 (error = suser(my_cred, &p->p_acflag))) { 1073 kauth_cred_unref(&my_cred); 1074 return (error); 1075 } 1076 1077 /* 1078 * If we are priviledged, then set the saved and real GID too; 1079 * otherwise, just set the effective GID 1080 */ 1081 if (suser(my_cred, &p->p_acflag) == 0) { 1082 svgid = gid; 1083 rgid = gid; 1084 } 1085 1086 /* get current credential and take a reference while we muck with it */ 1087 for (;;) { 1088 1089 /* 1090 * Set the credential with new info. If there is no change, 1091 * we get back the same credential we passed in; if there is 1092 * a change, we drop the reference on the credential we 1093 * passed in. The subsequent compare is safe, because it is 1094 * a pointer compare rather than a contents compare. 1095 */ 1096 my_new_cred = kauth_cred_setresgid(my_cred, rgid, gid, svgid); 1097 if (my_cred != my_new_cred) { 1098 1099 DEBUG_CRED_CHANGE("setgid(CH)%d: %p/0x%08x->%p/0x%08x\n", p->p_pid, my_cred, my_cred->cr_flags, my_new_cred, my_new_cred->cr_flags); 1100 1101 proc_lock(p); 1102 /* 1103 * We need to protect for a race where another thread 1104 * also changed the credential after we took our 1105 * reference. If p_ucred has changed then we 1106 * should restart this again with the new cred. 1107 */ 1108 if (p->p_ucred != my_cred) { 1109 proc_unlock(p); 1110 kauth_cred_unref(&my_new_cred); 1111 /* try again */ 1112 my_cred = kauth_cred_proc_ref(p); 1113 continue; 1114 } 1115 p->p_ucred = my_new_cred; 1116 OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag); 1117 proc_unlock(p); 1118 } 1119 break; 1120 } 1121 /* Drop old proc reference or our extra reference */ 1122 kauth_cred_unref(&my_cred); 1123 1124 set_security_token(p); 1125 return (0); 1126} 1127 1128 1129/* 1130 * setegid 1131 * 1132 * Description: Set effective group ID system call 1133 * 1134 * Parameters: uap->egid effective gid to set 1135 * 1136 * Returns: 0 Success 1137 * suser:EPERM 1138 * 1139 * Notes: If called by a privileged process, or called from an 1140 * unprivileged process but egid is equal to the real or saved 1141 * gid, then the effective gid will be set to the requested 1142 * value, but the real and saved gid will not change. 1143 * 1144 * If the credential is changed as a result of this call, then we 1145 * flag the process as having set privilege since the last exec. 1146 * 1147 * As an implementation detail, the effective gid is stored as 1148 * the first element of the supplementary group list, and 1149 * therefore the effective group list may be reordered to keep 1150 * the supplementary group list unchanged. 1151 */ 1152int 1153setegid(proc_t p, struct setegid_args *uap, __unused register_t *retval) 1154{ 1155 gid_t egid; 1156 int error; 1157 kauth_cred_t my_cred, my_new_cred; 1158 1159 DEBUG_CRED_ENTER("setegid %d\n", uap->egid); 1160 1161 egid = uap->egid; 1162 AUDIT_ARG(gid, 0, egid, 0, 0); 1163 1164 my_cred = kauth_cred_proc_ref(p); 1165 1166 if (egid != my_cred->cr_rgid && 1167 egid != my_cred->cr_svgid && 1168 (error = suser(my_cred, &p->p_acflag))) { 1169 kauth_cred_unref(&my_cred); 1170 return (error); 1171 } 1172 1173 /* get current credential and take a reference while we muck with it */ 1174 for (;;) { 1175 /* 1176 * Set the credential with new info. If there is no change, 1177 * we get back the same credential we passed in; if there is 1178 * a change, we drop the reference on the credential we 1179 * passed in. The subsequent compare is safe, because it is 1180 * a pointer compare rather than a contents compare. 1181 */ 1182 my_new_cred = kauth_cred_setresgid(my_cred, KAUTH_GID_NONE, egid, KAUTH_GID_NONE); 1183 if (my_cred != my_new_cred) { 1184 1185 DEBUG_CRED_CHANGE("setegid(CH)%d: %p/0x%08x->%p/0x%08x\n", p->p_pid, my_cred, my_cred->cr_flags, my_new_cred, my_new_cred->cr_flags); 1186 1187 proc_lock(p); 1188 /* 1189 * We need to protect for a race where another thread 1190 * also changed the credential after we took our 1191 * reference. If p_ucred has changed then we 1192 * should restart this again with the new cred. 1193 */ 1194 if (p->p_ucred != my_cred) { 1195 proc_unlock(p); 1196 kauth_cred_unref(&my_new_cred); 1197 /* try again */ 1198 my_cred = kauth_cred_proc_ref(p); 1199 continue; 1200 } 1201 p->p_ucred = my_new_cred; 1202 OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag); 1203 proc_unlock(p); 1204 } 1205 break; 1206 } 1207 1208 /* Drop old proc reference or our extra reference */ 1209 kauth_cred_unref(&my_cred); 1210 1211 set_security_token(p); 1212 return (0); 1213} 1214 1215/* 1216 * setregid 1217 * 1218 * Description: Set real and effective group ID system call 1219 * 1220 * Parameters: uap->rgid real gid to set 1221 * uap->egid effective gid to set 1222 * 1223 * Returns: 0 Success 1224 * suser:EPERM Permission denied 1225 * 1226 * Notes: A value of -1 is a special case indicating that the gid for 1227 * which that value is specified not be changed. If both values 1228 * are specified as -1, no action is taken. 1229 * 1230 * If called by a privileged process, the real and effective gid 1231 * will be set to the new value(s) specified. 1232 * 1233 * If called from an unprivileged process, the real gid may be 1234 * set to the current value of the real gid, or to the current 1235 * value of the saved gid. The effective gid may be set to the 1236 * current value of any of the effective, real, or saved gid. 1237 * 1238 * If the new real and effective gid will not be equal, or the 1239 * new real or effective gid is not the same as the saved gid, 1240 * then the saved gid will be updated to reflect the new 1241 * effective gid (potentially unrecoverably dropping saved 1242 * privilege). 1243 * 1244 * If the credential is changed as a result of this call, then we 1245 * flag the process as having set privilege since the last exec. 1246 * 1247 * As an implementation detail, the effective gid is stored as 1248 * the first element of the supplementary group list, and 1249 * therefore the effective group list may be reordered to keep 1250 * the supplementary group list unchanged. 1251 */ 1252int 1253setregid(proc_t p, struct setregid_args *uap, __unused register_t *retval) 1254{ 1255 gid_t rgid, egid; 1256 int error; 1257 kauth_cred_t my_cred, my_new_cred; 1258 1259 DEBUG_CRED_ENTER("setregid %d %d\n", uap->rgid, uap->egid); 1260 1261 rgid = uap->rgid; 1262 egid = uap->egid; 1263 1264 if (rgid == (uid_t)-1) 1265 rgid = KAUTH_GID_NONE; 1266 if (egid == (uid_t)-1) 1267 egid = KAUTH_GID_NONE; 1268 AUDIT_ARG(gid, egid, rgid, 0, 0); 1269 1270 my_cred = kauth_cred_proc_ref(p); 1271 1272 if (((rgid != KAUTH_UID_NONE && /* allow no change of rgid */ 1273 rgid != my_cred->cr_rgid && /* allow rgid = rgid */ 1274 rgid != my_cred->cr_gid && /* allow rgid = egid */ 1275 rgid != my_cred->cr_svgid) || /* allow rgid = svgid */ 1276 (egid != KAUTH_UID_NONE && /* allow no change of egid */ 1277 egid != my_cred->cr_groups[0] && /* allow no change of egid */ 1278 egid != my_cred->cr_gid && /* allow egid = egid */ 1279 egid != my_cred->cr_rgid && /* allow egid = rgid */ 1280 egid != my_cred->cr_svgid)) && /* allow egid = svgid */ 1281 (error = suser(my_cred, &p->p_acflag))) { /* allow root user any */ 1282 kauth_cred_unref(&my_cred); 1283 return (error); 1284 } 1285 1286 /* get current credential and take a reference while we muck with it */ 1287 for (;;) { 1288 uid_t new_egid = my_cred->cr_gid; 1289 uid_t new_rgid = my_cred->cr_rgid; 1290 uid_t svgid = KAUTH_UID_NONE; 1291 1292 1293 /* 1294 * Set the credential with new info. If there is no change, 1295 * we get back the same credential we passed in; if there is 1296 * a change, we drop the reference on the credential we 1297 * passed in. The subsequent compare is safe, because it is 1298 * a pointer compare rather than a contents compare. 1299 */ 1300 if (egid == KAUTH_UID_NONE && my_cred->cr_groups[0] != egid) { 1301 /* changing the effective GID */ 1302 new_egid = egid; 1303 OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag); 1304 } 1305 if (rgid != KAUTH_UID_NONE && my_cred->cr_rgid != rgid) { 1306 /* changing the real GID */ 1307 new_rgid = rgid; 1308 OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag); 1309 } 1310 /* 1311 * If the newly requested real gid or effective gid does 1312 * not match the saved gid, then set the saved gid to the 1313 * new effective gid. We are protected from escalation 1314 * by the prechecking. 1315 */ 1316 if (my_cred->cr_svgid != uap->rgid && 1317 my_cred->cr_svgid != uap->egid) { 1318 svgid = new_egid; 1319 OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag); 1320 } 1321 1322 my_new_cred = kauth_cred_setresgid(my_cred, rgid, egid, svgid); 1323 if (my_cred != my_new_cred) { 1324 1325 DEBUG_CRED_CHANGE("setregid(CH)%d: %p/0x%08x->%p/0x%08x\n", p->p_pid, my_cred, my_cred->cr_flags, my_new_cred, my_new_cred->cr_flags); 1326 1327 proc_lock(p); 1328 /* need to protect for a race where another thread 1329 * also changed the credential after we took our 1330 * reference. If p_ucred has changed then we 1331 * should restart this again with the new cred. 1332 */ 1333 if (p->p_ucred != my_cred) { 1334 proc_unlock(p); 1335 kauth_cred_unref(&my_new_cred); 1336 /* try again */ 1337 my_cred = kauth_cred_proc_ref(p); 1338 continue; 1339 } 1340 p->p_ucred = my_new_cred; 1341 OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag); /* XXX redundant? */ 1342 proc_unlock(p); 1343 } 1344 break; 1345 } 1346 /* Drop old proc reference or our extra reference */ 1347 kauth_cred_unref(&my_cred); 1348 1349 set_security_token(p); 1350 return (0); 1351} 1352 1353 1354/* 1355 * Set the per-thread override identity. The first parameter can be the 1356 * current real UID, KAUTH_UID_NONE, or, if the caller is priviledged, it 1357 * can be any UID. If it is KAUTH_UID_NONE, then as a special case, this 1358 * means "revert to the per process credential"; otherwise, if permitted, 1359 * it changes the effective, real, and saved UIDs and GIDs for the current 1360 * thread to the requested UID and single GID, and clears all other GIDs. 1361 */ 1362int 1363settid(proc_t p, struct settid_args *uap, __unused register_t *retval) 1364{ 1365 kauth_cred_t uc; 1366 struct uthread *uthread = get_bsdthread_info(current_thread()); 1367 uid_t uid; 1368 gid_t gid; 1369 1370 uid = uap->uid; 1371 gid = uap->gid; 1372 AUDIT_ARG(uid, uid, gid, gid, 0); 1373 1374 if (proc_suser(p) != 0) 1375 return (EPERM); 1376 1377 if (uid == KAUTH_UID_NONE) { 1378 1379 /* must already be assuming another identity in order to revert back */ 1380 if ((uthread->uu_flag & UT_SETUID) == 0) 1381 return (EPERM); 1382 1383 /* revert to delayed binding of process credential */ 1384 uc = kauth_cred_proc_ref(p); 1385 kauth_cred_unref(&uthread->uu_ucred); 1386 uthread->uu_ucred = uc; 1387 uthread->uu_flag &= ~UT_SETUID; 1388 } else { 1389 kauth_cred_t my_cred, my_new_cred; 1390 1391 /* cannot already be assuming another identity */ 1392 if ((uthread->uu_flag & UT_SETUID) != 0) { 1393 return (EPERM); 1394 } 1395 1396 /* 1397 * Get a new credential instance from the old if this one 1398 * changes; otherwise kauth_cred_setuidgid() returns the 1399 * same credential. We take an extra reference on the 1400 * current credential while we muck with it, so we can do 1401 * the post-compare for changes by pointer. 1402 */ 1403 kauth_cred_ref(uthread->uu_ucred); 1404 my_cred = uthread->uu_ucred; 1405 my_new_cred = kauth_cred_setuidgid(my_cred, uid, gid); 1406 if (my_cred != my_new_cred) 1407 uthread->uu_ucred = my_new_cred; 1408 uthread->uu_flag |= UT_SETUID; 1409 1410 /* Drop old uthread reference or our extra reference */ 1411 kauth_cred_unref(&my_cred); 1412 } 1413 /* 1414 * XXX should potentially set per thread security token (there is 1415 * XXX none). 1416 * XXX it is unclear whether P_SUGID should be st at this point; 1417 * XXX in theory, it is being deprecated. 1418 */ 1419 return (0); 1420} 1421 1422 1423/* 1424 * Set the per-thread override identity. Use this system call for a thread to 1425 * assume the identity of another process or to revert back to normal identity 1426 * of the current process. 1427 * 1428 * When the "assume" argument is non zero the current thread will assume the 1429 * identity of the process represented by the pid argument. 1430 * 1431 * When the assume argument is zero we revert back to our normal identity. 1432 */ 1433int 1434settid_with_pid(proc_t p, struct settid_with_pid_args *uap, __unused register_t *retval) 1435{ 1436 proc_t target_proc; 1437 struct uthread *uthread = get_bsdthread_info(current_thread()); 1438 kauth_cred_t my_cred, my_target_cred, my_new_cred; 1439 1440 AUDIT_ARG(pid, uap->pid); 1441 AUDIT_ARG(value, uap->assume); 1442 1443 if (proc_suser(p) != 0) { 1444 return (EPERM); 1445 } 1446 1447 /* 1448 * XXX should potentially set per thread security token (there is 1449 * XXX none). 1450 * XXX it is unclear whether P_SUGID should be st at this point; 1451 * XXX in theory, it is being deprecated. 1452 */ 1453 1454 /* 1455 * assume argument tells us to assume the identity of the process with the 1456 * id passed in the pid argument. 1457 */ 1458 if (uap->assume != 0) { 1459 /* can't do this if we have already assumed an identity */ 1460 if ((uthread->uu_flag & UT_SETUID) != 0) 1461 return (EPERM); 1462 1463 target_proc = proc_find(uap->pid); 1464 /* can't assume the identity of the kernel process */ 1465 if (target_proc == NULL || target_proc == kernproc) { 1466 if (target_proc!= NULL) 1467 proc_rele(target_proc); 1468 return (ESRCH); 1469 } 1470 1471 /* 1472 * Take a reference on the credential used in our target 1473 * process then use it as the identity for our current 1474 * thread. We take an extra reference on the current 1475 * credential while we muck with it, so we can do the 1476 * post-compare for changes by pointer. 1477 * 1478 * The post-compare is needed for the case that our process 1479 * credential has been changed to be identical to our thread 1480 * credential following our assumption of a per-thread one, 1481 * since the credential cache will maintain a unique instance. 1482 */ 1483 kauth_cred_ref(uthread->uu_ucred); 1484 my_cred = uthread->uu_ucred; 1485 my_target_cred = kauth_cred_proc_ref(target_proc); 1486 my_new_cred = kauth_cred_setuidgid(my_cred, my_target_cred->cr_uid, my_target_cred->cr_gid); 1487 if (my_cred != my_new_cred) 1488 uthread->uu_ucred = my_new_cred; 1489 1490 uthread->uu_flag |= UT_SETUID; 1491 1492 /* Drop old uthread reference or our extra reference */ 1493 proc_rele(target_proc); 1494 kauth_cred_unref(&my_cred); 1495 kauth_cred_unref(&my_target_cred); 1496 1497 return (0); 1498 } 1499 1500 /* 1501 * Otherwise, we are reverting back to normal mode of operation where 1502 * delayed binding of the process credential sets the credential in 1503 * the thread (uu_ucred) 1504 */ 1505 if ((uthread->uu_flag & UT_SETUID) == 0) 1506 return (EPERM); 1507 1508 /* revert to delayed binding of process credential */ 1509 my_new_cred = kauth_cred_proc_ref(p); 1510 kauth_cred_unref(&uthread->uu_ucred); 1511 uthread->uu_ucred = my_new_cred; 1512 uthread->uu_flag &= ~UT_SETUID; 1513 1514 return (0); 1515} 1516 1517 1518/* 1519 * setgroups1 1520 * 1521 * Description: Internal implementation for both the setgroups and initgroups 1522 * system calls 1523 * 1524 * Parameters: gidsetsize Number of groups in set 1525 * gidset Pointer to group list 1526 * gmuid Base gid (initgroups only!) 1527 * 1528 * Returns: 0 Success 1529 * suser:EPERM Permision denied 1530 * EINVAL Invalid gidsetsize value 1531 * copyin:EFAULT Bad gidset or gidsetsize is 1532 * too large 1533 * 1534 * Notes: When called from a thread running under an assumed per-thread 1535 * identity, this function will operate against the per-thread 1536 * credential, rather than against the process credential. In 1537 * this specific case, the process credential is verified to 1538 * still be privileged at the time of the call, rather than the 1539 * per-thread credential for this operation to be permitted. 1540 * 1541 * This effectively means that setgroups/initigroups calls in 1542 * a thread running a per-thread credential should occur *after* 1543 * the settid call that created it, not before (unlike setuid, 1544 * which must be called after, since it will result in privilege 1545 * being dropped). 1546 * 1547 * When called normally (i.e. no per-thread assumed identity), 1548 * the per process credential is updated per POSIX. 1549 * 1550 * If the credential is changed as a result of this call, then we 1551 * flag the process as having set privilege since the last exec. 1552 */ 1553static int 1554setgroups1(proc_t p, u_int gidsetsize, user_addr_t gidset, uid_t gmuid, __unused register_t *retval) 1555{ 1556 u_int ngrp; 1557 gid_t newgroups[NGROUPS] = { 0 }; 1558 int error; 1559 kauth_cred_t my_cred, my_new_cred; 1560 struct uthread *uthread = get_bsdthread_info(current_thread()); 1561 1562 DEBUG_CRED_ENTER("setgroups1 (%d/%d): %d 0x%016x %d\n", p->p_pid, (p->p_pptr ? p->p_pptr->p_pid : 0), gidsetsize, gidset, gmuid); 1563 1564 ngrp = gidsetsize; 1565 if (ngrp > NGROUPS) 1566 return (EINVAL); 1567 1568 if ( ngrp < 1 ) { 1569 ngrp = 1; 1570 } else { 1571 error = copyin(gidset, 1572 (caddr_t)newgroups, ngrp * sizeof(gid_t)); 1573 if (error) { 1574 return (error); 1575 } 1576 } 1577 1578 my_cred = kauth_cred_proc_ref(p); 1579 if ((error = suser(my_cred, &p->p_acflag))) { 1580 kauth_cred_unref(&my_cred); 1581 return (error); 1582 } 1583 1584 if ((uthread->uu_flag & UT_SETUID) != 0) { 1585#if DEBUG_CRED 1586 int my_cred_flags = uthread->uu_ucred->cr_flags; 1587#endif /* DEBUG_CRED */ 1588 kauth_cred_unref(&my_cred); 1589 1590 /* 1591 * If this thread is under an assumed identity, set the 1592 * supplementary grouplist on the thread credential instead 1593 * of the process one. If we were the only reference holder, 1594 * the credential is updated in place, otherwise, our reference 1595 * is dropped and we get back a different cred with a reference 1596 * already held on it. Because this is per-thread, we don't 1597 * need the referencing/locking/retry required for per-process. 1598 */ 1599 my_cred = uthread->uu_ucred; 1600 uthread->uu_ucred = kauth_cred_setgroups(my_cred, &newgroups[0], ngrp, gmuid); 1601#if DEBUG_CRED 1602 if (my_cred != uthread->uu_ucred) { 1603 DEBUG_CRED_CHANGE("setgroups1(CH)%d: %p/0x%08x->%p/0x%08x\n", p->p_pid, my_cred, my_cred_flags, uthread->uu_ucred , uthread->uu_ucred ->cr_flags); 1604 } 1605#endif /* DEBUG_CRED */ 1606 } else { 1607 1608 /* 1609 * get current credential and take a reference while we muck 1610 * with it 1611 */ 1612 for (;;) { 1613 /* 1614 * Set the credential with new info. If there is no 1615 * change, we get back the same credential we passed 1616 * in; if there is a change, we drop the reference on 1617 * the credential we passed in. The subsequent 1618 * compare is safe, because it is a pointer compare 1619 * rather than a contents compare. 1620 */ 1621 my_new_cred = kauth_cred_setgroups(my_cred, &newgroups[0], ngrp, gmuid); 1622 if (my_cred != my_new_cred) { 1623 1624 DEBUG_CRED_CHANGE("setgroups1(CH)%d: %p/0x%08x->%p/0x%08x\n", p->p_pid, my_cred, my_cred->cr_flags, my_new_cred, my_new_cred->cr_flags); 1625 1626 proc_lock(p); 1627 /* 1628 * We need to protect for a race where another 1629 * thread also changed the credential after we 1630 * took our reference. If p_ucred has 1631 * changed then we should restart this again 1632 * with the new cred. 1633 */ 1634 if (p->p_ucred != my_cred) { 1635 proc_unlock(p); 1636 kauth_cred_unref(&my_new_cred); 1637 my_cred = kauth_cred_proc_ref(p); 1638 /* try again */ 1639 continue; 1640 } 1641 p->p_ucred = my_new_cred; 1642 OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag); 1643 proc_unlock(p); 1644 } 1645 break; 1646 } 1647 /* Drop old proc reference or our extra reference */ 1648 AUDIT_ARG(groupset, my_cred->cr_groups, ngrp); 1649 kauth_cred_unref(&my_cred); 1650 1651 1652 set_security_token(p); 1653 } 1654 1655 return (0); 1656} 1657 1658 1659/* 1660 * initgroups 1661 * 1662 * Description: Initialize the default supplementary groups list and set the 1663 * gmuid for use by the external group resolver (if any) 1664 * 1665 * Parameters: uap->gidsetsize Number of groups in set 1666 * uap->gidset Pointer to group list 1667 * uap->gmuid Base gid 1668 * 1669 * Returns: 0 Success 1670 * setgroups1:EPERM Permision denied 1671 * setgroups1:EINVAL Invalid gidsetsize value 1672 * setgroups1:EFAULT Bad gidset or gidsetsize is 1673 * 1674 * Notes: This function opts *IN* to memberd participation 1675 * 1676 * The normal purpose of this function is for a privileged 1677 * process to indicate supplementary groups and identity for 1678 * participation in extended group membership resolution prior 1679 * to dropping privilege by assuming a specific user identity. 1680 * 1681 * It is the first half of the primary mechanism whereby user 1682 * identity is established to the system by programs such as 1683 * /usr/bin/login. The second half is the drop of uid privilege 1684 * for a specific uid corresponding to the user. 1685 * 1686 * See also: setgroups1() 1687 */ 1688int 1689initgroups(proc_t p, struct initgroups_args *uap, __unused register_t *retval) 1690{ 1691 DEBUG_CRED_ENTER("initgroups\n"); 1692 1693 return(setgroups1(p, uap->gidsetsize, uap->gidset, uap->gmuid, retval)); 1694} 1695 1696 1697/* 1698 * setgroups 1699 * 1700 * Description: Initialize the default supplementary groups list 1701 * 1702 * Parameters: gidsetsize Number of groups in set 1703 * gidset Pointer to group list 1704 * 1705 * Returns: 0 Success 1706 * setgroups1:EPERM Permision denied 1707 * setgroups1:EINVAL Invalid gidsetsize value 1708 * setgroups1:EFAULT Bad gidset or gidsetsize is 1709 * 1710 * Notes: This functions opts *OUT* of memberd participation. 1711 * 1712 * This function exists for compatibility with POSIX. Most user 1713 * programs should use initgroups() instead to ensure correct 1714 * participation in group membership resolution when utilizing 1715 * a directory service for authentication. 1716 * 1717 * It is identical to an initgroups() call with a gmuid argument 1718 * of KAUTH_UID_NONE. 1719 * 1720 * See also: setgroups1() 1721 */ 1722int 1723setgroups(proc_t p, struct setgroups_args *uap, __unused register_t *retval) 1724{ 1725 DEBUG_CRED_ENTER("setgroups\n"); 1726 1727 return(setgroups1(p, uap->gidsetsize, uap->gidset, KAUTH_UID_NONE, retval)); 1728} 1729 1730 1731/* 1732 * Set the per-thread/per-process supplementary groups list. 1733 */ 1734#warning XXX implement setsgroups 1735int 1736setsgroups(__unused proc_t p, __unused struct setsgroups_args *uap, __unused register_t *retval) 1737{ 1738 return(ENOTSUP); 1739} 1740 1741/* 1742 * Set the per-thread/per-process whiteout groups list. 1743 */ 1744#warning XXX implement setwgroups 1745int 1746setwgroups(__unused proc_t p, __unused struct setwgroups_args *uap, __unused register_t *retval) 1747{ 1748 return(ENOTSUP); 1749} 1750 1751 1752/* 1753 * Check if gid is a member of the group set. 1754 * 1755 * XXX This interface is going away; use kauth_cred_ismember_gid() directly 1756 * XXX instead. 1757 */ 1758int 1759groupmember(gid_t gid, kauth_cred_t cred) 1760{ 1761 int is_member; 1762 1763 if (kauth_cred_ismember_gid(cred, gid, &is_member) == 0 && is_member) 1764 return (1); 1765 return (0); 1766} 1767 1768 1769/* 1770 * Test whether the specified credentials imply "super-user" 1771 * privilege; if so, and we have accounting info, set the flag 1772 * indicating use of super-powers. 1773 * Returns 0 or error. 1774 * 1775 * XXX This interface is going away; use kauth_cred_issuser() directly 1776 * XXX instead. 1777 * 1778 * Note: This interface exists to implement the "has used privilege" 1779 * bit (ASU) in the p_acflags field of the process, which is 1780 * only externalized via private sysctl and in process accounting 1781 * records. The flag is technically not required in either case. 1782 */ 1783int 1784suser(kauth_cred_t cred, u_short *acflag) 1785{ 1786#if DIAGNOSTIC 1787 if (!IS_VALID_CRED(cred)) 1788 panic("suser"); 1789#endif 1790 if (kauth_cred_getuid(cred) == 0) { 1791 if (acflag) 1792 *acflag |= ASU; 1793 return (0); 1794 } 1795 return (EPERM); 1796} 1797 1798 1799/* 1800 * XXX This interface is going away; use kauth_cred_issuser() directly 1801 * XXX instead. 1802 */ 1803int 1804is_suser(void) 1805{ 1806 proc_t p = current_proc(); 1807 1808 if (!p) 1809 return (0); 1810 1811 return (proc_suser(p) == 0); 1812} 1813 1814 1815/* 1816 * XXX This interface is going away; use kauth_cred_issuser() directly 1817 * XXX instead. 1818 */ 1819int 1820is_suser1(void) 1821{ 1822 proc_t p = current_proc(); 1823 kauth_cred_t my_cred; 1824 int err; 1825 1826 if (!p) 1827 return (0); 1828 1829 my_cred = kauth_cred_proc_ref(p); 1830 1831 err = (suser(my_cred, &p->p_acflag) == 0 || 1832 my_cred->cr_ruid == 0 || my_cred->cr_svuid == 0); 1833 kauth_cred_unref(&my_cred); 1834 return(err); 1835} 1836 1837 1838/* 1839 * getlogin 1840 * 1841 * Description: Get login name, if available. 1842 * 1843 * Parameters: uap->namebuf User buffer for return 1844 * uap->namelen User buffer length 1845 * 1846 * Returns: 0 Success 1847 * copyout:EFAULT 1848 * 1849 * Notes: Intended to obtain a string containing the user name of the 1850 * user associated with the controlling terminal for the calling 1851 * process. 1852 * 1853 * Not very useful on modern systems, due to inherent length 1854 * limitations for the static array in the session structure 1855 * which is used to store the login name. 1856 * 1857 * Permitted to return NULL 1858 * 1859 * XXX: Belongs in kern_proc.c 1860 */ 1861int 1862getlogin(proc_t p, struct getlogin_args *uap, __unused register_t *retval) 1863{ 1864 char buffer[MAXLOGNAME+1]; 1865 struct session * sessp; 1866 1867 bzero(buffer, MAXLOGNAME+1); 1868 1869 sessp = proc_session(p); 1870 1871 if (uap->namelen > MAXLOGNAME) 1872 uap->namelen = MAXLOGNAME; 1873 1874 if(sessp != SESSION_NULL) { 1875 session_lock(sessp); 1876 bcopy( sessp->s_login, buffer, uap->namelen); 1877 session_unlock(sessp); 1878 } 1879 session_rele(sessp); 1880 1881 return (copyout((caddr_t)buffer, uap->namebuf, uap->namelen)); 1882} 1883 1884 1885/* 1886 * setlogin 1887 * 1888 * Description: Set login name. 1889 * 1890 * Parameters: uap->namebuf User buffer containing name 1891 * 1892 * Returns: 0 Success 1893 * suser:EPERM Permission denied 1894 * copyinstr:EFAULT User buffer invalid 1895 * copyinstr:EINVAL Supplied name was too long 1896 * 1897 * Notes: This is a utility system call to support getlogin(). 1898 * 1899 * XXX: Belongs in kern_proc.c 1900 */ 1901int 1902setlogin(proc_t p, struct setlogin_args *uap, __unused register_t *retval) 1903{ 1904 int error; 1905 int dummy=0; 1906 char buffer[MAXLOGNAME+1]; 1907 struct session * sessp; 1908 1909 if ((error = proc_suser(p))) 1910 return (error); 1911 1912 bzero(&buffer[0], MAXLOGNAME+1); 1913 1914 1915 error = copyinstr(uap->namebuf, 1916 (caddr_t) &buffer[0], 1917 MAXLOGNAME - 1, (size_t *)&dummy); 1918 1919 sessp = proc_session(p); 1920 1921 if (sessp != SESSION_NULL) { 1922 session_lock(sessp); 1923 bcopy(buffer, sessp->s_login, MAXLOGNAME); 1924 session_unlock(sessp); 1925 session_rele(sessp); 1926 } 1927 1928 1929 if (!error) { 1930 AUDIT_ARG(text, buffer); 1931 } else if (error == ENAMETOOLONG) 1932 error = EINVAL; 1933 return (error); 1934} 1935 1936 1937/* Set the secrity token of the task with current euid and eguid */ 1938/* 1939 * XXX This needs to change to give the task a reference and/or an opaque 1940 * XXX identifier. 1941 */ 1942int 1943set_security_token(proc_t p) 1944{ 1945 security_token_t sec_token; 1946 audit_token_t audit_token; 1947 kauth_cred_t my_cred; 1948 host_priv_t host_priv; 1949 1950 /* 1951 * Don't allow a vfork child to override the parent's token settings 1952 * (since they share a task). Instead, the child will just have to 1953 * suffer along using the parent's token until the exec(). It's all 1954 * undefined behavior anyway, right? 1955 */ 1956 if (p->task == current_task()) { 1957 uthread_t uthread; 1958 uthread = (uthread_t)get_bsdthread_info(current_thread()); 1959 if (uthread->uu_flag & UT_VFORK) 1960 return (1); 1961 } 1962 1963 my_cred = kauth_cred_proc_ref(p); 1964 /* XXX mach_init doesn't have a p_ucred when it calls this function */ 1965 if (IS_VALID_CRED(my_cred)) { 1966 sec_token.val[0] = kauth_cred_getuid(my_cred); 1967 sec_token.val[1] = my_cred->cr_gid; 1968 } else { 1969 sec_token.val[0] = 0; 1970 sec_token.val[1] = 0; 1971 } 1972 1973 /* 1974 * The current layout of the Mach audit token explicitly 1975 * adds these fields. But nobody should rely on such 1976 * a literal representation. Instead, the BSM library 1977 * provides a function to convert an audit token into 1978 * a BSM subject. Use of that mechanism will isolate 1979 * the user of the trailer from future representation 1980 * changes. 1981 */ 1982 audit_token.val[0] = my_cred->cr_au.ai_auid; 1983 audit_token.val[1] = my_cred->cr_uid; 1984 audit_token.val[2] = my_cred->cr_gid; 1985 audit_token.val[3] = my_cred->cr_ruid; 1986 audit_token.val[4] = my_cred->cr_rgid; 1987 audit_token.val[5] = p->p_pid; 1988 audit_token.val[6] = my_cred->cr_au.ai_asid; 1989 audit_token.val[7] = p->p_idversion; 1990 1991#if CONFIG_MACF_MACH 1992 mac_task_label_update_cred(my_cred, p->task); 1993#endif 1994 1995 host_priv = (sec_token.val[0]) ? HOST_PRIV_NULL : host_priv_self(); 1996#if CONFIG_MACF 1997 if (host_priv != HOST_PRIV_NULL && mac_system_check_host_priv(my_cred)) 1998 host_priv = HOST_PRIV_NULL; 1999#endif 2000 kauth_cred_unref(&my_cred); 2001 2002 return (host_security_set_task_token(host_security_self(), 2003 p->task, 2004 sec_token, 2005 audit_token, 2006 host_priv) != KERN_SUCCESS); 2007} 2008 2009 2010/* 2011 * Fill in a struct xucred based on a kauth_cred_t. 2012 */ 2013__private_extern__ 2014void 2015cru2x(kauth_cred_t cr, struct xucred *xcr) 2016{ 2017 2018 bzero(xcr, sizeof(*xcr)); 2019 xcr->cr_version = XUCRED_VERSION; 2020 xcr->cr_uid = kauth_cred_getuid(cr); 2021 xcr->cr_ngroups = cr->cr_ngroups; 2022 bcopy(cr->cr_groups, xcr->cr_groups, sizeof(xcr->cr_groups)); 2023} 2024 2025#if CONFIG_LCTX 2026 2027/* 2028 * Set Login Context ID 2029 */ 2030/* 2031 * MPSAFE - assignment of (visible) process to context protected by ALLLCTX_LOCK, 2032 * LCTX by its own locks. 2033 */ 2034int 2035setlcid(proc_t p0, struct setlcid_args *uap, __unused register_t *retval) 2036{ 2037 proc_t p; 2038 struct lctx *l; 2039 int error = 0; 2040 int refheld = 0; 2041 2042 AUDIT_ARG(pid, uap->pid); 2043 AUDIT_ARG(value, uap->lcid); 2044 if (uap->pid == LCID_PROC_SELF) { /* Create/Join/Leave */ 2045 p = p0; 2046 } else { /* Adopt/Orphan */ 2047 p = proc_find(uap->pid); 2048 if (p == NULL) 2049 return (ESRCH); 2050 refheld = 1; 2051 } 2052 2053#if CONFIG_MACF 2054 error = mac_proc_check_setlcid(p0, p, uap->pid, uap->lcid); 2055 if (error) 2056 goto out; 2057#endif 2058 2059 switch (uap->lcid) { 2060 /* Leave/Orphan */ 2061 case LCID_REMOVE: 2062 2063 /* Only root may Leave/Orphan. */ 2064 if (!is_suser1()) { 2065 error = EPERM; 2066 goto out; 2067 } 2068 2069 /* Process not in login context. */ 2070 if (p->p_lctx == NULL) { 2071 error = ENOATTR; 2072 goto out; 2073 } 2074 2075 l = NULL; 2076 2077 break; 2078 2079 /* Create */ 2080 case LCID_CREATE: 2081 2082 /* Create only valid for self! */ 2083 if (uap->pid != LCID_PROC_SELF) { 2084 error = EPERM; 2085 goto out; 2086 } 2087 2088 /* Already in a login context. */ 2089 if (p->p_lctx != NULL) { 2090 error = EPERM; 2091 goto out; 2092 } 2093 2094 l = lccreate(); 2095 if (l == NULL) { 2096 error = ENOMEM; 2097 goto out; 2098 } 2099 2100 LCTX_LOCK(l); 2101 2102 break; 2103 2104 /* Join/Adopt */ 2105 default: 2106 2107 /* Only root may Join/Adopt. */ 2108 if (!is_suser1()) { 2109 error = EPERM; 2110 goto out; 2111 } 2112 2113 l = lcfind(uap->lcid); 2114 if (l == NULL) { 2115 error = ENOATTR; 2116 goto out; 2117 } 2118 2119 break; 2120 } 2121 2122 ALLLCTX_LOCK; 2123 leavelctx(p); 2124 enterlctx(p, l, (uap->lcid == LCID_CREATE) ? 1 : 0); 2125 ALLLCTX_UNLOCK; 2126 2127out: 2128 if (refheld != 0) 2129 proc_rele(p); 2130 return (error); 2131} 2132 2133/* 2134 * Get Login Context ID 2135 */ 2136/* 2137 * MPSAFE - membership of (visible) process in a login context 2138 * protected by the all-context lock. 2139 */ 2140int 2141getlcid(proc_t p0, struct getlcid_args *uap, register_t *retval) 2142{ 2143 proc_t p; 2144 int error = 0; 2145 int refheld = 0; 2146 2147 AUDIT_ARG(pid, uap->pid); 2148 if (uap->pid == LCID_PROC_SELF) { 2149 p = p0; 2150 } else { 2151 p = proc_find(uap->pid); 2152 if (p == NULL) 2153 return (ESRCH); 2154 refheld = 1; 2155 } 2156 2157#if CONFIG_MACF 2158 error = mac_proc_check_getlcid(p0, p, uap->pid); 2159 if (error) 2160 goto out; 2161#endif 2162 ALLLCTX_LOCK; 2163 if (p->p_lctx == NULL) { 2164 error = ENOATTR; 2165 ALLLCTX_UNLOCK; 2166 goto out; 2167 } 2168 *retval = p->p_lctx->lc_id; 2169 ALLLCTX_UNLOCK; 2170 out: 2171 if (refheld != 0) 2172 proc_rele(p); 2173 2174 return (error); 2175} 2176#else /* LCTX */ 2177int 2178setlcid(proc_t p0, struct setlcid_args *uap, register_t *retval) 2179{ 2180 2181 return (ENOSYS); 2182} 2183 2184int 2185getlcid(proc_t p0, struct getlcid_args *uap, register_t *retval) 2186{ 2187 2188 return (ENOSYS); 2189} 2190#endif /* !LCTX */ 2191