Libinfo-542.40.3.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 #include <os/log.h>
36
37 static int _si_muser_disabled = 0;
38 static xpc_pipe_t __muser_pipe;
39 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
40
41 XPC_RETURNS_RETAINED
42 static xpc_pipe_t _muser_xpc_pipe(bool reset);
43
44 XPC_RETURNS_RETAINED
45 static xpc_object_t
46 _muser_call(const char *procname, xpc_object_t payload)
47 {
48 xpc_object_t result = NULL;
49 xpc_object_t reply;
50 xpc_pipe_t pipe;
51 int rv;
52 bool retried = false; /* try again incase of usermanager jetsam */
53 bool retryagain = false;
54 if (!payload) { return NULL; }
55
56 pipe = _muser_xpc_pipe(false);
57 if (!pipe) { return NULL; }
58
59 xpc_dictionary_set_string(payload, kLIMMessageRPCName, procname);
60 xpc_dictionary_set_uint64(payload, kLIMMessageVersion, 1);
61
62 /* if EPIPE, EAGAIN retry once */
63 do {
64 retryagain = false; /* if set from previous pass, reset now */
65 rv = xpc_pipe_routine(pipe, payload, &reply);
66 switch (rv) {
67 case 0:
68 result = reply;
69 break;
70 case EAGAIN:
71 case EPIPE:
72 {
73 if (retried) {
74 /* retried already, so bail out */
75 os_log_error(OS_LOG_DEFAULT, "_muser_call: Failure (%d) with retry, bailing", rv);
76 break;
77 }
78 /* lets retry one more time as jetsam of usermanager results in EPIPE */
79 retried = true;
80 retryagain = true;
81 os_log_debug(OS_LOG_DEFAULT, "_muser_call: Error from xpc pipe (%d), retrying", rv);
82 /* reestablish connetion */
83 xpc_release(pipe);
84 /* reestablish muser pipe */
85 pipe = _muser_xpc_pipe(true);
86 if (!pipe) { return NULL; }
87 }
88 break;
89 default:
90 break;
91 }
92 } while (retryagain);
93
94 xpc_release(pipe);
95 return result;
96 }
97
98 static int
99 _muser_xpc_pipe_disabled(xpc_pipe_t pipe)
100 {
101 xpc_object_t dict, reply = NULL;
102 int disabled = 0;
103
104 if (pipe == NULL) { return 1; }
105
106 dict = xpc_dictionary_create(NULL, NULL, 0);
107 xpc_dictionary_set_string(dict, kLIMMessageReqtype, kLIMMessageReplyAvailable);
108 xpc_dictionary_set_int64(dict, kLIMMessageVersion, 1);
109
110 int rv = xpc_pipe_routine(pipe, dict, &reply);
111 switch (rv) {
112 case 0:
113 disabled = !xpc_dictionary_get_bool(reply, kLIMMessageReplyAvailable);
114 xpc_release(reply);
115 break;
116 case EPIPE:
117 case EAGAIN:
118 default:
119 disabled = 1;
120 break;
121 }
122
123 xpc_release(dict);
124 return disabled;
125 }
126
127 XPC_RETURNS_RETAINED
128 static xpc_pipe_t
129 _muser_xpc_pipe(bool reset)
130 {
131 static dispatch_once_t once;
132 xpc_pipe_t pipe = NULL;
133
134 dispatch_once(&once, ^{
135 char *xbs_disable;
136 kern_return_t rv;
137 uint32_t multiuser_flags = 0;
138
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;
143 return;
144 }
145
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;
150 return;
151 }
152 });
153
154 if (_si_muser_disabled == 1) {
155 return NULL;
156 }
157
158 pthread_mutex_lock(&mutex);
159 if (reset) {
160 xpc_release(__muser_pipe);
161 __muser_pipe = NULL;
162 }
163
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); }
167 }
168
169 if (__muser_pipe) { pipe = xpc_retain(__muser_pipe); }
170 pthread_mutex_unlock(&mutex);
171
172 return pipe;
173 }
174
175 static bool
176 _muser_available(void)
177 {
178 xpc_pipe_t pipe;
179
180 pipe = _muser_xpc_pipe(false);
181 if (pipe != NULL) {
182 xpc_release(pipe);
183 }
184
185 if (_si_muser_disabled) {
186 return 0;
187 }
188
189 return (pipe != NULL);
190 }
191
192 static si_item_t *
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 *)"*";
198 p.pw_uid = (uid_t)0;
199 p.pw_gid = (gid_t)0;
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";
206
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);
211 if (nm) {
212 p.pw_name = (char *)nm;
213 reqs--;
214 }
215 }
216 else if (!strcmp(key, kLIMMessageReplyPasswd)) {
217 const char *pw = xpc_string_get_string_ptr(value);
218 if (pw) { p.pw_passwd = (char *)pw; }
219 }
220 else if (!strcmp(key, kLIMMessageReplyUID)) {
221 if (xpc_get_type(value) == XPC_TYPE_INT64) {
222 int64_t uid = xpc_int64_get_value(value);
223 p.pw_uid = uid;
224 reqs--;
225 }
226 }
227 else if (!strcmp(key, kLIMMessageReplyGID)) {
228 if (xpc_get_type(value) == XPC_TYPE_INT64) {
229 p.pw_gid = xpc_int64_get_value(value);
230 reqs--;
231 }
232 }
233 else if (!strcmp(key, kLIMMessageReplyHome)) {
234 const char *home = xpc_string_get_string_ptr(value);
235 if (home) { p.pw_dir = (char *)home; };
236 }
237 else if (!strcmp(key, kLIMMessageReplyShell)) {
238 const char *shell = xpc_string_get_string_ptr(value);
239 if (shell) { p.pw_shell = (char *)shell; }
240 }
241 return true;
242 });
243
244 if (reqs != 0) { return NULL; }
245
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);
247 }
248
249 static si_item_t *
250 _muser_extract_group(si_mod_t *si, xpc_object_t reply, uint64_t vg, uint64_t vc)
251 {
252 __block struct group g;
253 __block int reqs = 2;
254 g.gr_gid = (gid_t)0;
255 g.gr_name = (char *)"";
256 g.gr_passwd = (char *)"*";
257 g.gr_mem = NULL;
258
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);
263 if (nm) {
264 g.gr_name = (char *)nm;
265 reqs--;
266 }
267 }
268 else if (!strcmp(key, kLIMMessageReplyGroupID)) {
269 if (xpc_get_type(value) == XPC_TYPE_INT64) {
270 g.gr_gid = xpc_int64_get_value(value);
271 reqs--;
272 }
273 }
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;
279
280 for (size_t i=0; i<count; i++) {
281 g.gr_mem[i] = (char *)xpc_array_get_string(value, i);
282 }
283 }
284 }
285 return true;
286 });
287
288 if (reqs != 0) { return NULL; }
289
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);
292
293 free(g.gr_mem);
294 return item;
295 }
296
297 static si_item_t *
298 _muser_extract_grouplist(si_mod_t *si, xpc_object_t reply, const char *user,
299 uint64_t vg, uint64_t vc)
300 {
301 gid_t __block *gidlist = NULL;
302 size_t gidcount = 0;
303 si_item_t *item = NULL;
304
305 if (user == NULL) { return NULL; }
306
307 xpc_object_t list = xpc_dictionary_get_value(reply, kLIMMessageReplyGrouplist);
308 if (list) {
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));
312
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);
316 return true;
317 } else {
318 free(gidlist);
319 gidlist = NULL;
320 return false;
321 }
322 });
323 }
324 }
325
326 if (gidlist) {
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);
329 }
330
331 free(gidlist);
332 return item;
333 }
334
335 #pragma mark -
336
337 static si_item_t *
338 muser_user_byname(si_mod_t *si, const char *name)
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, kLIMMessageRequestUsername);
349 xpc_dictionary_set_string(payload, kLIMMessageQuery, name);
350
351 xpc_object_t reply = _muser_call("getpwnam", 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_user_byuid(si_mod_t *si, uid_t uid)
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, kLIMMessageRequestUID);
373 xpc_dictionary_set_int64(payload, kLIMMessageQuery, uid);
374
375 xpc_object_t reply = _muser_call("getpwuid", payload);
376 if (reply) {
377 item = _muser_extract_user(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_byname(struct si_mod_s *si, const char *name)
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, kLIMMessageRequestGroupname);
397 xpc_dictionary_set_string(payload, kLIMMessageQuery, name);
398
399 xpc_object_t reply = _muser_call("getgrnam", 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_group_bygid(struct si_mod_s *si, gid_t gid)
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, kLIMMessageRequestGID);
421 xpc_dictionary_set_int64(payload, kLIMMessageQuery, gid);
422
423 xpc_object_t reply = _muser_call("getgrgid", payload);
424 if (reply) {
425 item = _muser_extract_group(si, reply, 0, 0);
426 xpc_release(reply);
427 }
428
429 xpc_release(payload);
430 return item;
431 }
432
433 static si_item_t *
434 muser_grouplist(struct si_mod_s *si, const char *name, uint32_t count)
435 {
436 xpc_object_t payload;
437 si_item_t *item = NULL;
438
439 if (!_muser_available()) { return NULL; }
440
441 payload = xpc_dictionary_create(NULL, NULL, 0);
442 if (!payload) { return NULL; }
443
444 xpc_dictionary_set_string(payload, kLIMMessageReqtype, kLIMMessageRequestGrouplist);
445 xpc_dictionary_set_string(payload, kLIMMessageQuery, name);
446
447 xpc_object_t reply = _muser_call("getgrouplist", payload);
448 if (reply) {
449 item = _muser_extract_grouplist(si, reply, name, 0, 0);
450 xpc_release(reply);
451 }
452
453 xpc_release(payload);
454 return item;
455 }
456
457 #pragma mark -
458
459 si_mod_t *
460 si_module_static_muser(void)
461 {
462 static const struct si_mod_vtable_s muser_vtable =
463 {
464 .sim_user_byname = &muser_user_byname,
465 .sim_user_byuid = &muser_user_byuid,
466
467 .sim_group_byname = &muser_group_byname,
468 .sim_group_bygid = &muser_group_bygid,
469 .sim_grouplist = &muser_grouplist,
470 };
471
472 static si_mod_t si =
473 {
474 .vers = 1,
475 .refcount = 1,
476 .flags = SI_MOD_FLAG_STATIC,
477
478 .private = NULL,
479 .vtable = &muser_vtable,
480 };
481
482 static dispatch_once_t once;
483 dispatch_once(&once, ^{
484 si.name = strdup("muser");
485 });
486
487 return &si;
488 }
489
490 #endif // MUSER_AVAILABLE