]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_prot.c
xnu-1228.12.14.tar.gz
[apple/xnu.git] / bsd / kern / kern_prot.c
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
119 int 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
134 extern 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 */
154 int
155 setprivexec(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 */
175 int
176 getpid(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 */
195 int
196 getppid(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 */
215 int
216 getpgrp(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 */
240 int
241 getpgid(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;
253 found:
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 */
277 int
278 getsid(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;
291 found:
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 */
311 int
312 getuid(__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 */
329 int
330 geteuid(__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 */
349 int
350 gettid(__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 */
381 int
382 getgid(__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 */
405 int
406 getegid(__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 */
444 int
445 getgroups(__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
480 int
481 getsgroups(__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
491 int
492 getwgroups(__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 */
523 int
524 setsid(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 */
578 int
579 setpgid(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);
641 out:
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 */
668 int
669 issetugid(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 */
705 int
706 setuid(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 */
820 int
821 seteuid(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 */
918 int
919 setreuid(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 */
1054 int
1055 setgid(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 */
1152 int
1153 setegid(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 */
1252 int
1253 setregid(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 */
1362 int
1363 settid(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 */
1433 int
1434 settid_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 */
1553 static int
1554 setgroups1(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 */
1688 int
1689 initgroups(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 */
1722 int
1723 setgroups(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
1735 int
1736 setsgroups(__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
1745 int
1746 setwgroups(__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 */
1758 int
1759 groupmember(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 */
1783 int
1784 suser(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 */
1803 int
1804 is_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 */
1819 int
1820 is_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 */
1861 int
1862 getlogin(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 */
1901 int
1902 setlogin(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 */
1942 int
1943 set_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__
2014 void
2015 cru2x(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 */
2034 int
2035 setlcid(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
2127 out:
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 */
2140 int
2141 getlcid(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 */
2177 int
2178 setlcid(proc_t p0, struct setlcid_args *uap, register_t *retval)
2179 {
2180
2181 return (ENOSYS);
2182 }
2183
2184 int
2185 getlcid(proc_t p0, struct getlcid_args *uap, register_t *retval)
2186 {
2187
2188 return (ENOSYS);
2189 }
2190 #endif /* !LCTX */