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