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>
36 static int _si_muser_disabled
= 0;
37 static xpc_pipe_t __muser_pipe
;
38 static pthread_mutex_t mutex
= PTHREAD_MUTEX_INITIALIZER
;
41 static xpc_pipe_t
_muser_xpc_pipe(bool reset
);
45 _muser_call(const char *procname
, xpc_object_t payload
)
47 xpc_object_t result
= NULL
;
51 if (!payload
) { return NULL
; }
53 pipe
= _muser_xpc_pipe(false);
54 if (!pipe
) { return NULL
; }
56 xpc_dictionary_set_string(payload
, kLIMMessageRPCName
, procname
);
57 xpc_dictionary_set_uint64(payload
, kLIMMessageVersion
, 1);
59 int rv
= xpc_pipe_routine(pipe
, payload
, &reply
);
75 _muser_xpc_pipe_disabled(xpc_pipe_t pipe
)
77 xpc_object_t dict
, reply
= NULL
;
80 if (pipe
== NULL
) { return 1; }
82 dict
= xpc_dictionary_create(NULL
, NULL
, 0);
83 xpc_dictionary_set_string(dict
, kLIMMessageReqtype
, kLIMMessageReplyAvailable
);
84 xpc_dictionary_set_int64(dict
, kLIMMessageVersion
, 1);
86 int rv
= xpc_pipe_routine(pipe
, dict
, &reply
);
89 disabled
= !xpc_dictionary_get_bool(reply
, kLIMMessageReplyAvailable
);
105 _muser_xpc_pipe(bool reset
)
107 static dispatch_once_t once
;
108 xpc_pipe_t pipe
= NULL
;
110 dispatch_once(&once
, ^{
113 uint32_t multiuser_flags
= 0;
115 /* mirrors the ds_module build environment override */
116 xbs_disable
= getenv("XBS_DISABLE_LIBINFO");
117 if ((issetugid() == 0) && (xbs_disable
!= NULL
) && (strcmp(xbs_disable
, "YES") == 0)) {
118 _si_muser_disabled
= 1;
122 /* check the xnu commpage bit to see if we're multiuser */
123 rv
= host_get_multiuser_config_flags(mach_host_self(), &multiuser_flags
);
124 if (rv
== KERN_SUCCESS
&& (multiuser_flags
& kIsMultiUserDevice
) == 0) {
125 _si_muser_disabled
= 1;
130 if (_si_muser_disabled
== 1) {
134 pthread_mutex_lock(&mutex
);
136 xpc_release(__muser_pipe
);
140 if (__muser_pipe
== NULL
) {
141 __muser_pipe
= xpc_pipe_create(kLibinfoMultiuserPortName
, 0);
142 if (!_si_muser_disabled
) { _si_muser_disabled
= _muser_xpc_pipe_disabled(__muser_pipe
); }
145 if (__muser_pipe
) { pipe
= xpc_retain(__muser_pipe
); }
146 pthread_mutex_unlock(&mutex
);
152 _muser_available(void)
156 pipe
= _muser_xpc_pipe(false);
161 if (_si_muser_disabled
) {
165 return (pipe
!= NULL
);
169 _muser_extract_user(si_mod_t
*si
, xpc_object_t reply
, uint64_t valid_global
, uint64_t valid_cat
) {
170 __block
struct passwd p
;
171 __block
int reqs
= 3;
172 p
.pw_name
= (char *)"";
173 p
.pw_passwd
= (char *)"*";
176 p
.pw_change
= (time_t)0;
177 p
.pw_expire
= (time_t)0;
178 p
.pw_class
= (char *)"";
179 p
.pw_gecos
= (char *)"";
180 p
.pw_dir
= (char *)"/var/empty";
181 p
.pw_shell
= (char *)"/usr/bin/false";
183 xpc_dictionary_apply(reply
, ^bool(const char *key
, xpc_object_t value
) {
184 if (key
== NULL
) { return true; }
185 else if (!strcmp(key
, kLIMMessageReplyName
)) {
186 const char *nm
= xpc_string_get_string_ptr(value
);
188 p
.pw_name
= (char *)nm
;
192 else if (!strcmp(key
, kLIMMessageReplyPasswd
)) {
193 const char *pw
= xpc_string_get_string_ptr(value
);
194 if (pw
) { p
.pw_passwd
= (char *)pw
; }
196 else if (!strcmp(key
, kLIMMessageReplyUID
)) {
197 if (xpc_get_type(value
) == XPC_TYPE_INT64
) {
198 int64_t uid
= xpc_int64_get_value(value
);
203 else if (!strcmp(key
, kLIMMessageReplyGID
)) {
204 if (xpc_get_type(value
) == XPC_TYPE_INT64
) {
205 p
.pw_gid
= xpc_int64_get_value(value
);
209 else if (!strcmp(key
, kLIMMessageReplyHome
)) {
210 const char *home
= xpc_string_get_string_ptr(value
);
211 if (home
) { p
.pw_dir
= (char *)home
; };
213 else if (!strcmp(key
, kLIMMessageReplyShell
)) {
214 const char *shell
= xpc_string_get_string_ptr(value
);
215 if (shell
) { p
.pw_shell
= (char *)shell
; }
220 if (reqs
!= 0) { return NULL
; }
222 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
);
226 _muser_extract_group(si_mod_t
*si
, xpc_object_t reply
, uint64_t vg
, uint64_t vc
)
228 __block
struct group g
;
229 __block
int reqs
= 2;
231 g
.gr_name
= (char *)"";
232 g
.gr_passwd
= (char *)"*";
235 xpc_dictionary_apply(reply
, ^bool(const char *key
, xpc_object_t value
) {
236 if (key
== NULL
) { return true; }
237 else if (!strcmp(key
, kLIMMessageReplyGroupname
)) {
238 const char *nm
= xpc_string_get_string_ptr(value
);
240 g
.gr_name
= (char *)nm
;
244 else if (!strcmp(key
, kLIMMessageReplyGroupID
)) {
245 if (xpc_get_type(value
) == XPC_TYPE_INT64
) {
246 g
.gr_gid
= xpc_int64_get_value(value
);
250 else if (!strcmp(key
, kLIMMessageReplyGroupMembers
)) {
251 if (xpc_get_type(value
) == XPC_TYPE_ARRAY
) {
252 size_t count
= xpc_array_get_count(value
);
253 g
.gr_mem
= (char **)malloc(sizeof(char *) * (count
+ 1));
254 g
.gr_mem
[count
] = NULL
;
256 for (size_t i
=0; i
<count
; i
++) {
257 g
.gr_mem
[i
] = (char *)xpc_array_get_string(value
, i
);
264 if (reqs
!= 0) { return NULL
; }
266 si_item_t
*item
= (si_item_t
*)LI_ils_create("L4488ss4*", (unsigned long)si
, CATEGORY_GROUP
, 1,
267 vg
, vc
, g
.gr_name
, g
.gr_passwd
, g
.gr_gid
, g
.gr_mem
);
274 _muser_extract_grouplist(si_mod_t
*si
, xpc_object_t reply
, const char *user
,
275 uint64_t vg
, uint64_t vc
)
277 gid_t __block
*gidlist
= NULL
;
279 si_item_t
*item
= NULL
;
281 if (user
== NULL
) { return NULL
; }
283 xpc_object_t list
= xpc_dictionary_get_value(reply
, kLIMMessageReplyGrouplist
);
285 if (xpc_get_type(list
) == XPC_TYPE_ARRAY
) {
286 gidcount
= xpc_array_get_count(list
);
287 gidlist
= (gid_t
*)calloc(gidcount
+ 1, sizeof(gid_t
));
289 xpc_array_apply(list
, ^bool(size_t index
, xpc_object_t value
) {
290 if (xpc_get_type(value
) == XPC_TYPE_INT64
) {
291 gidlist
[index
] = xpc_int64_get_value(value
);
303 item
= (si_item_t
*)LI_ils_create("L4488s4@", (unsigned long)si
, CATEGORY_GROUPLIST
,
304 1, vg
, vc
, user
, gidcount
, gidcount
* sizeof(gid_t
), gidlist
);
314 muser_user_byname(si_mod_t
*si
, const char *name
)
316 xpc_object_t payload
;
317 si_item_t
*item
= NULL
;
319 if (!_muser_available()) { return NULL
; }
321 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
322 if (!payload
) { return NULL
; }
324 xpc_dictionary_set_string(payload
, kLIMMessageReqtype
, kLIMMessageRequestUsername
);
325 xpc_dictionary_set_string(payload
, kLIMMessageQuery
, name
);
327 xpc_object_t reply
= _muser_call("getpwnam", payload
);
329 item
= _muser_extract_user(si
, reply
, 0, 0);
333 xpc_release(payload
);
338 muser_user_byuid(si_mod_t
*si
, uid_t uid
)
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
, kLIMMessageRequestUID
);
349 xpc_dictionary_set_int64(payload
, kLIMMessageQuery
, uid
);
351 xpc_object_t reply
= _muser_call("getpwuid", payload
);
353 item
= _muser_extract_user(si
, reply
, 0, 0);
357 xpc_release(payload
);
362 muser_group_byname(struct si_mod_s
*si
, const char *name
)
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
, kLIMMessageRequestGroupname
);
373 xpc_dictionary_set_string(payload
, kLIMMessageQuery
, name
);
375 xpc_object_t reply
= _muser_call("getgrnam", payload
);
377 item
= _muser_extract_group(si
, reply
, 0, 0);
381 xpc_release(payload
);
386 muser_group_bygid(struct si_mod_s
*si
, gid_t gid
)
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
, kLIMMessageRequestGID
);
397 xpc_dictionary_set_int64(payload
, kLIMMessageQuery
, gid
);
399 xpc_object_t reply
= _muser_call("getgrgid", payload
);
401 item
= _muser_extract_group(si
, reply
, 0, 0);
405 xpc_release(payload
);
410 muser_grouplist(struct si_mod_s
*si
, const char *name
, uint32_t count
)
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
, kLIMMessageRequestGrouplist
);
421 xpc_dictionary_set_string(payload
, kLIMMessageQuery
, name
);
423 xpc_object_t reply
= _muser_call("getgrouplist", payload
);
425 item
= _muser_extract_grouplist(si
, reply
, name
, 0, 0);
429 xpc_release(payload
);
436 si_module_static_muser(void)
438 static const struct si_mod_vtable_s muser_vtable
=
440 .sim_user_byname
= &muser_user_byname
,
441 .sim_user_byuid
= &muser_user_byuid
,
443 .sim_group_byname
= &muser_group_byname
,
444 .sim_group_bygid
= &muser_group_bygid
,
445 .sim_grouplist
= &muser_grouplist
,
452 .flags
= SI_MOD_FLAG_STATIC
,
455 .vtable
= &muser_vtable
,
458 static dispatch_once_t once
;
459 dispatch_once(&once
, ^{
460 si
.name
= strdup("muser");
466 #endif // MUSER_AVAILABLE