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
);
84 _muser_xpc_pipe_disabled(xpc_pipe_t pipe
)
86 xpc_object_t dict
, reply
= NULL
;
89 if (pipe
== NULL
) { return 1; }
91 dict
= xpc_dictionary_create(NULL
, NULL
, 0);
92 xpc_dictionary_set_string(dict
, kLIMMessageReqtype
, kLIMMessageReplyAvailable
);
93 xpc_dictionary_set_int64(dict
, kLIMMessageVersion
, 1);
95 int rv
= xpc_pipe_routine(pipe
, dict
, &reply
);
98 disabled
= !xpc_dictionary_get_bool(reply
, kLIMMessageReplyAvailable
);
112 _muser_xpc_pipe(bool reset
)
114 static dispatch_once_t once
;
115 xpc_pipe_t pipe
= NULL
;
117 dispatch_once(&once
, ^{
120 uint32_t multiuser_flags
= 0;
122 /* mirrors the ds_module build environment override */
123 xbs_disable
= getenv("XBS_DISABLE_LIBINFO");
124 if ((issetugid() == 0) && (xbs_disable
!= NULL
) && (strcmp(xbs_disable
, "YES") == 0)) {
125 _si_muser_disabled
= 1;
129 /* check the xnu commpage bit to see if we're multiuser */
130 rv
= host_get_multiuser_config_flags(mach_host_self(), &multiuser_flags
);
131 if (rv
== KERN_SUCCESS
&& (multiuser_flags
& kIsMultiUserDevice
) == 0) {
132 _si_muser_disabled
= 1;
137 if (_si_muser_disabled
== 1) {
141 pthread_mutex_lock(&mutex
);
143 xpc_release(__muser_pipe
);
147 if (__muser_pipe
== NULL
) {
148 __muser_pipe
= xpc_pipe_create(kLibinfoMultiuserPortName
, 0);
149 if (!_si_muser_disabled
) { _si_muser_disabled
= _muser_xpc_pipe_disabled(__muser_pipe
); }
152 if (__muser_pipe
) { pipe
= xpc_retain(__muser_pipe
); }
153 pthread_mutex_unlock(&mutex
);
159 _muser_available(void)
163 pipe
= _muser_xpc_pipe(false);
168 if (_si_muser_disabled
) {
172 return (pipe
!= NULL
);
176 _muser_extract_user(si_mod_t
*si
, xpc_object_t reply
, uint64_t valid_global
, uint64_t valid_cat
) {
177 __block
struct passwd p
;
178 __block
int reqs
= 3;
179 p
.pw_name
= (char *)"";
180 p
.pw_passwd
= (char *)"*";
183 p
.pw_change
= (time_t)0;
184 p
.pw_expire
= (time_t)0;
185 p
.pw_class
= (char *)"";
186 p
.pw_gecos
= (char *)"";
187 p
.pw_dir
= (char *)"/var/empty";
188 p
.pw_shell
= (char *)"/usr/bin/false";
190 xpc_dictionary_apply(reply
, ^bool(const char *key
, xpc_object_t value
) {
191 if (key
== NULL
) { return true; }
192 else if (!strcmp(key
, kLIMMessageReplyName
)) {
193 const char *nm
= xpc_string_get_string_ptr(value
);
195 p
.pw_name
= (char *)nm
;
199 else if (!strcmp(key
, kLIMMessageReplyPasswd
)) {
200 const char *pw
= xpc_string_get_string_ptr(value
);
201 if (pw
) { p
.pw_passwd
= (char *)pw
; }
203 else if (!strcmp(key
, kLIMMessageReplyUID
)) {
204 if (xpc_get_type(value
) == XPC_TYPE_INT64
) {
205 int64_t uid
= xpc_int64_get_value(value
);
210 else if (!strcmp(key
, kLIMMessageReplyGID
)) {
211 if (xpc_get_type(value
) == XPC_TYPE_INT64
) {
212 p
.pw_gid
= xpc_int64_get_value(value
);
216 else if (!strcmp(key
, kLIMMessageReplyHome
)) {
217 const char *home
= xpc_string_get_string_ptr(value
);
218 if (home
) { p
.pw_dir
= (char *)home
; };
220 else if (!strcmp(key
, kLIMMessageReplyShell
)) {
221 const char *shell
= xpc_string_get_string_ptr(value
);
222 if (shell
) { p
.pw_shell
= (char *)shell
; }
227 if (reqs
!= 0) { return NULL
; }
229 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
);
233 _muser_extract_group(si_mod_t
*si
, xpc_object_t reply
, uint64_t vg
, uint64_t vc
)
235 __block
struct group g
;
236 __block
int reqs
= 2;
238 g
.gr_name
= (char *)"";
239 g
.gr_passwd
= (char *)"*";
242 xpc_dictionary_apply(reply
, ^bool(const char *key
, xpc_object_t value
) {
243 if (key
== NULL
) { return true; }
244 else if (!strcmp(key
, kLIMMessageReplyGroupname
)) {
245 const char *nm
= xpc_string_get_string_ptr(value
);
247 g
.gr_name
= (char *)nm
;
251 else if (!strcmp(key
, kLIMMessageReplyGroupID
)) {
252 if (xpc_get_type(value
) == XPC_TYPE_INT64
) {
253 g
.gr_gid
= xpc_int64_get_value(value
);
260 if (reqs
!= 0) { return NULL
; }
262 return (si_item_t
*)LI_ils_create("L4488ss4*", (unsigned long)si
, CATEGORY_GROUP
, 1,
263 vg
, vc
, g
.gr_name
, g
.gr_passwd
, g
.gr_gid
, g
.gr_mem
);
267 _muser_extract_grouplist(si_mod_t
*si
, xpc_object_t reply
, const char *user
,
268 uint64_t vg
, uint64_t vc
)
270 gid_t __block
*gidlist
= NULL
;
272 si_item_t
*item
= NULL
;
274 if (user
== NULL
) { return NULL
; }
276 xpc_object_t list
= xpc_dictionary_get_value(reply
, kLIMMessageReplyGrouplist
);
278 if (xpc_get_type(list
) == XPC_TYPE_ARRAY
) {
279 gidcount
= xpc_array_get_count(list
);
280 gidlist
= (gid_t
*)calloc(gidcount
+ 1, sizeof(gid_t
));
282 xpc_array_apply(list
, ^bool(size_t index
, xpc_object_t value
) {
283 if (xpc_get_type(value
) == XPC_TYPE_INT64
) {
284 gidlist
[index
] = xpc_int64_get_value(value
);
296 item
= (si_item_t
*)LI_ils_create("L4488s4@", (unsigned long)si
, CATEGORY_GROUPLIST
,
297 1, vg
, vc
, user
, gidcount
, gidcount
* sizeof(gid_t
), gidlist
);
307 muser_user_byname(si_mod_t
*si
, const char *name
)
309 xpc_object_t payload
;
310 si_item_t
*item
= NULL
;
312 if (!_muser_available()) { return NULL
; }
314 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
315 if (!payload
) { return NULL
; }
317 xpc_dictionary_set_string(payload
, kLIMMessageReqtype
, kLIMMessageRequestUsername
);
318 xpc_dictionary_set_string(payload
, kLIMMessageQuery
, name
);
320 xpc_object_t reply
= _muser_call("getpwnam", payload
);
322 item
= _muser_extract_user(si
, reply
, 0, 0);
326 xpc_release(payload
);
331 muser_user_byuid(si_mod_t
*si
, uid_t uid
)
333 xpc_object_t payload
;
334 si_item_t
*item
= NULL
;
336 if (!_muser_available()) { return NULL
; }
338 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
339 if (!payload
) { return NULL
; }
341 xpc_dictionary_set_string(payload
, kLIMMessageReqtype
, kLIMMessageRequestUID
);
342 xpc_dictionary_set_int64(payload
, kLIMMessageQuery
, uid
);
344 xpc_object_t reply
= _muser_call("getpwuid", payload
);
346 item
= _muser_extract_user(si
, reply
, 0, 0);
350 xpc_release(payload
);
355 muser_group_byname(struct si_mod_s
*si
, const char *name
)
357 xpc_object_t payload
;
358 si_item_t
*item
= NULL
;
360 if (!_muser_available()) { return NULL
; }
362 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
363 if (!payload
) { return NULL
; }
365 xpc_dictionary_set_string(payload
, kLIMMessageReqtype
, kLIMMessageRequestGroupname
);
366 xpc_dictionary_set_string(payload
, kLIMMessageQuery
, name
);
368 xpc_object_t reply
= _muser_call("getgrnam", payload
);
370 item
= _muser_extract_group(si
, reply
, 0, 0);
374 xpc_release(payload
);
379 muser_group_bygid(struct si_mod_s
*si
, gid_t gid
)
381 xpc_object_t payload
;
382 si_item_t
*item
= NULL
;
384 if (!_muser_available()) { return NULL
; }
386 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
387 if (!payload
) { return NULL
; }
389 xpc_dictionary_set_string(payload
, kLIMMessageReqtype
, kLIMMessageRequestGID
);
390 xpc_dictionary_set_int64(payload
, kLIMMessageQuery
, gid
);
392 xpc_object_t reply
= _muser_call("getgrgid", payload
);
394 item
= _muser_extract_group(si
, reply
, 0, 0);
398 xpc_release(payload
);
403 muser_grouplist(struct si_mod_s
*si
, const char *name
, uint32_t count
)
405 xpc_object_t payload
;
406 si_item_t
*item
= NULL
;
408 if (!_muser_available()) { return NULL
; }
410 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
411 if (!payload
) { return NULL
; }
413 xpc_dictionary_set_string(payload
, kLIMMessageReqtype
, kLIMMessageRequestGrouplist
);
414 xpc_dictionary_set_int64(payload
, kLIMMessageQuery
, name
);
416 xpc_object_t reply
= _muser_call("getgrouplist", payload
);
418 item
= _muser_extract_grouplist(si
, reply
, name
, 0, 0);
422 xpc_release(payload
);
429 si_module_static_muser(void)
431 static const struct si_mod_vtable_s muser_vtable
=
433 .sim_user_byname
= &muser_user_byname
,
434 .sim_user_byuid
= &muser_user_byuid
,
436 .sim_group_byname
= &muser_group_byname
,
437 .sim_group_bygid
= &muser_group_bygid
,
438 .sim_grouplist
= &muser_grouplist
,
445 .flags
= SI_MOD_FLAG_STATIC
,
448 .vtable
= &muser_vtable
,
451 static dispatch_once_t once
;
452 dispatch_once(&once
, ^{
453 si
.name
= strdup("muser");
459 #endif // MUSER_AVAILABLE