Libinfo-517.200.9.tar.gz
[apple/libinfo.git] / lookup.subproj / ds_module.c
1 /*
2 * Copyright (c) 2008-2018 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 DS_AVAILABLE
25
26 #include "libinfo_common.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <time.h>
33 #include <errno.h>
34 #include <arpa/inet.h>
35 #include <sys/stat.h>
36 #include <pthread.h>
37 #include <ils.h>
38 #include <pwd.h>
39 #include <grp.h>
40 #include <fstab.h>
41 #include <netdb.h>
42 #include <notify.h>
43 #include <notify_keys.h>
44 #include <si_data.h>
45 #include <si_module.h>
46 #include <netdb_async.h>
47 #include <net/if.h>
48 #include <xpc/xpc.h>
49 #include <xpc/private.h>
50 #include <opendirectory/odipc.h>
51 #include <servers/bootstrap.h>
52 #include <bootstrap_priv.h>
53 #include <opendirectory/DSlibinfoMIG_types.h>
54 #include <os/activity.h>
55 #include <os/log.h>
56
57 #define IPV6_ADDR_LEN 16
58 #define IPV4_ADDR_LEN 4
59
60 /* The LI_OS_ACTIVITY macro contains os_activity_t functions
61 * that are not POSIX compliant. So calling them may
62 * inadvertantly change errno. To avoid this, the macro
63 * explicityly restores the errno to its state on entry
64 * when done.
65 *
66 * Macro cannot contain any {} because it will end the scope
67 * of the activity prematurely
68 */
69 #define _LI_OS_ACTIVITY(_var, _desc) \
70 int _var = errno; \
71 os_activity_t activity __attribute__((__cleanup__(_li_auto_os_release))) = os_activity_create(_desc, OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT); \
72 os_activity_scope(activity); \
73 errno = _var
74
75 #define LI_OS_ACTIVITY(_desc) _LI_OS_ACTIVITY(OS_CONCAT(errnosav, __COUNTER__), _desc)
76
77 typedef si_item_t *(*od_extract_t)(si_mod_t *si, xpc_object_t reply, const void *extra, uint64_t valid_global, uint64_t valid_cat);
78
79 /* notify SPI */
80 uint32_t notify_peek(int token, uint32_t *val);
81
82 typedef struct
83 {
84 int notify_token_global;
85 int notify_token_user;
86 int notify_token_group;
87 int notify_token_service;
88 } ds_si_private_t;
89
90 extern uint32_t gL1CacheEnabled;
91 extern int _si_opendirectory_disabled;
92
93 static pthread_key_t _ds_serv_cache_key = 0;
94 static xpc_pipe_t __od_pipe; /* use accessor only */
95 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
96
97 mach_port_t _ds_port;
98
99 static void
100 _od_fork_child(void)
101 {
102 // re-enable opendirectory interaction since we forked
103 _si_opendirectory_disabled = 0;
104
105 if (__od_pipe != NULL) {
106 xpc_pipe_invalidate(__od_pipe);
107 /* disable release due to 10649340, it will cause a minor leak for each fork without exec */
108 // xpc_release(__od_pipe);
109 __od_pipe = NULL;
110 }
111 _ds_port = MACH_PORT_NULL;
112 pthread_mutex_unlock(&mutex);
113 }
114
115 static void
116 _od_fork_prepare(void)
117 {
118 pthread_mutex_lock(&mutex);
119 }
120
121 static void
122 _od_fork_parent(void)
123 {
124 pthread_mutex_unlock(&mutex);
125 }
126
127 static void
128 _ds_serv_cache_free(void *x)
129 {
130 if (x != NULL) si_item_release(x);
131 }
132
133 LIBINFO_EXPORT
134 void
135 _si_disable_opendirectory(void)
136 {
137 _si_opendirectory_disabled = 1;
138 _ds_port = MACH_PORT_NULL;
139 }
140
141 static void
142 _li_auto_os_release(os_activity_t *activity)
143 {
144 os_release(*activity);
145 (*activity) = NULL;
146 }
147
148 XPC_RETURNS_RETAINED
149 static xpc_pipe_t
150 _od_xpc_pipe(bool resetPipe)
151 {
152 static dispatch_once_t once;
153 xpc_pipe_t result = NULL;
154
155 dispatch_once(&once, ^(void) {
156 char *xbs_disable;
157
158 /* if this is a build environment we ignore opendirectoryd */
159 xbs_disable = getenv("XBS_DISABLE_LIBINFO");
160 if ((issetugid() == 0) && (xbs_disable != NULL) && (strcmp(xbs_disable, "YES") == 0)) {
161 _si_opendirectory_disabled = 1;
162 return;
163 }
164
165 pthread_atfork(_od_fork_prepare, _od_fork_parent, _od_fork_child);
166 });
167
168 if (_si_opendirectory_disabled == 1) {
169 return NULL;
170 }
171
172 pthread_mutex_lock(&mutex);
173 if (resetPipe) {
174 xpc_release(__od_pipe);
175 __od_pipe = NULL;
176 }
177
178 if (__od_pipe == NULL) {
179 if (!issetugid() && getenv("OD_DEBUG_MODE") != NULL) {
180 __od_pipe = xpc_pipe_create(kODMachLibinfoPortNameDebug, 0);
181 } else {
182 __od_pipe = xpc_pipe_create(kODMachLibinfoPortName, XPC_PIPE_FLAG_PRIVILEGED);
183 }
184 }
185
186 if (__od_pipe != NULL) result = xpc_retain(__od_pipe);
187 pthread_mutex_unlock(&mutex);
188
189 return result;
190 }
191
192 static bool
193 _od_running(void)
194 {
195 xpc_pipe_t pipe;
196
197 pipe = _od_xpc_pipe(false);
198 if (pipe != NULL) {
199 xpc_release(pipe);
200 }
201
202 if (_si_opendirectory_disabled) {
203 return 0;
204 }
205
206 return (pipe != NULL);
207 }
208
209 static void
210 _ds_child(void)
211 {
212 _ds_port = MACH_PORT_NULL;
213 }
214
215 LIBINFO_EXPORT
216 int
217 _ds_running(void)
218 {
219 kern_return_t status;
220 char *od_debug_mode = NULL;
221
222 if (_ds_port != MACH_PORT_NULL) return 1;
223
224 if (_si_opendirectory_disabled) return 0;
225 pthread_atfork(NULL, NULL, _ds_child);
226
227 if (!issetugid()) {
228 od_debug_mode = getenv("OD_DEBUG_MODE");
229 }
230
231 if (od_debug_mode) {
232 status = bootstrap_look_up(bootstrap_port, kDSStdMachDSLookupPortName "_debug", &_ds_port);
233 } else {
234 status = bootstrap_look_up2(bootstrap_port, kDSStdMachDSLookupPortName, &_ds_port, 0, BOOTSTRAP_PRIVILEGED_SERVER);
235 }
236 if ((status != BOOTSTRAP_SUCCESS) && (status != BOOTSTRAP_UNKNOWN_SERVICE)) _ds_port = MACH_PORT_NULL;
237
238 return (_ds_port != MACH_PORT_NULL);
239 }
240
241 static bool
242 _valid_token(xpc_object_t reply)
243 {
244 audit_token_t token;
245
246 /*
247 * This should really call audit_token_to_au32,
248 * but that's in libbsm, not in a Libsystem library.
249 */
250 xpc_dictionary_get_audit_token(reply, &token);
251
252 return ((uid_t) token.val[1] == 0);
253 }
254
255 static void
256 _ds_get_validation(si_mod_t *si, uint64_t *a, uint64_t *b, int cat)
257 {
258 ds_si_private_t *pp;
259 uint32_t peek;
260 int status;
261
262 if (si == NULL) return;
263
264 pp = (ds_si_private_t *)si->private;
265 if (pp == NULL) return;
266
267 if (a != NULL)
268 {
269 *a = 0;
270 status = notify_peek(pp->notify_token_global, &peek);
271 if (status == NOTIFY_STATUS_OK) *a = ntohl(peek);
272 }
273
274 if (b != NULL)
275 {
276 *b = 0;
277 peek = 0;
278 status = NOTIFY_STATUS_FAILED;
279
280 if (cat == CATEGORY_USER) status = notify_peek(pp->notify_token_user, &peek);
281 else if (cat == CATEGORY_GROUP) status = notify_peek(pp->notify_token_group, &peek);
282 else if (cat == CATEGORY_GROUPLIST) status = notify_peek(pp->notify_token_group, &peek);
283 else if (cat == CATEGORY_SERVICE) status = notify_peek(pp->notify_token_service, &peek);
284
285 if (status == NOTIFY_STATUS_OK) *b = ntohl(peek);
286 }
287 }
288
289 XPC_RETURNS_RETAINED
290 __private_extern__ xpc_object_t
291 _od_rpc_call(const char *procname, xpc_object_t payload, xpc_pipe_t (*get_pipe)(bool))
292 {
293 xpc_object_t result = NULL;
294 xpc_object_t reply;
295 xpc_pipe_t od_pipe;
296 int retries, rc;
297 bool free_payload = false;
298
299 od_pipe = get_pipe(false);
300 if (od_pipe == NULL) return NULL;
301
302 if (payload == NULL) {
303 payload = xpc_dictionary_create(NULL, NULL, 0);
304 free_payload = true;
305 }
306
307 // we nest it for backward compatibility so we can do independent submissions
308 xpc_dictionary_set_string(payload, OD_RPC_NAME, procname);
309 xpc_dictionary_set_int64(payload, OD_RPC_VERSION, 2);
310
311 for (retries = 0; od_pipe != NULL && retries < 2; retries++) {
312 rc = xpc_pipe_routine(od_pipe, payload, &reply);
313 switch (rc) {
314 case EPIPE:
315 xpc_release(od_pipe);
316 od_pipe = get_pipe(true);
317 break;
318
319 case EAGAIN:
320 /* just loop and try to send again */
321 break;
322
323 case 0:
324 if (_valid_token(reply) == true) {
325 result = reply;
326 }
327 /* fall through since we got a valid response */
328
329 default:
330 /* release and NULL the pipe it'll break the loop */
331 xpc_release(od_pipe);
332 od_pipe = NULL;
333 break;
334 }
335 }
336
337 if (od_pipe != NULL) {
338 xpc_release(od_pipe);
339 }
340
341 if (free_payload) {
342 xpc_release(payload);
343 }
344
345 return result;
346 }
347
348 static si_list_t *
349 _ds_list(si_mod_t *si, int cat, const char *procname, const void *extra, od_extract_t extract)
350 {
351 __block si_list_t *list;
352 uint64_t va = 0, vb = 0;
353 xpc_object_t reply, result;
354
355 if (procname == NULL) return NULL;
356
357 _ds_get_validation(si, &va, &vb, cat);
358
359 list = NULL;
360 reply = _od_rpc_call(procname, NULL, _od_xpc_pipe);
361 if (reply != NULL) {
362 result = xpc_dictionary_get_value(reply, OD_RPC_RESULT);
363 if (result != NULL && xpc_get_type(result) == XPC_TYPE_ARRAY) {
364 xpc_array_apply(result, ^bool(size_t index, xpc_object_t value) {
365 si_item_t *item = extract(si, value, extra, va, vb);
366 list = si_list_add(list, item);
367 si_item_release(item);
368
369 return true;
370 });
371 }
372
373 xpc_release(reply);
374 }
375
376 return list;
377 }
378
379 static si_item_t *
380 _ds_item(si_mod_t *si, int cat, const char *procname, const void *extra, od_extract_t extract, xpc_object_t payload)
381 {
382 xpc_object_t result;
383 uint64_t va = 0, vb = 0;
384 si_item_t *item = NULL;
385
386 if (procname == NULL) return NULL;
387
388 result = _od_rpc_call(procname, payload, _od_xpc_pipe);
389 if (result != NULL) {
390 _ds_get_validation(si, &va, &vb, cat);
391 if (xpc_dictionary_get_int64(result, OD_RPC_ERROR) == 0) {
392 item = extract(si, result, extra, va, vb);
393 }
394
395 xpc_release(result);
396 }
397
398 return item;
399 }
400
401 static int
402 _ds_is_valid(si_mod_t *si, si_item_t *item)
403 {
404 si_mod_t *src;
405 ds_si_private_t *pp;
406 int status;
407 uint32_t oldval, newval;
408
409 if (si == NULL) return 0;
410 if (item == NULL) return 0;
411 if (si->name == NULL) return 0;
412 if (item->src == NULL) return 0;
413
414 pp = (ds_si_private_t *)si->private;
415 if (pp == NULL) return 0;
416
417 src = (si_mod_t *)item->src;
418
419 if (src->name == NULL) return 0;
420 if (string_not_equal(si->name, src->name)) return 0;
421
422 /* check global invalidation */
423 oldval = item->validation_a;
424 newval = -1;
425 status = notify_peek(pp->notify_token_global, &newval);
426 if (status != NOTIFY_STATUS_OK) return 0;
427
428 newval = ntohl(newval);
429 if (oldval != newval) return 0;
430
431 oldval = item->validation_b;
432 newval = -1;
433 if (item->type == CATEGORY_USER) status = notify_peek(pp->notify_token_user, &newval);
434 else if (item->type == CATEGORY_GROUP) status = notify_peek(pp->notify_token_group, &newval);
435 else if (item->type == CATEGORY_SERVICE) status = notify_peek(pp->notify_token_service, &newval);
436 else return 0;
437
438 if (status != NOTIFY_STATUS_OK) return 0;
439
440 newval = ntohl(newval);
441 if (oldval != newval) return 0;
442
443 return 1;
444 }
445
446 static xpc_object_t
447 _xpc_query_key_string(const char *key, const char *value)
448 {
449 xpc_object_t payload;
450
451 if (value == NULL) return NULL;
452
453 payload = xpc_dictionary_create(NULL, NULL, 0);
454 if (payload == NULL) return NULL;
455
456 xpc_dictionary_set_string(payload, key, value);
457
458 return payload;
459 }
460
461 static xpc_object_t
462 _xpc_query_key_id(const char *key, id_t idValue)
463 {
464 xpc_object_t payload;
465
466 payload = xpc_dictionary_create(NULL, NULL, 0);
467 if (payload == NULL) return NULL;
468
469 xpc_dictionary_set_int64(payload, key, idValue);
470
471 return payload;
472 }
473
474 static xpc_object_t
475 _xpc_query_key_uuid(const char *key, uuid_t uu)
476 {
477 xpc_object_t payload;
478
479 payload = xpc_dictionary_create(NULL, NULL, 0);
480 if (payload == NULL) return NULL;
481
482 xpc_dictionary_set_uuid(payload, key, uu);
483
484 return payload;
485 }
486
487 static xpc_object_t
488 _xpc_query_key_int(const char *key, int64_t intValue)
489 {
490 xpc_object_t payload;
491
492 payload = xpc_dictionary_create(NULL, NULL, 0);
493 if (payload == NULL) return NULL;
494
495 xpc_dictionary_set_int64(payload, key, intValue);
496
497 return payload;
498 }
499
500 #pragma mark -
501
502 static int
503 _extract_string_from_xpc_array_index(xpc_object_t reply, int index, const char **str)
504 {
505 xpc_object_t value;
506
507 if (xpc_array_get_count(reply) < index) return -1;
508
509 value = xpc_array_get_value(reply, index);
510 if (xpc_get_type(value) != XPC_TYPE_STRING) return -1;
511
512 *str = xpc_string_get_string_ptr(value);
513 return 0;
514 }
515
516 static int
517 _extract_string_from_xpc_object(xpc_object_t value, const char **str)
518 {
519 if (value == NULL) return -1;
520 else if (xpc_get_type(value) == XPC_TYPE_STRING)
521 {
522 *str = xpc_string_get_string_ptr(value);
523 return 0;
524 }
525 else if (xpc_get_type(value) == XPC_TYPE_ARRAY)
526 {
527 return _extract_string_from_xpc_array_index(value, 0, str);
528 }
529
530 return -1;
531 }
532
533 static int
534 _extract_uint32_from_xpc_object(xpc_object_t value, uint32_t *val32)
535 {
536 xpc_type_t type;
537
538 if (value == NULL) return -1;
539 type = xpc_get_type(value);
540
541 if (type == XPC_TYPE_STRING)
542 {
543 *val32 = atoi(xpc_string_get_string_ptr(value));
544 return 0;
545 }
546 else if (type == XPC_TYPE_INT64)
547 {
548 *val32 = (uint32_t)xpc_int64_get_value(value);
549 return 0;
550 }
551 else if (type == XPC_TYPE_BOOL)
552 {
553 *val32 = (uint32_t)xpc_bool_get_value(value);
554 return 0;
555 }
556 else if (type == XPC_TYPE_ARRAY)
557 {
558 if (xpc_array_get_count(value) == 0) return -1;
559 return _extract_uint32_from_xpc_object(xpc_array_get_value(value, 0), val32);
560 }
561
562 return -1;
563 }
564
565 static int
566 _extract_string_list_from_xpc_array_index(xpc_object_t reply, int index, unsigned int *len, char ***list)
567 {
568 char **result;
569 xpc_object_t xpc_array = xpc_array_get_value(reply, index);
570
571 if ((xpc_array == NULL) || (xpc_get_type(xpc_array) != XPC_TYPE_ARRAY)) return -1;
572
573 result = calloc(xpc_array_get_count(xpc_array) + 1, sizeof(*result));
574 if (result == NULL) return -1;
575
576 /* include trailing NULL */
577 if (len != NULL) (*len) = xpc_array_get_count(xpc_array) + 1;
578
579 xpc_array_apply(xpc_array, ^bool(size_t idx, xpc_object_t value) {
580 result[idx] = (char *)xpc_string_get_string_ptr(value);
581 return true;
582 });
583
584 *list = result;
585 return 0;
586 }
587
588 static int
589 _extract_uint32_from_xpc_array_index(xpc_object_t reply, int index, uint32_t *val32)
590 {
591 xpc_object_t value = xpc_array_get_value(reply, index);
592 return _extract_uint32_from_xpc_object(value, val32);
593 }
594
595 static int
596 _extract_string_list_from_xpc_array(xpc_object_t xpc_array, unsigned int *len, char ***list)
597 {
598 char **result;
599
600 if ((xpc_array == NULL) || (xpc_get_type(xpc_array) != XPC_TYPE_ARRAY)) return -1;
601
602 result = calloc(xpc_array_get_count(xpc_array) + 1, sizeof(*result));
603 if (result == NULL) return -1;
604
605 /* include trailing NULL */
606 if (len != NULL) (*len) = xpc_array_get_count(xpc_array) + 1;
607
608 xpc_array_apply(xpc_array, ^bool(size_t idx, xpc_object_t value) {
609 result[idx] = (char *)xpc_string_get_string_ptr(value);
610 return true;
611 });
612
613 *list = result;
614 return 0;
615 }
616
617 static int
618 _extract_string_from_xpc_dict(xpc_object_t reply, const char *key, const char **str)
619 {
620 xpc_object_t value = xpc_dictionary_get_value(reply, key);
621 const char *result;
622
623 if (value == NULL) return -1;
624
625 if (xpc_get_type(value) != XPC_TYPE_STRING) return -1;
626
627 result = xpc_string_get_string_ptr(value);
628 if (result == NULL) return -1;
629
630 *str = result;
631 return 0;
632 }
633
634 static int
635 _extract_uint32_from_xpc_dict(xpc_object_t reply, const char *key, uint32_t *val32)
636 {
637 xpc_object_t value = xpc_dictionary_get_value(reply, key);
638 return _extract_uint32_from_xpc_object(value, val32);
639 }
640
641 #pragma mark -
642
643 /*
644 * user schema
645 *
646 * name : string
647 * passwd : string
648 * uid : uint32
649 * gid : uint32
650 * gecos : string
651 * dir : string
652 * shell : string
653 */
654
655 static si_item_t *
656 _extract_user_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
657 {
658 struct passwd tmp;
659 int i = 0;
660
661 if (xpc_array_get_count(reply) < 7) return NULL;
662
663 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.pw_name)) return NULL;
664 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.pw_passwd)) return NULL;
665 if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.pw_uid)) return NULL;
666 if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.pw_gid)) return NULL;
667 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.pw_gecos)) return NULL;
668 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.pw_dir)) return NULL;
669 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.pw_shell)) return NULL;
670
671 /* default values */
672 tmp.pw_change = (time_t)0;
673 tmp.pw_expire = (time_t)0;
674 tmp.pw_class = (char *)"";
675
676 return (si_item_t *)LI_ils_create("L4488ss44LssssL", (unsigned long)si, CATEGORY_USER, 1, valid_global, valid_cat, tmp.pw_name, tmp.pw_passwd, tmp.pw_uid, tmp.pw_gid, tmp.pw_change, tmp.pw_class, tmp.pw_gecos, tmp.pw_dir, tmp.pw_shell, tmp.pw_expire);
677 }
678
679 static si_item_t *
680 _extract_user_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
681 {
682 __block struct passwd tmp;
683 __block int status = 0;
684 __block int parts = 3;
685
686 tmp.pw_name = (char *)"";
687 tmp.pw_passwd = (char *)"*";
688 tmp.pw_uid = (uid_t)0;
689 tmp.pw_gid = (gid_t)0;
690 tmp.pw_change = (time_t)0;
691 tmp.pw_expire = (time_t)0;
692 tmp.pw_class = (char *)"";
693 tmp.pw_gecos = (char *)"";
694 tmp.pw_dir = (char *)"/var/empty";
695 tmp.pw_shell = (char *)"/usr/bin/false";
696
697 xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
698 if (key == NULL) return true;
699 else if (!strcmp(key, "pw_name"))
700 {
701 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.pw_name);
702 if (status == 0) parts--;
703 }
704 else if (!strcmp(key, "pw_passwd"))
705 {
706 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.pw_passwd);
707 /* no parts check - this value is optional */
708 }
709 else if (!strcmp(key, "pw_uid"))
710 {
711 status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.pw_uid);
712 if (status == 0) parts--;
713 }
714 else if (!strcmp(key, "pw_gid"))
715 {
716 status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.pw_gid);
717 if (status == 0) parts--;
718 }
719 else if (!strcmp(key, "pw_change"))
720 {
721 status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.pw_change);
722 /* no parts check - this value is optional */
723 }
724 else if (!strcmp(key, "pw_expire"))
725 {
726 status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.pw_expire);
727 /* no parts check - this value is optional */
728 }
729 else if (!strcmp(key, "pw_class"))
730 {
731 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.pw_class);
732 /* no parts check - this value is optional */
733 }
734 else if (!strcmp(key, "pw_gecos"))
735 {
736 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.pw_gecos);
737 /* no parts check - this value is optional */
738 }
739 else if (!strcmp(key, "pw_dir"))
740 {
741 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.pw_dir);
742 /* no parts check - this value is optional */
743 }
744 else if (!strcmp(key, "pw_shell"))
745 {
746 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.pw_shell);
747 /* no parts check - this value is optional */
748 }
749 return true;
750 });
751
752 if ((status != 0) || (parts != 0)) return NULL;
753
754 return (si_item_t *)LI_ils_create("L4488ss44LssssL", (unsigned long)si, CATEGORY_USER, 1, valid_global, valid_cat, tmp.pw_name, tmp.pw_passwd, tmp.pw_uid, tmp.pw_gid, tmp.pw_change, tmp.pw_class, tmp.pw_gecos, tmp.pw_dir, tmp.pw_shell, tmp.pw_expire);
755 }
756
757 static si_item_t *
758 _extract_user(si_mod_t *si, xpc_object_t reply, __unused const void *ignored, uint64_t valid_global, uint64_t valid_cat)
759 {
760 xpc_type_t type;
761
762 if (si == NULL) return NULL;
763 if (reply == NULL) return NULL;
764
765 type = xpc_get_type(reply);
766
767 if (type == XPC_TYPE_ARRAY) return _extract_user_array(si, reply, valid_global, valid_cat);
768 else if (type == XPC_TYPE_DICTIONARY) return _extract_user_dict(si, reply, valid_global, valid_cat);
769
770 return NULL;
771 }
772
773 /*
774 * group schema
775 *
776 * name : string
777 * gid : uint32
778 * optional members : array of string
779 *
780 */
781
782 static si_item_t *
783 _extract_group_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
784 {
785 si_item_t *item;
786 struct group tmp;
787 int i = 0;
788 int arraycount = xpc_array_get_count(reply);
789
790 if ((arraycount < 2) || (arraycount > 3)) return NULL;
791
792 memset(&tmp, 0, sizeof(tmp));
793
794 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.gr_name)) return NULL;
795 if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.gr_gid)) return NULL;
796
797 if (arraycount == 3)
798 {
799 if (0 != _extract_string_list_from_xpc_array_index(reply, i++, NULL, (char ***)&tmp.gr_mem)) return NULL;
800 }
801
802 /* default value */
803 tmp.gr_passwd = (char *)"*";
804
805 item = (si_item_t *) LI_ils_create("L4488ss4*", (unsigned long)si, CATEGORY_GROUP, 1, valid_global, valid_cat, tmp.gr_name, tmp.gr_passwd, tmp.gr_gid, tmp.gr_mem);
806
807 free(tmp.gr_mem);
808
809 return item;
810 }
811
812 static si_item_t *
813 _extract_group_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
814 {
815 si_item_t *item;
816 __block struct group tmp;
817 __block int status = 0;
818 __block int parts = 2;
819
820 tmp.gr_name = (char *)"";
821 tmp.gr_passwd = (char *)"*";
822 tmp.gr_gid = (gid_t)0;
823 tmp.gr_mem = NULL;
824
825 xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
826 if (key == NULL) return true;
827 else if (!strcmp(key, "gr_name"))
828 {
829 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.gr_name);
830 if (status == 0) parts--;
831 }
832 else if (!strcmp(key, "gr_passwd"))
833 {
834 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.gr_passwd);
835 /* no parts check - this value is optional */
836 }
837 else if (!strcmp(key, "gr_gid"))
838 {
839 status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.gr_gid);
840 if (status == 0) parts--;
841 }
842 else if (!strcmp(key, "gr_mem"))
843 {
844 status |= _extract_string_list_from_xpc_array(value, NULL, (char ***)&tmp.gr_mem);
845 /* no parts check - this value is optional */
846 }
847 return true;
848 });
849
850 if ((status != 0) || (parts != 0))
851 {
852 free(tmp.gr_mem);
853 return NULL;
854 }
855
856 item = (si_item_t *) LI_ils_create("L4488ss4*", (unsigned long)si, CATEGORY_GROUP, 1, valid_global, valid_cat, tmp.gr_name, tmp.gr_passwd, tmp.gr_gid, tmp.gr_mem);
857
858 free(tmp.gr_mem);
859
860 return item;
861 }
862
863 static si_item_t *
864 _extract_group(si_mod_t *si, xpc_object_t reply, __unused const void *ignored, uint64_t valid_global, uint64_t valid_cat)
865 {
866 xpc_type_t type;
867
868 if (si == NULL) return NULL;
869 if (reply == NULL) return NULL;
870
871 type = xpc_get_type(reply);
872 if (type == XPC_TYPE_ARRAY) return _extract_group_array(si, reply, valid_global, valid_cat);
873 else if (type == XPC_TYPE_DICTIONARY) return _extract_group_dict(si, reply, valid_global, valid_cat);
874
875 return NULL;
876 }
877
878 /*
879 * netgroup schema
880 *
881 * host : string
882 * user : string
883 * domain : string
884 *
885 */
886 static si_item_t *
887 _extract_netgroup_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
888 {
889 const char *host, *user, *domain;
890 int i = 0;
891
892 if (xpc_array_get_count(reply) != 3) return NULL;
893
894 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&host)) return NULL;
895 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&user)) return NULL;
896 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&domain)) return NULL;
897
898 return (si_item_t *)LI_ils_create("L4488sss", (unsigned long)si, CATEGORY_NETGROUP, 1, valid_global, valid_cat, host, user, domain);
899 }
900
901 static si_item_t *
902 _extract_netgroup_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
903 {
904 __block const char *host = "";
905 __block const char *user = "";
906 __block const char *domain = "";
907 __block int status = 0;
908 __block int parts = 3;
909
910 xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
911 if (key == NULL) return true;
912 else if (!strcmp(key, "host"))
913 {
914 status |= _extract_string_from_xpc_object(value, (const char **)&host);
915 if (status == 0) parts--;
916 }
917 else if (!strcmp(key, "user"))
918 {
919 status |= _extract_string_from_xpc_object(value, (const char **)&user);
920 if (status == 0) parts--;
921 }
922 else if (!strcmp(key, "domain"))
923 {
924 status |= _extract_string_from_xpc_object(value, (const char **)&domain);
925 if (status == 0) parts--;
926 }
927 return true;
928 });
929
930 if ((status != 0) || (parts != 0)) return NULL;
931
932 return (si_item_t *)LI_ils_create("L4488sss", (unsigned long)si, CATEGORY_NETGROUP, 1, valid_global, valid_cat, host, user, domain);
933 }
934
935 static si_item_t *
936 _extract_netgroup(si_mod_t *si, xpc_object_t reply, const void *ignored, uint64_t valid_global, uint64_t valid_cat)
937 {
938 xpc_type_t type;
939
940 if (si == NULL) return NULL;
941 if (reply == NULL) return NULL;
942
943 type = xpc_get_type(reply);
944 if (type == XPC_TYPE_ARRAY) return _extract_netgroup_array(si, reply, valid_global, valid_cat);
945 else if (type == XPC_TYPE_DICTIONARY) return _extract_netgroup_dict(si, reply, valid_global, valid_cat);
946
947 return NULL;
948 }
949
950 /*
951 * alias schema
952 *
953 * name : string
954 * local : uint32
955 * optional members : array of string
956 *
957 */
958
959 static si_item_t *
960 _extract_alias_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
961 {
962 si_item_t *item;
963 struct aliasent tmp;
964 int i = 0;
965 int arraycount = xpc_array_get_count(reply);
966
967 if ((arraycount < 2) || (arraycount > 3)) return NULL;
968
969 memset(&tmp, 0, sizeof(tmp));
970
971 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.alias_name)) return NULL;
972 if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.alias_local)) return NULL;
973
974 if (arraycount == 3)
975 {
976 if (0 != _extract_string_list_from_xpc_array_index(reply, i++, NULL, (char ***)&tmp.alias_members)) return NULL;
977 }
978
979 item = (si_item_t *)LI_ils_create("L4488s4*4", (unsigned long)si, CATEGORY_ALIAS, 1, valid_global, valid_cat, tmp.alias_name, tmp.alias_members_len, tmp.alias_members, tmp.alias_local);
980
981 free(tmp.alias_members);
982
983 return item;
984 }
985
986 static si_item_t *
987 _extract_alias_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
988 {
989 si_item_t *item;
990 __block struct aliasent tmp;
991 __block int status = 0;
992 __block int parts = 2;
993
994 tmp.alias_name = (char *)"";
995 tmp.alias_local = 0;
996 tmp.alias_members = NULL;
997 tmp.alias_members_len = 0;
998
999 xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
1000 if (key == NULL) return true;
1001 else if (!strcmp(key, "alias_name"))
1002 {
1003 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.alias_name);
1004 if (status == 0) parts--;
1005 }
1006 else if (!strcmp(key, "alias_local"))
1007 {
1008 status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.alias_local);
1009 if (status == 0) parts--;
1010 }
1011 else if (!strcmp(key, "alias_members"))
1012 {
1013 status |= _extract_string_list_from_xpc_array(value, &tmp.alias_members_len, (char ***)&tmp.alias_members);
1014 /* no parts check - this value is optional */
1015 }
1016 return true;
1017 });
1018
1019 if ((status != 0) || (parts != 0))
1020 {
1021 free(tmp.alias_members);
1022 return NULL;
1023 }
1024
1025 item = (si_item_t *)LI_ils_create("L4488s4*4", (unsigned long)si, CATEGORY_ALIAS, 1, valid_global, valid_cat, tmp.alias_name, tmp.alias_members_len, tmp.alias_members, tmp.alias_local);
1026
1027 free(tmp.alias_members);
1028
1029 return item;
1030 }
1031
1032 static si_item_t *
1033 _extract_alias(si_mod_t *si, xpc_object_t reply, __unused const void *ignored, uint64_t valid_global, uint64_t valid_cat)
1034 {
1035 xpc_type_t type;
1036
1037 if (si == NULL) return NULL;
1038 if (reply == NULL) return NULL;
1039
1040 type = xpc_get_type(reply);
1041 if (type == XPC_TYPE_ARRAY) return _extract_alias_array(si, reply, valid_global, valid_cat);
1042 else if (type == XPC_TYPE_DICTIONARY) return _extract_alias_dict(si, reply, valid_global, valid_cat);
1043
1044 return NULL;
1045 }
1046
1047 /*
1048 * network schema
1049 *
1050 * name : string
1051 * net : uint32
1052 * optional aliases : array of string
1053 *
1054 */
1055
1056 static si_item_t *
1057 _extract_network_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1058 {
1059 si_item_t *item;
1060 struct netent tmp;
1061 int i = 0;
1062 int arraycount = xpc_array_get_count(reply);
1063
1064 if ((arraycount < 2) || (arraycount > 3)) return NULL;
1065
1066 memset(&tmp, 0, sizeof(tmp));
1067
1068 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.n_name)) return NULL;
1069 if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.n_net)) return NULL;
1070
1071 if (arraycount == 3)
1072 {
1073 if (0 != _extract_string_list_from_xpc_array_index(reply, i++, NULL, (char ***)&tmp.n_aliases)) return NULL;
1074 }
1075
1076 /* default value */
1077 tmp.n_addrtype = AF_INET;
1078
1079 item = (si_item_t *)LI_ils_create("L4488s*44", (unsigned long)si, CATEGORY_NETWORK, 1, valid_global, valid_cat, tmp.n_name, tmp.n_aliases, tmp.n_addrtype, tmp.n_net);
1080
1081 free(tmp.n_aliases);
1082
1083 return item;
1084 }
1085
1086 static si_item_t *
1087 _extract_network_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1088 {
1089 si_item_t *item;
1090 __block struct netent tmp;
1091 __block int status = 0;
1092 __block int parts = 2;
1093
1094 if (si == NULL) return NULL;
1095 if (reply == NULL) return NULL;
1096
1097 tmp.n_name = (char *)"";
1098 tmp.n_aliases = NULL;
1099 tmp.n_net = 0;
1100
1101 /* default value */
1102 tmp.n_addrtype = AF_INET;
1103
1104 xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
1105 if (key == NULL) return true;
1106 else if (!strcmp(key, "n_name"))
1107 {
1108 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.n_name);
1109 if (status == 0) parts--;
1110 }
1111 else if (!strcmp(key, "n_aliases"))
1112 {
1113 status |= _extract_string_list_from_xpc_array(value, NULL, (char ***)&tmp.n_aliases);
1114 /* no parts check - this value is optional */
1115 }
1116 else if (!strcmp(key, "n_net"))
1117 {
1118 status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.n_net);
1119 if (status == 0) parts--;
1120 }
1121 return true;
1122 });
1123
1124 if ((status != 0) || (parts != 0))
1125 {
1126 free(tmp.n_aliases);
1127 return NULL;
1128 }
1129
1130 item = (si_item_t *)LI_ils_create("L4488s*44", (unsigned long)si, CATEGORY_NETWORK, 1, valid_global, valid_cat, tmp.n_name, tmp.n_aliases, tmp.n_addrtype, tmp.n_net);
1131
1132 free(tmp.n_aliases);
1133
1134 return item;
1135 }
1136
1137 static si_item_t *
1138 _extract_network(si_mod_t *si, xpc_object_t reply, __unused const void *ignored, uint64_t valid_global, uint64_t valid_cat)
1139 {
1140 xpc_type_t type;
1141
1142 if (si == NULL) return NULL;
1143 if (reply == NULL) return NULL;
1144
1145 type = xpc_get_type(reply);
1146 if (type == XPC_TYPE_ARRAY) return _extract_network_array(si, reply, valid_global, valid_cat);
1147 else if (type == XPC_TYPE_DICTIONARY) return _extract_network_dict(si, reply, valid_global, valid_cat);
1148
1149 return NULL;
1150 }
1151
1152 /*
1153 * service schema
1154 *
1155 * name : string
1156 * port : uint32
1157 * proto : string
1158 * optional aliases : array of string
1159 *
1160 */
1161
1162 static si_item_t *
1163 _extract_service_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1164 {
1165 si_item_t *item;
1166 struct servent tmp;
1167 int i = 0;
1168 int arraycount = xpc_array_get_count(reply);
1169
1170 if ((arraycount < 3) || (arraycount > 4)) return NULL;
1171
1172 memset(&tmp, 0, sizeof(tmp));
1173
1174 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.s_name)) return NULL;
1175 if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.s_port)) return NULL;
1176 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.s_proto)) return NULL;
1177
1178 if (arraycount == 4)
1179 {
1180 if (0 != _extract_string_list_from_xpc_array_index(reply, i++, NULL, (char ***)&tmp.s_aliases)) return NULL;
1181 }
1182
1183 item = (si_item_t *)LI_ils_create("L4488s*4s", (unsigned long)si, CATEGORY_SERVICE, 1, valid_global, valid_cat, tmp.s_name, tmp.s_aliases, tmp.s_port, tmp.s_proto);
1184
1185 free(tmp.s_aliases);
1186
1187 return item;
1188 }
1189
1190 static si_item_t *
1191 _extract_service_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1192 {
1193 si_item_t *item;
1194 __block struct servent tmp;
1195 __block int status = 0;
1196 __block int parts = 3;
1197
1198 if (si == NULL) return NULL;
1199 if (reply == NULL) return NULL;
1200
1201 tmp.s_name = (char *)"";
1202 tmp.s_aliases = NULL;
1203 tmp.s_port = 0;
1204 tmp.s_proto = (char *)"";
1205
1206 xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
1207 if (key == NULL) return true;
1208 else if (!strcmp(key, "s_name"))
1209 {
1210 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.s_name);
1211 if (status == 0) parts--;
1212 }
1213 else if (!strcmp(key, "s_aliases"))
1214 {
1215 status |= _extract_string_list_from_xpc_array(value, NULL, (char ***)&tmp.s_aliases);
1216 /* no parts check - this value is optional */
1217 }
1218 else if (!strcmp(key, "s_port"))
1219 {
1220 uint32_t v32;
1221 status |= _extract_uint32_from_xpc_object(value, &v32);
1222 if (status == 0)
1223 {
1224 tmp.s_port = (unsigned int)htons(v32); // ugh
1225 parts--;
1226 }
1227 }
1228 else if (!strcmp(key, "s_proto"))
1229 {
1230 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.s_proto);
1231 if (status == 0) parts--;
1232 }
1233 return true;
1234 });
1235
1236 if ((status != 0) || (parts != 0))
1237 {
1238 free(tmp.s_aliases);
1239 return NULL;
1240 }
1241
1242 item = (si_item_t *)LI_ils_create("L4488s*4s", (unsigned long)si, CATEGORY_SERVICE, 1, valid_global, valid_cat, tmp.s_name, tmp.s_aliases, tmp.s_port, tmp.s_proto);
1243
1244 free(tmp.s_aliases);
1245
1246 return item;
1247 }
1248
1249 static si_item_t *
1250 _extract_service(si_mod_t *si, xpc_object_t reply, __unused const void *ignored, uint64_t valid_global, uint64_t valid_cat)
1251 {
1252 xpc_type_t type;
1253
1254 if (si == NULL) return NULL;
1255 if (reply == NULL) return NULL;
1256
1257 type = xpc_get_type(reply);
1258 if (type == XPC_TYPE_ARRAY) return _extract_service_array(si, reply, valid_global, valid_cat);
1259 else if (type == XPC_TYPE_DICTIONARY) return _extract_service_dict(si, reply, valid_global, valid_cat);
1260
1261 return NULL;
1262 }
1263
1264 /*
1265 * protocol schema
1266 *
1267 * name : string
1268 * proto : uint32
1269 * optional aliases : array of string
1270 *
1271 */
1272 static si_item_t *
1273 _extract_protocol_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1274 {
1275 si_item_t *item;
1276 struct protoent tmp;
1277 int i = 0;
1278 int arraycount = xpc_array_get_count(reply);
1279
1280 if ((arraycount < 2) || (arraycount > 3)) return NULL;
1281
1282 memset(&tmp, 0, sizeof(tmp));
1283
1284 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.p_name)) return NULL;
1285 if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.p_proto)) return NULL;
1286
1287 if (arraycount == 3)
1288 {
1289 if (0 != _extract_string_list_from_xpc_array_index(reply, i++, NULL, (char ***)&tmp.p_aliases)) return NULL;
1290 }
1291
1292 item = (si_item_t *)LI_ils_create("L4488s*4", (unsigned long)si, CATEGORY_PROTOCOL, 1, valid_global, valid_cat, tmp.p_name, tmp.p_aliases, tmp.p_proto);
1293
1294 free(tmp.p_aliases);
1295
1296 return item;
1297 }
1298
1299 static si_item_t *
1300 _extract_protocol_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1301 {
1302 si_item_t *item;
1303 __block struct protoent tmp;
1304 __block int status = 0;
1305 __block int parts = 2;
1306
1307 tmp.p_name = (char *)"";
1308 tmp.p_proto = 0;
1309 tmp.p_aliases = NULL;
1310
1311 xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
1312 if (key == NULL) return true;
1313 else if (!strcmp(key, "p_name"))
1314 {
1315 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.p_name);
1316 if (status == 0) parts--;
1317 }
1318 else if (!strcmp(key, "p_proto"))
1319 {
1320 status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.p_proto);
1321 if (status == 0) parts--;
1322 }
1323 else if (!strcmp(key, "p_aliases"))
1324 {
1325 status |= _extract_string_list_from_xpc_array(value, NULL, (char ***)&tmp.p_aliases);
1326 /* no parts check - this value is optional */
1327 }
1328 return true;
1329 });
1330
1331 if ((status != 0) || (parts != 0))
1332 {
1333 free(tmp.p_aliases);
1334 return NULL;
1335 }
1336
1337 item = (si_item_t *)LI_ils_create("L4488s*4", (unsigned long)si, CATEGORY_PROTOCOL, 1, valid_global, valid_cat, tmp.p_name, tmp.p_aliases, tmp.p_proto);
1338
1339 free(tmp.p_aliases);
1340
1341 return item;
1342 }
1343
1344 static si_item_t *
1345 _extract_protocol(si_mod_t *si, xpc_object_t reply, __unused const void *ignored, uint64_t valid_global, uint64_t valid_cat)
1346 {
1347 xpc_type_t type;
1348
1349 if (si == NULL) return NULL;
1350 if (reply == NULL) return NULL;
1351
1352 type = xpc_get_type(reply);
1353 if (type == XPC_TYPE_ARRAY) return _extract_protocol_array(si, reply, valid_global, valid_cat);
1354 else if (type == XPC_TYPE_DICTIONARY) return _extract_protocol_dict(si, reply, valid_global, valid_cat);
1355
1356 return NULL;
1357 }
1358
1359 /*
1360 * rpc schema
1361 *
1362 * name : string
1363 * number : uint32
1364 * optional aliases : array of string
1365 *
1366 */
1367
1368 static si_item_t *
1369 _extract_rpc_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1370 {
1371 si_item_t *item;
1372 struct rpcent tmp;
1373 int i = 0;
1374 int arraycount = xpc_array_get_count(reply);
1375
1376 if ((arraycount < 2) || (arraycount > 3)) return NULL;
1377
1378 memset(&tmp, 0, sizeof(tmp));
1379
1380 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.r_name)) return NULL;
1381 if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.r_number)) return NULL;
1382
1383 if (arraycount == 3)
1384 {
1385 if (0 != _extract_string_list_from_xpc_array_index(reply, i++, NULL, (char ***)&tmp.r_aliases)) return NULL;
1386 }
1387
1388 item = (si_item_t *)LI_ils_create("L4488s*4", (unsigned long)si, CATEGORY_RPC, 1, valid_global, valid_cat, tmp.r_name, tmp.r_aliases, tmp.r_number);
1389
1390 free(tmp.r_aliases);
1391
1392 return item;
1393 }
1394
1395 static si_item_t *
1396 _extract_rpc_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1397 {
1398 si_item_t *item;
1399 __block struct rpcent tmp;
1400 __block int status = 0;
1401 __block int parts = 2;
1402
1403 tmp.r_name = (char *)"";
1404 tmp.r_number = 0;
1405 tmp.r_aliases = NULL;
1406
1407 xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
1408 if (key == NULL) return true;
1409 else if (!strcmp(key, "r_name"))
1410 {
1411 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.r_name);
1412 if (status == 0) parts--;
1413 }
1414 else if (!strcmp(key, "r_number"))
1415 {
1416 status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.r_number);
1417 if (status == 0) parts--;
1418 }
1419 else if (!strcmp(key, "r_aliases"))
1420 {
1421 status |= _extract_string_list_from_xpc_array(value, NULL, (char ***)&tmp.r_aliases);
1422 /* no parts check - this value is optional */
1423 }
1424 return true;
1425 });
1426
1427 if ((status != 0) || (parts != 0))
1428 {
1429 free(tmp.r_aliases);
1430 return NULL;
1431 }
1432
1433 item = (si_item_t *)LI_ils_create("L4488s*4", (unsigned long)si, CATEGORY_RPC, 1, valid_global, valid_cat, tmp.r_name, tmp.r_aliases, tmp.r_number);
1434
1435 free(tmp.r_aliases);
1436
1437 return item;
1438 }
1439
1440 static si_item_t *
1441 _extract_rpc(si_mod_t *si, xpc_object_t reply, __unused const void *ignored, uint64_t valid_global, uint64_t valid_cat)
1442 {
1443 xpc_type_t type;
1444
1445 if (si == NULL) return NULL;
1446 if (reply == NULL) return NULL;
1447
1448 type = xpc_get_type(reply);
1449 if (type == XPC_TYPE_ARRAY) return _extract_rpc_array(si, reply, valid_global, valid_cat);
1450 else if (type == XPC_TYPE_DICTIONARY) return _extract_rpc_dict(si, reply, valid_global, valid_cat);
1451
1452 return NULL;
1453 }
1454
1455 /*
1456 * fstab schema
1457 *
1458 * file : string
1459 * spec : string
1460 * freq : uint32
1461 * passno : uint32
1462 * mntopts : string
1463 * type : string
1464 * vfstype : string
1465 */
1466
1467 static si_item_t *
1468 _extract_fstab_array(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1469 {
1470 __block struct fstab tmp;
1471 int i = 0;
1472
1473 if (xpc_array_get_count(reply) != 7) return NULL;
1474
1475 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.fs_file)) return NULL;
1476 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.fs_spec)) return NULL;
1477 if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.fs_freq)) return NULL;
1478 if (0 != _extract_uint32_from_xpc_array_index(reply, i++, (uint32_t *)&tmp.fs_passno)) return NULL;
1479 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.fs_mntops)) return NULL;
1480 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.fs_type)) return NULL;
1481 if (0 != _extract_string_from_xpc_array_index(reply, i++, (const char **)&tmp.fs_vfstype)) return NULL;
1482
1483 return (si_item_t *)LI_ils_create("L4488sssss44", (unsigned long)si, CATEGORY_FS, 1, valid_global, valid_cat, tmp.fs_spec, tmp.fs_file, tmp.fs_vfstype, tmp.fs_mntops, tmp.fs_type, tmp.fs_freq, tmp.fs_passno);
1484 }
1485
1486 static si_item_t *
1487 _extract_fstab_dict(si_mod_t *si, xpc_object_t reply, uint64_t valid_global, uint64_t valid_cat)
1488 {
1489 __block struct fstab tmp;
1490 __block int status = 0;
1491 __block int parts = 7;
1492
1493 tmp.fs_file = NULL;
1494 tmp.fs_spec = (char *)"";
1495 tmp.fs_freq = 0;
1496 tmp.fs_passno = 0;
1497 tmp.fs_mntops = (char *)"";
1498 tmp.fs_type = (char *)"";
1499 tmp.fs_vfstype = (char *)"";
1500
1501 xpc_dictionary_apply(reply, ^bool(const char *key, xpc_object_t value) {
1502 if (key == NULL) return true;
1503 else if (!strcmp(key, "fs_file"))
1504 {
1505 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.fs_file);
1506 if (status == 0) parts--;
1507 }
1508 else if (!strcmp(key, "fs_spec"))
1509 {
1510 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.fs_spec);
1511 if (status == 0) parts--;
1512 }
1513 else if (!strcmp(key, "fs_freq"))
1514 {
1515 status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.fs_freq);
1516 if (status == 0) parts--;
1517 }
1518 else if (!strcmp(key, "fs_passno"))
1519 {
1520 status |= _extract_uint32_from_xpc_object(value, (uint32_t *)&tmp.fs_passno);
1521 if (status == 0) parts--;
1522 }
1523 else if (!strcmp(key, "fs_mntops"))
1524 {
1525 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.fs_mntops);
1526 if (status == 0) parts--;
1527 }
1528 else if (!strcmp(key, "fs_type"))
1529 {
1530 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.fs_type);
1531 if (status == 0) parts--;
1532 }
1533 else if (!strcmp(key, "fs_vfstype"))
1534 {
1535 status |= _extract_string_from_xpc_object(value, (const char **)&tmp.fs_vfstype);
1536 if (status == 0) parts--;
1537 }
1538 return true;
1539 });
1540
1541 if ((status != 0) || (parts != 0)) return NULL;
1542
1543 return (si_item_t *)LI_ils_create("L4488sssss44", (unsigned long)si, CATEGORY_FS, 1, valid_global, valid_cat, tmp.fs_spec, tmp.fs_file, tmp.fs_vfstype, tmp.fs_mntops, tmp.fs_type, tmp.fs_freq, tmp.fs_passno);
1544 }
1545
1546 static si_item_t *
1547 _extract_fstab(si_mod_t *si, xpc_object_t reply, __unused const void *ignored, uint64_t valid_global, uint64_t valid_cat)
1548 {
1549 xpc_type_t type;
1550
1551 if (si == NULL) return NULL;
1552 if (reply == NULL) return NULL;
1553
1554 type = xpc_get_type(reply);
1555 if (type == XPC_TYPE_ARRAY) return _extract_fstab_array(si, reply, valid_global, valid_cat);
1556 else if (type == XPC_TYPE_DICTIONARY) return _extract_fstab_dict(si, reply, valid_global, valid_cat);
1557
1558 return NULL;
1559 }
1560
1561 static si_item_t *
1562 _extract_mac_mac(si_mod_t *si, xpc_object_t reply, const void *extra, uint64_t valid_global, uint64_t valid_cat)
1563 {
1564 xpc_type_t type;
1565 char *cmac;
1566 const char *value = NULL;
1567 si_item_t *out;
1568
1569 if (si == NULL) return NULL;
1570 if (reply == NULL) return NULL;
1571 if (extra == NULL) return NULL;
1572
1573 type = xpc_get_type(reply);
1574 if (type == XPC_TYPE_ARRAY)
1575 {
1576 if (xpc_array_get_count(reply) >= 1)
1577 {
1578 if (0 != _extract_string_from_xpc_array_index(reply, 0, (const char **)&value)) return NULL;
1579 }
1580 }
1581 else if (type == XPC_TYPE_DICTIONARY)
1582 {
1583 if (0 != _extract_string_from_xpc_dict(reply, "mac", &value)) return NULL;
1584 }
1585
1586 if (value == NULL || value[0] == '\0') return NULL;
1587
1588 cmac = si_standardize_mac_address(value);
1589 if (cmac == NULL) return NULL;
1590
1591 out = (si_item_t *)LI_ils_create("L4488ss", (unsigned long)si, CATEGORY_MAC, 1, valid_global, valid_cat, extra, cmac);
1592
1593 free(cmac);
1594
1595 return out;
1596 }
1597
1598 static si_item_t *
1599 _extract_mac_name(si_mod_t *si, xpc_object_t reply, const void *extra, uint64_t valid_global, uint64_t valid_cat)
1600 {
1601 xpc_type_t type;
1602 const char *name = NULL;
1603 si_item_t *out;
1604
1605 if (si == NULL) return NULL;
1606 if (reply == NULL) return NULL;
1607 if (extra == NULL) return NULL;
1608
1609 type = xpc_get_type(reply);
1610 if (type == XPC_TYPE_ARRAY)
1611 {
1612 if (xpc_array_get_count(reply) >= 1)
1613 {
1614 if (0 != _extract_string_from_xpc_array_index(reply, 0, (const char **)&name )) return NULL;
1615 }
1616 }
1617 else if (type == XPC_TYPE_DICTIONARY)
1618 {
1619 if (0 != _extract_string_from_xpc_dict(reply, "name", &name)) return NULL;
1620 }
1621
1622 if (name == NULL) return NULL;
1623
1624 out = (si_item_t *)LI_ils_create("L4488ss", (unsigned long)si, CATEGORY_MAC, 1, valid_global, valid_cat, name, extra);
1625
1626 return out;
1627 }
1628
1629 #pragma mark -
1630
1631 static si_item_t *
1632 ds_user_byname(si_mod_t *si, const char *name)
1633 {
1634 xpc_object_t payload;
1635 si_item_t *item;
1636
1637 if (!_od_running()) return NULL;
1638
1639 LI_OS_ACTIVITY("Retrieve User by Name");
1640
1641 payload = _xpc_query_key_string("name", name);
1642 if (payload == NULL) return NULL;
1643
1644 item = _ds_item(si, CATEGORY_USER, "getpwnam", NULL, _extract_user, payload);
1645
1646 xpc_release(payload);
1647 return item;
1648 }
1649
1650 static si_item_t *
1651 ds_user_byuid(si_mod_t *si, uid_t uid)
1652 {
1653 xpc_object_t payload;
1654 si_item_t *item;
1655
1656 if (!_od_running()) return NULL;
1657
1658 LI_OS_ACTIVITY("Retrieve User by ID");
1659
1660 payload = _xpc_query_key_id("uid", uid);
1661 if (payload == NULL) return NULL;
1662
1663 item = _ds_item(si, CATEGORY_USER, "getpwuid", NULL, _extract_user, payload);
1664
1665 xpc_release(payload);
1666 return item;
1667 }
1668
1669 static si_item_t *
1670 ds_user_byuuid(si_mod_t *si, uuid_t uuid)
1671 {
1672 xpc_object_t payload;
1673 si_item_t *item;
1674
1675 if (!_od_running()) return NULL;
1676
1677 LI_OS_ACTIVITY("Retrieve User by UUID");
1678
1679 payload = _xpc_query_key_uuid("uuid", uuid);
1680 if (payload == NULL) return NULL;
1681
1682 item = _ds_item(si, CATEGORY_USER, "getpwuuid", NULL, _extract_user, payload);
1683
1684 xpc_release(payload);
1685 return item;
1686 }
1687
1688 static si_list_t *
1689 ds_user_all(si_mod_t *si)
1690 {
1691 LI_OS_ACTIVITY("Performance Impact - Enumerate all users");
1692
1693 return _ds_list(si, CATEGORY_USER, "getpwent", NULL, _extract_user);
1694 }
1695
1696 static si_item_t *
1697 ds_group_byname(si_mod_t *si, const char *name)
1698 {
1699 xpc_object_t payload;
1700 si_item_t *item;
1701
1702 if (!_od_running()) return NULL;
1703
1704 LI_OS_ACTIVITY("Retrieve Group by Name");
1705
1706 payload = _xpc_query_key_string("name", name);
1707 if (payload == NULL) return NULL;
1708
1709 item = _ds_item(si, CATEGORY_GROUP, "getgrnam", NULL, _extract_group, payload);
1710
1711 xpc_release(payload);
1712 return item;
1713 }
1714
1715 static si_item_t *
1716 ds_group_bygid(si_mod_t *si, gid_t gid)
1717 {
1718 xpc_object_t payload;
1719 si_item_t *item;
1720
1721 if (!_od_running()) return NULL;
1722
1723 LI_OS_ACTIVITY("Retrieve Group by ID");
1724
1725 payload = _xpc_query_key_id("gid", gid);
1726 if (payload == NULL) return NULL;
1727
1728 item = _ds_item(si, CATEGORY_GROUP, "getgrgid", NULL, _extract_group, payload);
1729
1730 xpc_release(payload);
1731 return item;
1732 }
1733
1734 static si_item_t *
1735 ds_group_byuuid(si_mod_t *si, uuid_t uuid)
1736 {
1737 xpc_object_t payload;
1738 si_item_t *item;
1739
1740 if (!_od_running()) return NULL;
1741
1742 LI_OS_ACTIVITY("Retrieve Group by UUID");
1743
1744 payload = _xpc_query_key_uuid("uuid", uuid);
1745 if (payload == NULL) return NULL;
1746
1747 item = _ds_item(si, CATEGORY_GROUP, "getgruuid", NULL, _extract_group, payload);
1748
1749 xpc_release(payload);
1750 return item;
1751 }
1752
1753 static si_list_t *
1754 ds_group_all(si_mod_t *si)
1755 {
1756 if (!_od_running()) return NULL;
1757 LI_OS_ACTIVITY("Performance Impact - Enumerate all Groups");
1758 return _ds_list(si, CATEGORY_GROUP, "getgrent", NULL, _extract_group);
1759 }
1760
1761 static si_item_t *
1762 ds_grouplist(si_mod_t *si, const char *name, uint32_t ngroups)
1763 {
1764 xpc_object_t payload, reply;
1765 si_item_t *item = NULL;
1766 os_activity_t activity;
1767
1768 if (!_od_running()) return NULL;
1769 if (name == NULL) return NULL;
1770
1771 if (ngroups > 17) {
1772 activity = os_activity_create("Performance impact - Resolve user group list (>17 groups)", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
1773 os_log(OS_LOG_DEFAULT, "Too many groups requested (%u). Can cause performance issues when network directories are involved", ngroups);
1774 } else {
1775 activity = os_activity_create("Resolve user group list", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
1776 }
1777 os_activity_scope(activity);
1778
1779 payload = xpc_dictionary_create(NULL, NULL, 0);
1780 if (payload == NULL) return NULL;
1781
1782 xpc_dictionary_set_string(payload, "name", name);
1783 xpc_dictionary_set_int64(payload, "ngroups", ngroups);
1784
1785 reply = _od_rpc_call("getgrouplist", payload, _od_xpc_pipe);
1786 if (reply != NULL) {
1787 size_t gidptrsz;
1788 const gid_t *gidptr = xpc_dictionary_get_data(reply, "groups", &gidptrsz);
1789 uint32_t count = 0;
1790 uint64_t va, vb;
1791
1792 _ds_get_validation(si, &va, &vb, CATEGORY_GROUPLIST);
1793
1794 /* see what we were sent */
1795 if (0 == _extract_uint32_from_xpc_dict(reply, "count", &count))
1796 {
1797 if (count != 0)
1798 {
1799 item = (si_item_t *)LI_ils_create("L4488s4@", (unsigned long)si, CATEGORY_GROUPLIST, 1, va, vb, name, count,
1800 gidptrsz, gidptr);
1801 }
1802 }
1803
1804 xpc_release(reply);
1805 }
1806
1807 xpc_release(payload);
1808 os_release(activity);
1809
1810 return item;
1811 }
1812
1813 static si_list_t *
1814 ds_netgroup_byname(si_mod_t *si, const char *name)
1815 {
1816 xpc_object_t payload;
1817 si_list_t *list = NULL;
1818 si_item_t *item;
1819
1820 if (!_od_running()) return NULL;
1821
1822 LI_OS_ACTIVITY("Retrieve netgroup by name");
1823
1824 payload = _xpc_query_key_string("netgroup", name);
1825 if (payload == NULL) return NULL;
1826
1827 item = _ds_item(si, CATEGORY_NETGROUP, "getnetgrent", NULL, _extract_netgroup, payload);
1828 if (item != NULL) {
1829 list = si_list_add(list, item);
1830 si_item_release(item);
1831 }
1832
1833 xpc_release(payload);
1834
1835 return list;
1836 }
1837
1838 static int
1839 ds_in_netgroup(si_mod_t *si, const char *group, const char *host, const char *user, const char *domain)
1840 {
1841 xpc_object_t payload, reply;
1842 int is_innetgr;
1843
1844 if (!_od_running()) return 0;
1845
1846 LI_OS_ACTIVITY("Match netgroup");
1847
1848 payload = xpc_dictionary_create(NULL, NULL, 0);
1849 if (payload == NULL) return 0;
1850
1851 xpc_dictionary_set_string(payload, "netgroup", (group ? group : ""));
1852 xpc_dictionary_set_string(payload, "host", (host ? host : ""));
1853 xpc_dictionary_set_string(payload, "user", (user ? user : ""));
1854 xpc_dictionary_set_string(payload, "domain", (domain ? domain : ""));
1855
1856 reply = _od_rpc_call("innetgr", payload, _od_xpc_pipe);
1857 if (reply != NULL) {
1858 is_innetgr = xpc_dictionary_get_bool(reply, OD_RPC_RESULT);
1859 xpc_release(reply);
1860 } else {
1861 is_innetgr = 0;
1862 }
1863
1864 xpc_release(payload);
1865
1866 return is_innetgr;
1867 }
1868
1869 static si_item_t *
1870 ds_alias_byname(si_mod_t *si, const char *name)
1871 {
1872 xpc_object_t payload;
1873 si_item_t *item;
1874
1875 if (!_od_running()) return NULL;
1876
1877 LI_OS_ACTIVITY("Retrieve alias by name");
1878 payload = _xpc_query_key_string("name", name);
1879 if (payload == NULL) return NULL;
1880
1881 item = _ds_item(si, CATEGORY_ALIAS, "alias_getbyname", NULL, _extract_alias, payload);
1882
1883 xpc_release(payload);
1884 return item;
1885 }
1886
1887 static si_list_t *
1888 ds_alias_all(si_mod_t *si)
1889 {
1890 if (!_od_running()) return NULL;
1891 LI_OS_ACTIVITY("Enumerate all alias entries");
1892 return _ds_list(si, CATEGORY_ALIAS, "alias_getent", NULL, _extract_alias);
1893 }
1894
1895 static si_item_t *
1896 ds_network_byname(si_mod_t *si, const char *name)
1897 {
1898 xpc_object_t payload;
1899 si_item_t *item;
1900
1901 if (!_od_running()) return NULL;
1902
1903 LI_OS_ACTIVITY("Retrieve network by name");
1904 payload = _xpc_query_key_string("name", name);
1905 if (payload == NULL) return NULL;
1906
1907 item = _ds_item(si, CATEGORY_NETWORK, "getnetbyname", NULL, _extract_network, payload);
1908
1909 xpc_release(payload);
1910 return item;
1911 }
1912
1913 static si_item_t *
1914 ds_network_byaddr(si_mod_t *si, uint32_t addr)
1915 {
1916 unsigned char f1, f2, f3;
1917 char val[64];
1918 xpc_object_t payload;
1919 si_item_t *item;
1920
1921 if (!_od_running()) return NULL;
1922
1923 LI_OS_ACTIVITY("Retrieve network by address");
1924 f1 = addr & 0xff;
1925 addr >>= 8;
1926 f2 = addr & 0xff;
1927 addr >>= 8;
1928 f3 = addr & 0xff;
1929
1930 if (f3 != 0) snprintf(val, sizeof(val), "%u.%u.%u", f3, f2, f1);
1931 else if (f2 != 0) snprintf(val, sizeof(val), "%u.%u", f2, f1);
1932 else snprintf(val, sizeof(val), "%u", f1);
1933
1934 payload = _xpc_query_key_string("net", val);
1935 if (payload == NULL) return NULL;
1936
1937 item = _ds_item(si, CATEGORY_NETWORK, "getnetbyaddr", NULL, _extract_network, payload);
1938
1939 xpc_release(payload);
1940 return item;
1941 }
1942
1943 static si_list_t *
1944 ds_network_all(si_mod_t *si)
1945 {
1946 if (!_od_running()) return NULL;
1947 LI_OS_ACTIVITY("Emumerate all network entries");
1948 return _ds_list(si, CATEGORY_NETWORK, "getnetent", NULL, _extract_network);
1949 }
1950
1951 static si_item_t *
1952 ds_service_byname(si_mod_t *si, const char *name, const char *proto)
1953 {
1954 xpc_object_t payload;
1955 si_item_t *item;
1956 struct servent *s;
1957
1958 if (!_od_running()) return NULL;
1959 if (name == NULL) name = "";
1960 if (proto == NULL) proto = "";
1961
1962 LI_OS_ACTIVITY("Retrieve service by name");
1963
1964 /* Check our local service cache (see ds_addrinfo). */
1965 item = pthread_getspecific(_ds_serv_cache_key);
1966 if (item != NULL)
1967 {
1968 s = (struct servent *)((uintptr_t)item + sizeof(si_item_t));
1969 if (string_equal(name, s->s_name)) return si_item_retain(item);
1970 }
1971
1972 payload = xpc_dictionary_create(NULL, NULL, 0);
1973 if (payload == NULL) return NULL;
1974
1975 xpc_dictionary_set_string(payload, "name", name);
1976 xpc_dictionary_set_string(payload, "proto", proto);
1977
1978 item = _ds_item(si, CATEGORY_SERVICE, "getservbyname", NULL, _extract_service, payload);
1979
1980 xpc_release(payload);
1981
1982 return item;
1983 }
1984
1985 static si_item_t *
1986 ds_service_byport(si_mod_t *si, int port, const char *proto)
1987 {
1988 xpc_object_t payload;
1989 si_item_t *item;
1990
1991 if (!_od_running()) return NULL;
1992
1993 LI_OS_ACTIVITY("Retrieve service by port");
1994
1995 payload = xpc_dictionary_create(NULL, NULL, 0);
1996 if (payload == NULL) return NULL;
1997
1998 /* swap to native order, API passes network order */
1999 xpc_dictionary_set_int64(payload, "port", ntohs(port));
2000 xpc_dictionary_set_string(payload, "proto", (proto ? proto : ""));
2001
2002 item = _ds_item(si, CATEGORY_SERVICE, "getservbyport", NULL, _extract_service, payload);
2003
2004 xpc_release(payload);
2005
2006 return item;
2007 }
2008
2009 static si_list_t *
2010 ds_service_all(si_mod_t *si)
2011 {
2012 if (!_od_running()) return NULL;
2013 LI_OS_ACTIVITY("Enumerate all services");
2014 return _ds_list(si, CATEGORY_SERVICE, "getservent", NULL, _extract_service);
2015 }
2016
2017 static si_item_t *
2018 ds_protocol_byname(si_mod_t *si, const char *name)
2019 {
2020 xpc_object_t payload;
2021 si_item_t *item;
2022
2023 if (!_od_running()) return NULL;
2024
2025 LI_OS_ACTIVITY("Retrieve protocol by name");
2026 payload = _xpc_query_key_string("name", name);
2027 if (payload == NULL) return NULL;
2028
2029 item = _ds_item(si, CATEGORY_PROTOCOL, "getprotobyname", NULL, _extract_protocol, payload);
2030
2031 xpc_release(payload);
2032 return item;
2033 }
2034
2035 static si_item_t *
2036 ds_protocol_bynumber(si_mod_t *si, int number)
2037 {
2038 xpc_object_t payload;
2039 si_item_t *item;
2040
2041 if (!_od_running()) return NULL;
2042
2043 LI_OS_ACTIVITY("Retrieve protocol by number");
2044 payload = _xpc_query_key_int("number", number);
2045 if (payload == NULL) return NULL;
2046
2047 item = _ds_item(si, CATEGORY_PROTOCOL, "getprotobynumber", NULL, _extract_protocol, payload);
2048
2049 xpc_release(payload);
2050 return item;
2051 }
2052
2053 static si_list_t *
2054 ds_protocol_all(si_mod_t *si)
2055 {
2056 LI_OS_ACTIVITY("Enumerate all protocols");
2057 return _ds_list(si, CATEGORY_PROTOCOL, "getprotoent", NULL, _extract_protocol);
2058 }
2059
2060 static si_item_t *
2061 ds_rpc_byname(si_mod_t *si, const char *name)
2062 {
2063 xpc_object_t payload;
2064 si_item_t *item;
2065
2066 if (!_od_running()) return NULL;
2067
2068 LI_OS_ACTIVITY("Retrieve RPC by name");
2069 payload = _xpc_query_key_string("name", name);
2070 if (payload == NULL) return NULL;
2071
2072 item = _ds_item(si, CATEGORY_RPC, "getrpcbyname", NULL, _extract_rpc, payload);
2073
2074 xpc_release(payload);
2075 return item;
2076 }
2077
2078 static si_item_t *
2079 ds_rpc_bynumber(si_mod_t *si, int number)
2080 {
2081 xpc_object_t payload;
2082 si_item_t *item;
2083
2084 if (!_od_running()) return NULL;
2085
2086 LI_OS_ACTIVITY("Retrieve RPC by number");
2087 payload = _xpc_query_key_int("number", number);
2088 if (payload == NULL) return NULL;
2089
2090 item = _ds_item(si, CATEGORY_RPC, "getrpcbynumber", NULL, _extract_rpc, payload);
2091
2092 xpc_release(payload);
2093 return item;
2094 }
2095
2096 static si_list_t *
2097 ds_rpc_all(si_mod_t *si)
2098 {
2099 LI_OS_ACTIVITY("Enumerate all RPC entries");
2100 return _ds_list(si, CATEGORY_RPC, "getrpcent", NULL, _extract_rpc);
2101 }
2102
2103 static si_item_t *
2104 ds_fs_byspec(si_mod_t *si, const char *name)
2105 {
2106 xpc_object_t payload;
2107 si_item_t *item;
2108
2109 if (!_od_running()) return NULL;
2110
2111 LI_OS_ACTIVITY("Lookup FS entry by spec");
2112 payload = _xpc_query_key_string("name", name);
2113 if (payload == NULL) return NULL;
2114
2115 item = _ds_item(si, CATEGORY_FS, "getfsbyname", NULL, _extract_fstab, payload);
2116
2117 xpc_release(payload);
2118 return item;
2119 }
2120
2121 static si_list_t *
2122 ds_fs_all(si_mod_t *si)
2123 {
2124 LI_OS_ACTIVITY("Performance impact - Enumerate all FS entries");
2125 return _ds_list(si, CATEGORY_FS, "getfsent", NULL, _extract_fstab);
2126 }
2127
2128 static si_item_t *
2129 ds_fs_byfile(si_mod_t *si, const char *name)
2130 {
2131 si_item_t *item;
2132 si_list_t *list;
2133 uint32_t i;
2134 struct fstab *f;
2135
2136 if (!_od_running()) return NULL;
2137 if (name == NULL) return NULL;
2138
2139 LI_OS_ACTIVITY("Retrieve FS by file location");
2140 list = ds_fs_all(si);
2141 if (list == NULL) return NULL;
2142
2143 item = NULL;
2144 for (i = 0; (i < list->count) && (item == NULL); i++)
2145 {
2146 f = (struct fstab *)((uintptr_t)(list->entry[i]) + sizeof(si_item_t));
2147 if (string_equal(name, f->fs_file)) item = si_item_retain(list->entry[i]);
2148 }
2149
2150 si_list_release(list);
2151 return item;
2152 }
2153
2154 static si_item_t *
2155 ds_mac_byname(si_mod_t *si, const char *name)
2156 {
2157 xpc_object_t payload;
2158 si_item_t *item;
2159
2160 if (!_od_running()) return NULL;
2161
2162 LI_OS_ACTIVITY("Retrieve FS by name");
2163 payload = _xpc_query_key_string("name", name);
2164 if (payload == NULL) return NULL;
2165
2166 item = _ds_item(si, CATEGORY_MAC, "getmacbyname", name, _extract_mac_mac, payload);
2167
2168 xpc_release(payload);
2169 return item;
2170 }
2171
2172 static si_item_t *
2173 ds_mac_bymac(si_mod_t *si, const char *mac)
2174 {
2175 xpc_object_t payload;
2176 si_item_t *item;
2177 char *cmac;
2178
2179 if (!_od_running()) return NULL;
2180
2181 LI_OS_ACTIVITY("Retrieve MAC entry by MAC");
2182 cmac = si_standardize_mac_address(mac);
2183 if (cmac == NULL) return NULL;
2184
2185 payload = xpc_dictionary_create(NULL, NULL, 0);
2186 if (payload == NULL) return NULL;
2187
2188 payload = _xpc_query_key_string("mac", cmac);
2189 item = _ds_item(si, CATEGORY_MAC, "gethostbymac", cmac, _extract_mac_name, payload);
2190
2191 free(cmac);
2192 xpc_release(payload);
2193
2194 return item;
2195 }
2196
2197 #pragma mark -
2198
2199 si_mod_t *
2200 si_module_static_ds(void)
2201 {
2202 static const struct si_mod_vtable_s ds_vtable =
2203 {
2204 .sim_is_valid = &_ds_is_valid,
2205
2206 .sim_user_byname = &ds_user_byname,
2207 .sim_user_byuid = &ds_user_byuid,
2208 .sim_user_byuuid = &ds_user_byuuid,
2209 .sim_user_all = &ds_user_all,
2210
2211 .sim_group_byname = &ds_group_byname,
2212 .sim_group_bygid = &ds_group_bygid,
2213 .sim_group_byuuid = &ds_group_byuuid,
2214 .sim_group_all = &ds_group_all,
2215
2216 .sim_grouplist = &ds_grouplist,
2217
2218 .sim_netgroup_byname = &ds_netgroup_byname,
2219 .sim_in_netgroup = &ds_in_netgroup,
2220
2221 .sim_alias_byname = &ds_alias_byname,
2222 .sim_alias_all = &ds_alias_all,
2223
2224 /* host lookups not supported */
2225 .sim_host_byname = NULL,
2226 .sim_host_byaddr = NULL,
2227 .sim_host_all = NULL,
2228
2229 .sim_network_byname = &ds_network_byname,
2230 .sim_network_byaddr = &ds_network_byaddr,
2231 .sim_network_all = &ds_network_all,
2232
2233 .sim_service_byname = &ds_service_byname,
2234 .sim_service_byport = &ds_service_byport,
2235 .sim_service_all = &ds_service_all,
2236
2237 .sim_protocol_byname = &ds_protocol_byname,
2238 .sim_protocol_bynumber = &ds_protocol_bynumber,
2239 .sim_protocol_all = &ds_protocol_all,
2240
2241 .sim_rpc_byname = &ds_rpc_byname,
2242 .sim_rpc_bynumber = &ds_rpc_bynumber,
2243 .sim_rpc_all = &ds_rpc_all,
2244
2245 .sim_fs_byspec = &ds_fs_byspec,
2246 .sim_fs_byfile = &ds_fs_byfile,
2247 .sim_fs_all = &ds_fs_all,
2248
2249 .sim_mac_byname = &ds_mac_byname,
2250 .sim_mac_bymac = &ds_mac_bymac,
2251
2252 /* si_mac_all not supported */
2253 .sim_mac_all = NULL,
2254
2255 /* si_addrinfo not supported */
2256 .sim_wants_addrinfo = NULL,
2257 .sim_addrinfo = NULL,
2258 };
2259
2260 static si_mod_t si =
2261 {
2262 .vers = 1,
2263 .refcount = 1,
2264 .flags = SI_MOD_FLAG_STATIC,
2265
2266 .private = NULL,
2267 .vtable = &ds_vtable,
2268 };
2269
2270 static dispatch_once_t once;
2271 dispatch_once(&once, ^{
2272 pthread_key_create(&_ds_serv_cache_key, _ds_serv_cache_free);
2273
2274 si.name = strdup("ds");
2275 ds_si_private_t *pp = calloc(1, sizeof(ds_si_private_t));
2276
2277 if (pp != NULL)
2278 {
2279 pp->notify_token_global = -1;
2280 pp->notify_token_user = -1;
2281 pp->notify_token_group = -1;
2282 pp->notify_token_service = -1;
2283 }
2284
2285 /*
2286 * Don't register for notifications if the cache is disabled.
2287 * notifyd (notably) disables the cache to prevent deadlocks.
2288 */
2289 if (gL1CacheEnabled != 0)
2290 {
2291 /*
2292 * Errors in registering for cache invalidation notifications are ignored.
2293 * If there are failures, the tokens remain set to -1 which just causes
2294 * cached items to be invalidated.
2295 */
2296 notify_register_check(kNotifyDSCacheInvalidation, &(pp->notify_token_global));
2297 notify_register_check(kNotifyDSCacheInvalidationUser, &(pp->notify_token_user));
2298 notify_register_check(kNotifyDSCacheInvalidationGroup, &(pp->notify_token_group));
2299 notify_register_check(kNotifyDSCacheInvalidationService, &(pp->notify_token_service));
2300 }
2301
2302 si.private = pp;
2303 });
2304
2305 return &si;
2306 }
2307
2308 #endif /* DS_AVAILABLE */