Libinfo-538.tar.gz
[apple/libinfo.git] / lookup.subproj / muser_module.c
1 /*
2 * Copyright (c) 2015 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * ds.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #ifdef MUSER_AVAILABLE
25
26 #include <dispatch/dispatch.h>
27 #include <libinfo_muser.h>
28 #include <mach/mach_host.h>
29 #include <machine/cpu_capabilities.h>
30 #include <si_data.h>
31 #include <si_module.h>
32 #include <stdio.h>
33 #include <xpc/xpc.h>
34 #include <xpc/private.h>
35
36 static int _si_muser_disabled = 0;
37 static xpc_pipe_t __muser_pipe;
38 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
39
40 XPC_RETURNS_RETAINED
41 static xpc_pipe_t _muser_xpc_pipe(bool reset);
42
43 XPC_RETURNS_RETAINED
44 static xpc_object_t
45 _muser_call(const char *procname, xpc_object_t payload)
46 {
47 xpc_object_t result = NULL;
48 xpc_object_t reply;
49 xpc_pipe_t pipe;
50
51 if (!payload) { return NULL; }
52
53 pipe = _muser_xpc_pipe(false);
54 if (!pipe) { return NULL; }
55
56 xpc_dictionary_set_string(payload, kLIMMessageRPCName, procname);
57 xpc_dictionary_set_uint64(payload, kLIMMessageVersion, 1);
58
59 int rv = xpc_pipe_routine(pipe, payload, &reply);
60 switch (rv) {
61 case 0:
62 result = reply;
63 break;
64 case EAGAIN:
65 case EPIPE:
66 default:
67 break;
68 }
69
70 xpc_release(pipe);
71 return result;
72 }
73
74 static int
75 _muser_xpc_pipe_disabled(xpc_pipe_t pipe)
76 {
77 xpc_object_t dict, reply = NULL;
78 int disabled = 0;
79
80 if (pipe == NULL) { return 1; }
81
82 dict = xpc_dictionary_create(NULL, NULL, 0);
83 xpc_dictionary_set_string(dict, kLIMMessageReqtype, kLIMMessageReplyAvailable);
84 xpc_dictionary_set_int64(dict, kLIMMessageVersion, 1);
85
86 int rv = xpc_pipe_routine(pipe, dict, &reply);
87 switch (rv) {
88 case 0:
89 disabled = !xpc_dictionary_get_bool(reply, kLIMMessageReplyAvailable);
90 xpc_release(reply);
91 break;
92 case EPIPE:
93 case EAGAIN:
94 default:
95 disabled = 1;
96 break;
97 }
98
99 xpc_release(dict);
100 return disabled;
101 }
102
103 XPC_RETURNS_RETAINED
104 static xpc_pipe_t
105 _muser_xpc_pipe(bool reset)
106 {
107 static dispatch_once_t once;
108 xpc_pipe_t pipe = NULL;
109
110 dispatch_once(&once, ^{
111 char *xbs_disable;
112 kern_return_t rv;
113 uint32_t multiuser_flags = 0;
114
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;
119 return;
120 }
121
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;
126 return;
127 }
128 });
129
130 if (_si_muser_disabled == 1) {
131 return NULL;
132 }
133
134 pthread_mutex_lock(&mutex);
135 if (reset) {
136 xpc_release(__muser_pipe);
137 __muser_pipe = NULL;
138 }
139
140 if (__muser_pipe == NULL) {
141 __muser_pipe = xpc_pipe_create(kLibinfoMultiuserPortName, XPC_PIPE_USE_SYNC_IPC_OVERRIDE | XPC_PIPE_PROPAGATE_QOS);
142 if (!_si_muser_disabled) { _si_muser_disabled = _muser_xpc_pipe_disabled(__muser_pipe); }
143 }
144
145 if (__muser_pipe) { pipe = xpc_retain(__muser_pipe); }
146 pthread_mutex_unlock(&mutex);
147
148 return pipe;
149 }
150
151 static bool
152 _muser_available(void)
153 {
154 xpc_pipe_t pipe;
155
156 pipe = _muser_xpc_pipe(false);
157 if (pipe != NULL) {
158 xpc_release(pipe);
159 }
160
161 if (_si_muser_disabled) {
162 return 0;
163 }
164
165 return (pipe != NULL);
166 }
167
168 static si_item_t *
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 *)"*";
174 p.pw_uid = (uid_t)0;
175 p.pw_gid = (gid_t)0;
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";
182
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);
187 if (nm) {
188 p.pw_name = (char *)nm;
189 reqs--;
190 }
191 }
192 else if (!strcmp(key, kLIMMessageReplyPasswd)) {
193 const char *pw = xpc_string_get_string_ptr(value);
194 if (pw) { p.pw_passwd = (char *)pw; }
195 }
196 else if (!strcmp(key, kLIMMessageReplyUID)) {
197 if (xpc_get_type(value) == XPC_TYPE_INT64) {
198 int64_t uid = xpc_int64_get_value(value);
199 p.pw_uid = uid;
200 reqs--;
201 }
202 }
203 else if (!strcmp(key, kLIMMessageReplyGID)) {
204 if (xpc_get_type(value) == XPC_TYPE_INT64) {
205 p.pw_gid = xpc_int64_get_value(value);
206 reqs--;
207 }
208 }
209 else if (!strcmp(key, kLIMMessageReplyHome)) {
210 const char *home = xpc_string_get_string_ptr(value);
211 if (home) { p.pw_dir = (char *)home; };
212 }
213 else if (!strcmp(key, kLIMMessageReplyShell)) {
214 const char *shell = xpc_string_get_string_ptr(value);
215 if (shell) { p.pw_shell = (char *)shell; }
216 }
217 return true;
218 });
219
220 if (reqs != 0) { return NULL; }
221
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);
223 }
224
225 static si_item_t *
226 _muser_extract_group(si_mod_t *si, xpc_object_t reply, uint64_t vg, uint64_t vc)
227 {
228 __block struct group g;
229 __block int reqs = 2;
230 g.gr_gid = (gid_t)0;
231 g.gr_name = (char *)"";
232 g.gr_passwd = (char *)"*";
233 g.gr_mem = NULL;
234
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);
239 if (nm) {
240 g.gr_name = (char *)nm;
241 reqs--;
242 }
243 }
244 else if (!strcmp(key, kLIMMessageReplyGroupID)) {
245 if (xpc_get_type(value) == XPC_TYPE_INT64) {
246 g.gr_gid = xpc_int64_get_value(value);
247 reqs--;
248 }
249 }
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;
255
256 for (size_t i=0; i<count; i++) {
257 g.gr_mem[i] = (char *)xpc_array_get_string(value, i);
258 }
259 }
260 }
261 return true;
262 });
263
264 if (reqs != 0) { return NULL; }
265
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);
268
269 free(g.gr_mem);
270 return item;
271 }
272
273 static si_item_t *
274 _muser_extract_grouplist(si_mod_t *si, xpc_object_t reply, const char *user,
275 uint64_t vg, uint64_t vc)
276 {
277 gid_t __block *gidlist = NULL;
278 size_t gidcount = 0;
279 si_item_t *item = NULL;
280
281 if (user == NULL) { return NULL; }
282
283 xpc_object_t list = xpc_dictionary_get_value(reply, kLIMMessageReplyGrouplist);
284 if (list) {
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));
288
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);
292 return true;
293 } else {
294 free(gidlist);
295 gidlist = NULL;
296 return false;
297 }
298 });
299 }
300 }
301
302 if (gidlist) {
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);
305 }
306
307 free(gidlist);
308 return item;
309 }
310
311 #pragma mark -
312
313 static si_item_t *
314 muser_user_byname(si_mod_t *si, const char *name)
315 {
316 xpc_object_t payload;
317 si_item_t *item = NULL;
318
319 if (!_muser_available()) { return NULL; }
320
321 payload = xpc_dictionary_create(NULL, NULL, 0);
322 if (!payload) { return NULL; }
323
324 xpc_dictionary_set_string(payload, kLIMMessageReqtype, kLIMMessageRequestUsername);
325 xpc_dictionary_set_string(payload, kLIMMessageQuery, name);
326
327 xpc_object_t reply = _muser_call("getpwnam", payload);
328 if (reply) {
329 item = _muser_extract_user(si, reply, 0, 0);
330 xpc_release(reply);
331 }
332
333 xpc_release(payload);
334 return item;
335 }
336
337 static si_item_t *
338 muser_user_byuid(si_mod_t *si, uid_t uid)
339 {
340 xpc_object_t payload;
341 si_item_t *item = NULL;
342
343 if (!_muser_available()) { return NULL; }
344
345 payload = xpc_dictionary_create(NULL, NULL, 0);
346 if (!payload) { return NULL; }
347
348 xpc_dictionary_set_string(payload, kLIMMessageReqtype, kLIMMessageRequestUID);
349 xpc_dictionary_set_int64(payload, kLIMMessageQuery, uid);
350
351 xpc_object_t reply = _muser_call("getpwuid", payload);
352 if (reply) {
353 item = _muser_extract_user(si, reply, 0, 0);
354 xpc_release(reply);
355 }
356
357 xpc_release(payload);
358 return item;
359 }
360
361 static si_item_t *
362 muser_group_byname(struct si_mod_s *si, const char *name)
363 {
364 xpc_object_t payload;
365 si_item_t *item = NULL;
366
367 if (!_muser_available()) { return NULL; }
368
369 payload = xpc_dictionary_create(NULL, NULL, 0);
370 if (!payload) { return NULL; }
371
372 xpc_dictionary_set_string(payload, kLIMMessageReqtype, kLIMMessageRequestGroupname);
373 xpc_dictionary_set_string(payload, kLIMMessageQuery, name);
374
375 xpc_object_t reply = _muser_call("getgrnam", payload);
376 if (reply) {
377 item = _muser_extract_group(si, reply, 0, 0);
378 xpc_release(reply);
379 }
380
381 xpc_release(payload);
382 return item;
383 }
384
385 static si_item_t *
386 muser_group_bygid(struct si_mod_s *si, gid_t gid)
387 {
388 xpc_object_t payload;
389 si_item_t *item = NULL;
390
391 if (!_muser_available()) { return NULL; }
392
393 payload = xpc_dictionary_create(NULL, NULL, 0);
394 if (!payload) { return NULL; }
395
396 xpc_dictionary_set_string(payload, kLIMMessageReqtype, kLIMMessageRequestGID);
397 xpc_dictionary_set_int64(payload, kLIMMessageQuery, gid);
398
399 xpc_object_t reply = _muser_call("getgrgid", payload);
400 if (reply) {
401 item = _muser_extract_group(si, reply, 0, 0);
402 xpc_release(reply);
403 }
404
405 xpc_release(payload);
406 return item;
407 }
408
409 static si_item_t *
410 muser_grouplist(struct si_mod_s *si, const char *name, uint32_t count)
411 {
412 xpc_object_t payload;
413 si_item_t *item = NULL;
414
415 if (!_muser_available()) { return NULL; }
416
417 payload = xpc_dictionary_create(NULL, NULL, 0);
418 if (!payload) { return NULL; }
419
420 xpc_dictionary_set_string(payload, kLIMMessageReqtype, kLIMMessageRequestGrouplist);
421 xpc_dictionary_set_string(payload, kLIMMessageQuery, name);
422
423 xpc_object_t reply = _muser_call("getgrouplist", payload);
424 if (reply) {
425 item = _muser_extract_grouplist(si, reply, name, 0, 0);
426 xpc_release(reply);
427 }
428
429 xpc_release(payload);
430 return item;
431 }
432
433 #pragma mark -
434
435 si_mod_t *
436 si_module_static_muser(void)
437 {
438 static const struct si_mod_vtable_s muser_vtable =
439 {
440 .sim_user_byname = &muser_user_byname,
441 .sim_user_byuid = &muser_user_byuid,
442
443 .sim_group_byname = &muser_group_byname,
444 .sim_group_bygid = &muser_group_bygid,
445 .sim_grouplist = &muser_grouplist,
446 };
447
448 static si_mod_t si =
449 {
450 .vers = 1,
451 .refcount = 1,
452 .flags = SI_MOD_FLAG_STATIC,
453
454 .private = NULL,
455 .vtable = &muser_vtable,
456 };
457
458 static dispatch_once_t once;
459 dispatch_once(&once, ^{
460 si.name = strdup("muser");
461 });
462
463 return &si;
464 }
465
466 #endif // MUSER_AVAILABLE