Libinfo-477.40.5.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 EPIPE:
62 xpc_release(pipe);
63 break;
64 case EAGAIN:
65 /* try again? */
66 break;
67 case 0:
68 result = reply;
69 /* fallthrough */
70 default:
71 xpc_release(pipe);
72 pipe = NULL;
73 break;
74 }
75
76 if (pipe != NULL) {
77 xpc_release(pipe);
78 }
79
80 return result;
81 }
82
83 static int
84 _muser_xpc_pipe_disabled(xpc_pipe_t pipe)
85 {
86 xpc_object_t dict, reply = NULL;
87 int disabled = 0;
88
89 if (pipe == NULL) { return 1; }
90
91 dict = xpc_dictionary_create(NULL, NULL, 0);
92 xpc_dictionary_set_string(dict, kLIMMessageReqtype, kLIMMessageReplyAvailable);
93 xpc_dictionary_set_int64(dict, kLIMMessageVersion, 1);
94
95 int rv = xpc_pipe_routine(pipe, dict, &reply);
96 switch (rv) {
97 case 0:
98 disabled = !xpc_dictionary_get_bool(reply, kLIMMessageReplyAvailable);
99 break;
100 case EPIPE:
101 case EAGAIN:
102 default:
103 disabled = 1;
104 break;
105 }
106
107 return disabled;
108 }
109
110 XPC_RETURNS_RETAINED
111 static xpc_pipe_t
112 _muser_xpc_pipe(bool reset)
113 {
114 static dispatch_once_t once;
115 xpc_pipe_t pipe = NULL;
116
117 dispatch_once(&once, ^{
118 char *xbs_disable;
119 kern_return_t rv;
120 uint32_t multiuser_flags = 0;
121
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;
126 return;
127 }
128
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;
133 return;
134 }
135 });
136
137 if (_si_muser_disabled == 1) {
138 return NULL;
139 }
140
141 pthread_mutex_lock(&mutex);
142 if (reset) {
143 xpc_release(__muser_pipe);
144 __muser_pipe = NULL;
145 }
146
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); }
150 }
151
152 if (__muser_pipe) { pipe = xpc_retain(__muser_pipe); }
153 pthread_mutex_unlock(&mutex);
154
155 return pipe;
156 }
157
158 static bool
159 _muser_available(void)
160 {
161 xpc_pipe_t pipe;
162
163 pipe = _muser_xpc_pipe(false);
164 if (pipe != NULL) {
165 xpc_release(pipe);
166 }
167
168 if (_si_muser_disabled) {
169 return 0;
170 }
171
172 return (pipe != NULL);
173 }
174
175 static si_item_t *
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 *)"*";
181 p.pw_uid = (uid_t)0;
182 p.pw_gid = (gid_t)0;
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";
189
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);
194 if (nm) {
195 p.pw_name = (char *)nm;
196 reqs--;
197 }
198 }
199 else if (!strcmp(key, kLIMMessageReplyPasswd)) {
200 const char *pw = xpc_string_get_string_ptr(value);
201 if (pw) { p.pw_passwd = (char *)pw; }
202 }
203 else if (!strcmp(key, kLIMMessageReplyUID)) {
204 if (xpc_get_type(value) == XPC_TYPE_INT64) {
205 int64_t uid = xpc_int64_get_value(value);
206 p.pw_uid = uid;
207 reqs--;
208 }
209 }
210 else if (!strcmp(key, kLIMMessageReplyGID)) {
211 if (xpc_get_type(value) == XPC_TYPE_INT64) {
212 p.pw_gid = xpc_int64_get_value(value);
213 reqs--;
214 }
215 }
216 else if (!strcmp(key, kLIMMessageReplyHome)) {
217 const char *home = xpc_string_get_string_ptr(value);
218 if (home) { p.pw_dir = (char *)home; };
219 }
220 else if (!strcmp(key, kLIMMessageReplyShell)) {
221 const char *shell = xpc_string_get_string_ptr(value);
222 if (shell) { p.pw_shell = (char *)shell; }
223 }
224 return true;
225 });
226
227 if (reqs != 0) { return NULL; }
228
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);
230 }
231
232 static si_item_t *
233 _muser_extract_group(si_mod_t *si, xpc_object_t reply, uint64_t vg, uint64_t vc)
234 {
235 __block struct group g;
236 __block int reqs = 2;
237 g.gr_gid = (gid_t)0;
238 g.gr_name = (char *)"";
239 g.gr_passwd = (char *)"*";
240 g.gr_mem = NULL;
241
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);
246 if (nm) {
247 g.gr_name = (char *)nm;
248 reqs--;
249 }
250 }
251 else if (!strcmp(key, kLIMMessageReplyGroupID)) {
252 if (xpc_get_type(value) == XPC_TYPE_INT64) {
253 g.gr_gid = xpc_int64_get_value(value);
254 reqs--;
255 }
256 }
257 return true;
258 });
259
260 if (reqs != 0) { return NULL; }
261
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);
264 }
265
266 static si_item_t *
267 _muser_extract_grouplist(si_mod_t *si, xpc_object_t reply, const char *user,
268 uint64_t vg, uint64_t vc)
269 {
270 gid_t __block *gidlist = NULL;
271 size_t gidcount = 0;
272 si_item_t *item = NULL;
273
274 if (user == NULL) { return NULL; }
275
276 xpc_object_t list = xpc_dictionary_get_value(reply, kLIMMessageReplyGrouplist);
277 if (list) {
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));
281
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);
285 return true;
286 } else {
287 free(gidlist);
288 gidlist = NULL;
289 return false;
290 }
291 });
292 }
293 }
294
295 if (gidlist) {
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);
298 }
299
300 free(gidlist);
301 return item;
302 }
303
304 #pragma mark -
305
306 static si_item_t *
307 muser_user_byname(si_mod_t *si, const char *name)
308 {
309 xpc_object_t payload;
310 si_item_t *item = NULL;
311
312 if (!_muser_available()) { return NULL; }
313
314 payload = xpc_dictionary_create(NULL, NULL, 0);
315 if (!payload) { return NULL; }
316
317 xpc_dictionary_set_string(payload, kLIMMessageReqtype, kLIMMessageRequestUsername);
318 xpc_dictionary_set_string(payload, kLIMMessageQuery, name);
319
320 xpc_object_t reply = _muser_call("getpwnam", payload);
321 if (reply) {
322 item = _muser_extract_user(si, reply, 0, 0);
323 xpc_release(reply);
324 }
325
326 xpc_release(payload);
327 return item;
328 }
329
330 static si_item_t *
331 muser_user_byuid(si_mod_t *si, uid_t uid)
332 {
333 xpc_object_t payload;
334 si_item_t *item = NULL;
335
336 if (!_muser_available()) { return NULL; }
337
338 payload = xpc_dictionary_create(NULL, NULL, 0);
339 if (!payload) { return NULL; }
340
341 xpc_dictionary_set_string(payload, kLIMMessageReqtype, kLIMMessageRequestUID);
342 xpc_dictionary_set_int64(payload, kLIMMessageQuery, uid);
343
344 xpc_object_t reply = _muser_call("getpwuid", payload);
345 if (reply) {
346 item = _muser_extract_user(si, reply, 0, 0);
347 xpc_release(reply);
348 }
349
350 xpc_release(payload);
351 return item;
352 }
353
354 static si_item_t *
355 muser_group_byname(struct si_mod_s *si, const char *name)
356 {
357 xpc_object_t payload;
358 si_item_t *item = NULL;
359
360 if (!_muser_available()) { return NULL; }
361
362 payload = xpc_dictionary_create(NULL, NULL, 0);
363 if (!payload) { return NULL; }
364
365 xpc_dictionary_set_string(payload, kLIMMessageReqtype, kLIMMessageRequestGroupname);
366 xpc_dictionary_set_string(payload, kLIMMessageQuery, name);
367
368 xpc_object_t reply = _muser_call("getgrnam", payload);
369 if (reply) {
370 item = _muser_extract_group(si, reply, 0, 0);
371 xpc_release(reply);
372 }
373
374 xpc_release(payload);
375 return item;
376 }
377
378 static si_item_t *
379 muser_group_bygid(struct si_mod_s *si, gid_t gid)
380 {
381 xpc_object_t payload;
382 si_item_t *item = NULL;
383
384 if (!_muser_available()) { return NULL; }
385
386 payload = xpc_dictionary_create(NULL, NULL, 0);
387 if (!payload) { return NULL; }
388
389 xpc_dictionary_set_string(payload, kLIMMessageReqtype, kLIMMessageRequestGID);
390 xpc_dictionary_set_int64(payload, kLIMMessageQuery, gid);
391
392 xpc_object_t reply = _muser_call("getgrgid", payload);
393 if (reply) {
394 item = _muser_extract_group(si, reply, 0, 0);
395 xpc_release(reply);
396 }
397
398 xpc_release(payload);
399 return item;
400 }
401
402 static si_item_t *
403 muser_grouplist(struct si_mod_s *si, const char *name, uint32_t count)
404 {
405 xpc_object_t payload;
406 si_item_t *item = NULL;
407
408 if (!_muser_available()) { return NULL; }
409
410 payload = xpc_dictionary_create(NULL, NULL, 0);
411 if (!payload) { return NULL; }
412
413 xpc_dictionary_set_string(payload, kLIMMessageReqtype, kLIMMessageRequestGrouplist);
414 xpc_dictionary_set_int64(payload, kLIMMessageQuery, name);
415
416 xpc_object_t reply = _muser_call("getgrouplist", payload);
417 if (reply) {
418 item = _muser_extract_grouplist(si, reply, name, 0, 0);
419 xpc_release(reply);
420 }
421
422 xpc_release(payload);
423 return item;
424 }
425
426 #pragma mark -
427
428 si_mod_t *
429 si_module_static_muser(void)
430 {
431 static const struct si_mod_vtable_s muser_vtable =
432 {
433 .sim_user_byname = &muser_user_byname,
434 .sim_user_byuid = &muser_user_byuid,
435
436 .sim_group_byname = &muser_group_byname,
437 .sim_group_bygid = &muser_group_bygid,
438 .sim_grouplist = &muser_grouplist,
439 };
440
441 static si_mod_t si =
442 {
443 .vers = 1,
444 .refcount = 1,
445 .flags = SI_MOD_FLAG_STATIC,
446
447 .private = NULL,
448 .vtable = &muser_vtable,
449 };
450
451 static dispatch_once_t once;
452 dispatch_once(&once, ^{
453 si.name = strdup("muser");
454 });
455
456 return &si;
457 }
458
459 #endif // MUSER_AVAILABLE