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