2 * Copyright (c) 2015 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This ds 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 ds except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #ifdef MUSER_AVAILABLE
26 #include <dispatch/dispatch.h>
27 #include <libinfo_muser.h>
28 #include <mach/mach_host.h>
29 #include <machine/cpu_capabilities.h>
31 #include <si_module.h>
34 #include <xpc/private.h>
37 static int _si_muser_disabled
= 0;
38 static xpc_pipe_t __muser_pipe
;
39 static pthread_mutex_t mutex
= PTHREAD_MUTEX_INITIALIZER
;
42 static xpc_pipe_t
_muser_xpc_pipe(bool reset
);
46 _muser_call(const char *procname
, xpc_object_t payload
)
48 xpc_object_t result
= NULL
;
52 bool retried
= false; /* try again incase of usermanager jetsam */
53 bool retryagain
= false;
54 if (!payload
) { return NULL
; }
56 pipe
= _muser_xpc_pipe(false);
57 if (!pipe
) { return NULL
; }
59 xpc_dictionary_set_string(payload
, kLIMMessageRPCName
, procname
);
60 xpc_dictionary_set_uint64(payload
, kLIMMessageVersion
, 1);
62 /* if EPIPE, EAGAIN retry once */
64 retryagain
= false; /* if set from previous pass, reset now */
65 rv
= xpc_pipe_routine(pipe
, payload
, &reply
);
74 /* retried already, so bail out */
75 os_log_error(OS_LOG_DEFAULT
, "_muser_call: Failure (%d) with retry, bailing", rv
);
78 /* lets retry one more time as jetsam of usermanager results in EPIPE */
81 os_log_debug(OS_LOG_DEFAULT
, "_muser_call: Error from xpc pipe (%d), retrying", rv
);
82 /* reestablish connetion */
84 /* reestablish muser pipe */
85 pipe
= _muser_xpc_pipe(true);
86 if (!pipe
) { return NULL
; }
99 _muser_xpc_pipe_disabled(xpc_pipe_t pipe
)
101 xpc_object_t dict
, reply
= NULL
;
104 if (pipe
== NULL
) { return 1; }
106 dict
= xpc_dictionary_create(NULL
, NULL
, 0);
107 xpc_dictionary_set_string(dict
, kLIMMessageReqtype
, kLIMMessageReplyAvailable
);
108 xpc_dictionary_set_int64(dict
, kLIMMessageVersion
, 1);
110 int rv
= xpc_pipe_routine(pipe
, dict
, &reply
);
113 disabled
= !xpc_dictionary_get_bool(reply
, kLIMMessageReplyAvailable
);
129 _muser_xpc_pipe(bool reset
)
131 static dispatch_once_t once
;
132 xpc_pipe_t pipe
= NULL
;
134 dispatch_once(&once
, ^{
137 uint32_t multiuser_flags
= 0;
139 /* mirrors the ds_module build environment override */
140 xbs_disable
= getenv("XBS_DISABLE_LIBINFO");
141 if ((issetugid() == 0) && (xbs_disable
!= NULL
) && (strcmp(xbs_disable
, "YES") == 0)) {
142 _si_muser_disabled
= 1;
146 /* check the xnu commpage bit to see if we're multiuser */
147 rv
= host_get_multiuser_config_flags(mach_host_self(), &multiuser_flags
);
148 if (rv
== KERN_SUCCESS
&& (multiuser_flags
& kIsMultiUserDevice
) == 0) {
149 _si_muser_disabled
= 1;
154 if (_si_muser_disabled
== 1) {
158 pthread_mutex_lock(&mutex
);
160 xpc_release(__muser_pipe
);
164 if (__muser_pipe
== NULL
) {
165 __muser_pipe
= xpc_pipe_create(kLibinfoMultiuserPortName
, XPC_PIPE_USE_SYNC_IPC_OVERRIDE
| XPC_PIPE_PROPAGATE_QOS
);
166 if (!_si_muser_disabled
) { _si_muser_disabled
= _muser_xpc_pipe_disabled(__muser_pipe
); }
169 if (__muser_pipe
) { pipe
= xpc_retain(__muser_pipe
); }
170 pthread_mutex_unlock(&mutex
);
176 _muser_available(void)
180 pipe
= _muser_xpc_pipe(false);
185 if (_si_muser_disabled
) {
189 return (pipe
!= NULL
);
193 _muser_extract_user(si_mod_t
*si
, xpc_object_t reply
, uint64_t valid_global
, uint64_t valid_cat
) {
194 __block
struct passwd p
;
195 __block
int reqs
= 3;
196 p
.pw_name
= (char *)"";
197 p
.pw_passwd
= (char *)"*";
200 p
.pw_change
= (time_t)0;
201 p
.pw_expire
= (time_t)0;
202 p
.pw_class
= (char *)"";
203 p
.pw_gecos
= (char *)"";
204 p
.pw_dir
= (char *)"/var/empty";
205 p
.pw_shell
= (char *)"/usr/bin/false";
207 xpc_dictionary_apply(reply
, ^bool(const char *key
, xpc_object_t value
) {
208 if (key
== NULL
) { return true; }
209 else if (!strcmp(key
, kLIMMessageReplyName
)) {
210 const char *nm
= xpc_string_get_string_ptr(value
);
212 p
.pw_name
= (char *)nm
;
216 else if (!strcmp(key
, kLIMMessageReplyPasswd
)) {
217 const char *pw
= xpc_string_get_string_ptr(value
);
218 if (pw
) { p
.pw_passwd
= (char *)pw
; }
220 else if (!strcmp(key
, kLIMMessageReplyUID
)) {
221 if (xpc_get_type(value
) == XPC_TYPE_INT64
) {
222 int64_t uid
= xpc_int64_get_value(value
);
227 else if (!strcmp(key
, kLIMMessageReplyGID
)) {
228 if (xpc_get_type(value
) == XPC_TYPE_INT64
) {
229 p
.pw_gid
= xpc_int64_get_value(value
);
233 else if (!strcmp(key
, kLIMMessageReplyHome
)) {
234 const char *home
= xpc_string_get_string_ptr(value
);
235 if (home
) { p
.pw_dir
= (char *)home
; };
237 else if (!strcmp(key
, kLIMMessageReplyShell
)) {
238 const char *shell
= xpc_string_get_string_ptr(value
);
239 if (shell
) { p
.pw_shell
= (char *)shell
; }
244 if (reqs
!= 0) { return NULL
; }
246 return (si_item_t
*)LI_ils_create("L4488ss44LssssL", (unsigned long)si
, CATEGORY_USER
, 1, valid_global
, valid_cat
, p
.pw_name
, p
.pw_passwd
, p
.pw_uid
, p
.pw_gid
, p
.pw_change
, p
.pw_class
, p
.pw_gecos
, p
.pw_dir
, p
.pw_shell
, p
.pw_expire
);
250 _muser_extract_group(si_mod_t
*si
, xpc_object_t reply
, uint64_t vg
, uint64_t vc
)
252 __block
struct group g
;
253 __block
int reqs
= 2;
255 g
.gr_name
= (char *)"";
256 g
.gr_passwd
= (char *)"*";
259 xpc_dictionary_apply(reply
, ^bool(const char *key
, xpc_object_t value
) {
260 if (key
== NULL
) { return true; }
261 else if (!strcmp(key
, kLIMMessageReplyGroupname
)) {
262 const char *nm
= xpc_string_get_string_ptr(value
);
264 g
.gr_name
= (char *)nm
;
268 else if (!strcmp(key
, kLIMMessageReplyGroupID
)) {
269 if (xpc_get_type(value
) == XPC_TYPE_INT64
) {
270 g
.gr_gid
= xpc_int64_get_value(value
);
274 else if (!strcmp(key
, kLIMMessageReplyGroupMembers
)) {
275 if (xpc_get_type(value
) == XPC_TYPE_ARRAY
) {
276 size_t count
= xpc_array_get_count(value
);
277 g
.gr_mem
= (char **)malloc(sizeof(char *) * (count
+ 1));
278 g
.gr_mem
[count
] = NULL
;
280 for (size_t i
=0; i
<count
; i
++) {
281 g
.gr_mem
[i
] = (char *)xpc_array_get_string(value
, i
);
288 if (reqs
!= 0) { return NULL
; }
290 si_item_t
*item
= (si_item_t
*)LI_ils_create("L4488ss4*", (unsigned long)si
, CATEGORY_GROUP
, 1,
291 vg
, vc
, g
.gr_name
, g
.gr_passwd
, g
.gr_gid
, g
.gr_mem
);
298 _muser_extract_grouplist(si_mod_t
*si
, xpc_object_t reply
, const char *user
,
299 uint64_t vg
, uint64_t vc
)
301 gid_t __block
*gidlist
= NULL
;
303 si_item_t
*item
= NULL
;
305 if (user
== NULL
) { return NULL
; }
307 xpc_object_t list
= xpc_dictionary_get_value(reply
, kLIMMessageReplyGrouplist
);
309 if (xpc_get_type(list
) == XPC_TYPE_ARRAY
) {
310 gidcount
= xpc_array_get_count(list
);
311 gidlist
= (gid_t
*)calloc(gidcount
+ 1, sizeof(gid_t
));
313 xpc_array_apply(list
, ^bool(size_t index
, xpc_object_t value
) {
314 if (xpc_get_type(value
) == XPC_TYPE_INT64
) {
315 gidlist
[index
] = xpc_int64_get_value(value
);
327 item
= (si_item_t
*)LI_ils_create("L4488s4@", (unsigned long)si
, CATEGORY_GROUPLIST
,
328 1, vg
, vc
, user
, gidcount
, gidcount
* sizeof(gid_t
), gidlist
);
338 muser_user_byname(si_mod_t
*si
, const char *name
)
340 xpc_object_t payload
;
341 si_item_t
*item
= NULL
;
343 if (!_muser_available()) { return NULL
; }
345 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
346 if (!payload
) { return NULL
; }
348 xpc_dictionary_set_string(payload
, kLIMMessageReqtype
, kLIMMessageRequestUsername
);
349 xpc_dictionary_set_string(payload
, kLIMMessageQuery
, name
);
351 xpc_object_t reply
= _muser_call("getpwnam", payload
);
353 item
= _muser_extract_user(si
, reply
, 0, 0);
357 xpc_release(payload
);
362 muser_user_byuid(si_mod_t
*si
, uid_t uid
)
364 xpc_object_t payload
;
365 si_item_t
*item
= NULL
;
367 if (!_muser_available()) { return NULL
; }
369 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
370 if (!payload
) { return NULL
; }
372 xpc_dictionary_set_string(payload
, kLIMMessageReqtype
, kLIMMessageRequestUID
);
373 xpc_dictionary_set_int64(payload
, kLIMMessageQuery
, uid
);
375 xpc_object_t reply
= _muser_call("getpwuid", payload
);
377 item
= _muser_extract_user(si
, reply
, 0, 0);
381 xpc_release(payload
);
386 muser_group_byname(struct si_mod_s
*si
, const char *name
)
388 xpc_object_t payload
;
389 si_item_t
*item
= NULL
;
391 if (!_muser_available()) { return NULL
; }
393 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
394 if (!payload
) { return NULL
; }
396 xpc_dictionary_set_string(payload
, kLIMMessageReqtype
, kLIMMessageRequestGroupname
);
397 xpc_dictionary_set_string(payload
, kLIMMessageQuery
, name
);
399 xpc_object_t reply
= _muser_call("getgrnam", payload
);
401 item
= _muser_extract_group(si
, reply
, 0, 0);
405 xpc_release(payload
);
410 muser_group_bygid(struct si_mod_s
*si
, gid_t gid
)
412 xpc_object_t payload
;
413 si_item_t
*item
= NULL
;
415 if (!_muser_available()) { return NULL
; }
417 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
418 if (!payload
) { return NULL
; }
420 xpc_dictionary_set_string(payload
, kLIMMessageReqtype
, kLIMMessageRequestGID
);
421 xpc_dictionary_set_int64(payload
, kLIMMessageQuery
, gid
);
423 xpc_object_t reply
= _muser_call("getgrgid", payload
);
425 item
= _muser_extract_group(si
, reply
, 0, 0);
429 xpc_release(payload
);
434 muser_grouplist(struct si_mod_s
*si
, const char *name
, uint32_t count
)
436 xpc_object_t payload
;
437 si_item_t
*item
= NULL
;
439 if (!_muser_available()) { return NULL
; }
441 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
442 if (!payload
) { return NULL
; }
444 xpc_dictionary_set_string(payload
, kLIMMessageReqtype
, kLIMMessageRequestGrouplist
);
445 xpc_dictionary_set_string(payload
, kLIMMessageQuery
, name
);
447 xpc_object_t reply
= _muser_call("getgrouplist", payload
);
449 item
= _muser_extract_grouplist(si
, reply
, name
, 0, 0);
453 xpc_release(payload
);
460 si_module_static_muser(void)
462 static const struct si_mod_vtable_s muser_vtable
=
464 .sim_user_byname
= &muser_user_byname
,
465 .sim_user_byuid
= &muser_user_byuid
,
467 .sim_group_byname
= &muser_group_byname
,
468 .sim_group_bygid
= &muser_group_bygid
,
469 .sim_grouplist
= &muser_grouplist
,
476 .flags
= SI_MOD_FLAG_STATIC
,
479 .vtable
= &muser_vtable
,
482 static dispatch_once_t once
;
483 dispatch_once(&once
, ^{
484 si
.name
= strdup("muser");
490 #endif // MUSER_AVAILABLE