2 * Copyright (c) 2008-2011 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
32 #include <arpa/inet.h>
41 #include <notify_keys.h>
43 #include <si_module.h>
44 #include <netdb_async.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>
57 /* <rdar://problem/10675978> */
58 __attribute__((weak_import
))
59 void xpc_dictionary_get_audit_token(xpc_object_t xdict
, audit_token_t
*token
);
61 __attribute__((weak_import
))
62 xpc_pipe_t
xpc_pipe_create(const char *name
, uint64_t flags
);
64 __attribute__((weak_import
))
65 void xpc_pipe_invalidate(xpc_pipe_t pipe
);
67 __attribute__((weak_import
))
68 int xpc_pipe_routine(xpc_pipe_t pipe
, xpc_object_t message
, xpc_object_t
*reply
);
71 #define IPV6_ADDR_LEN 16
72 #define IPV4_ADDR_LEN 4
74 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
);
77 uint32_t notify_peek(int token
, uint32_t *val
);
81 int notify_token_global
;
82 int notify_token_user
;
83 int notify_token_group
;
84 int notify_token_service
;
87 extern uint32_t gL1CacheEnabled
;
88 extern int _si_opendirectory_disabled
;
90 static pthread_key_t _ds_serv_cache_key
= 0;
91 static xpc_pipe_t __od_pipe
; /* use accessor only */
92 static pthread_mutex_t mutex
= PTHREAD_MUTEX_INITIALIZER
;
99 // re-enable opendirectory interaction since we forked
100 _si_opendirectory_disabled
= 0;
102 if (__od_pipe
!= NULL
) {
103 xpc_pipe_invalidate(__od_pipe
);
104 /* disable release due to 10649340, it will cause a minor leak for each fork without exec */
105 // xpc_release(__od_pipe);
108 _ds_port
= MACH_PORT_NULL
;
109 pthread_mutex_unlock(&mutex
);
113 _od_fork_prepare(void)
115 pthread_mutex_lock(&mutex
);
119 _od_fork_parent(void)
121 pthread_mutex_unlock(&mutex
);
125 _ds_serv_cache_free(void *x
)
127 if (x
!= NULL
) si_item_release(x
);
131 _si_disable_opendirectory(void)
133 _si_opendirectory_disabled
= 1;
134 _ds_port
= MACH_PORT_NULL
;
139 _od_xpc_pipe(bool resetPipe
)
141 static dispatch_once_t once
;
142 xpc_pipe_t result
= NULL
;
145 if (xpc_pipe_create
== NULL
) {
146 _si_disable_opendirectory();
151 dispatch_once(&once
, ^(void) {
154 /* if this is a build environment we ignore opendirectoryd */
155 rc_xbs
= getenv("RC_XBS");
156 if ((issetugid() == 0) && (rc_xbs
!= NULL
) && (strcmp(rc_xbs
, "YES") == 0)) {
157 _si_opendirectory_disabled
= 1;
161 pthread_atfork(_od_fork_prepare
, _od_fork_parent
, _od_fork_child
);
164 if (_si_opendirectory_disabled
== 1) {
168 pthread_mutex_lock(&mutex
);
170 xpc_release(__od_pipe
);
174 if (__od_pipe
== NULL
) {
175 if (!issetugid() && getenv("OD_DEBUG_MODE") != NULL
) {
176 __od_pipe
= xpc_pipe_create(kODMachLibinfoPortNameDebug
, 0);
178 __od_pipe
= xpc_pipe_create(kODMachLibinfoPortName
, XPC_PIPE_FLAG_PRIVILEGED
);
182 if (__od_pipe
!= NULL
) result
= xpc_retain(__od_pipe
);
183 pthread_mutex_unlock(&mutex
);
193 pipe
= _od_xpc_pipe(false);
198 if (_si_opendirectory_disabled
) {
202 return (pipe
!= NULL
);
208 _ds_port
= MACH_PORT_NULL
;
214 kern_return_t status
;
215 char *od_debug_mode
= NULL
;
217 if (_ds_port
!= MACH_PORT_NULL
) return 1;
219 if (_si_opendirectory_disabled
) return 0;
220 pthread_atfork(NULL
, NULL
, _ds_child
);
223 od_debug_mode
= getenv("OD_DEBUG_MODE");
227 status
= bootstrap_look_up(bootstrap_port
, kDSStdMachDSLookupPortName
"_debug", &_ds_port
);
229 status
= bootstrap_look_up2(bootstrap_port
, kDSStdMachDSLookupPortName
, &_ds_port
, 0, BOOTSTRAP_PRIVILEGED_SERVER
);
231 if ((status
!= BOOTSTRAP_SUCCESS
) && (status
!= BOOTSTRAP_UNKNOWN_SERVICE
)) _ds_port
= MACH_PORT_NULL
;
233 return (_ds_port
!= MACH_PORT_NULL
);
237 _valid_token(xpc_object_t reply
)
242 * This should really call audit_token_to_au32,
243 * but that's in libbsm, not in a Libsystem library.
245 xpc_dictionary_get_audit_token(reply
, &token
);
247 return ((uid_t
) token
.val
[1] == 0);
251 _ds_get_validation(si_mod_t
*si
, uint64_t *a
, uint64_t *b
, int cat
)
257 if (si
== NULL
) return;
259 pp
= (ds_si_private_t
*)si
->private;
260 if (pp
== NULL
) return;
265 status
= notify_peek(pp
->notify_token_global
, &peek
);
266 if (status
== NOTIFY_STATUS_OK
) *a
= ntohl(peek
);
273 status
= NOTIFY_STATUS_FAILED
;
275 if (cat
== CATEGORY_USER
) status
= notify_peek(pp
->notify_token_user
, &peek
);
276 else if (cat
== CATEGORY_GROUP
) status
= notify_peek(pp
->notify_token_group
, &peek
);
277 else if (cat
== CATEGORY_GROUPLIST
) status
= notify_peek(pp
->notify_token_group
, &peek
);
278 else if (cat
== CATEGORY_SERVICE
) status
= notify_peek(pp
->notify_token_service
, &peek
);
280 if (status
== NOTIFY_STATUS_OK
) *b
= ntohl(peek
);
285 __private_extern__ xpc_object_t
286 _od_rpc_call(const char *procname
, xpc_object_t payload
, xpc_pipe_t (*get_pipe
)(bool))
288 xpc_object_t result
= NULL
;
293 od_pipe
= get_pipe(false);
294 if (od_pipe
== NULL
) return NULL
;
296 if (payload
== NULL
) {
297 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
300 // we nest it for backward compatibility so we can do independent submissions
301 xpc_dictionary_set_string(payload
, OD_RPC_NAME
, procname
);
302 xpc_dictionary_set_int64(payload
, OD_RPC_VERSION
, 2);
304 for (retries
= 0; od_pipe
!= NULL
&& retries
< 2; retries
++) {
305 rc
= xpc_pipe_routine(od_pipe
, payload
, &reply
);
308 xpc_release(od_pipe
);
309 od_pipe
= get_pipe(true);
313 /* just loop and try to send again */
317 if (_valid_token(reply
) == true) {
320 /* fall through since we got a valid response */
323 /* release and NULL the pipe it'll break the loop */
324 xpc_release(od_pipe
);
330 if (od_pipe
!= NULL
) {
331 xpc_release(od_pipe
);
338 _ds_list(si_mod_t
*si
, int cat
, const char *procname
, const void *extra
, od_extract_t extract
)
340 __block si_list_t
*list
;
342 xpc_object_t reply
, result
;
344 if (procname
== NULL
) return NULL
;
346 _ds_get_validation(si
, &va
, &vb
, cat
);
349 reply
= _od_rpc_call(procname
, NULL
, _od_xpc_pipe
);
351 result
= xpc_dictionary_get_value(reply
, OD_RPC_RESULT
);
352 if (result
!= NULL
&& xpc_get_type(result
) == XPC_TYPE_ARRAY
) {
353 xpc_array_apply(result
, ^bool(size_t index
, xpc_object_t value
) {
354 si_item_t
*item
= extract(si
, value
, extra
, va
, vb
);
355 list
= si_list_add(list
, item
);
356 si_item_release(item
);
369 _ds_item(si_mod_t
*si
, int cat
, const char *procname
, const void *extra
, od_extract_t extract
, xpc_object_t payload
)
373 si_item_t
*item
= NULL
;
375 if (procname
== NULL
) return NULL
;
377 result
= _od_rpc_call(procname
, payload
, _od_xpc_pipe
);
378 if (result
!= NULL
) {
379 _ds_get_validation(si
, &va
, &vb
, cat
);
380 if (xpc_dictionary_get_int64(result
, OD_RPC_ERROR
) == 0) {
381 item
= extract(si
, result
, extra
, va
, vb
);
391 _ds_is_valid(si_mod_t
*si
, si_item_t
*item
)
396 uint32_t oldval
, newval
;
398 if (si
== NULL
) return 0;
399 if (item
== NULL
) return 0;
400 if (si
->name
== NULL
) return 0;
401 if (item
->src
== NULL
) return 0;
403 pp
= (ds_si_private_t
*)si
->private;
404 if (pp
== NULL
) return 0;
406 src
= (si_mod_t
*)item
->src
;
408 if (src
->name
== NULL
) return 0;
409 if (string_not_equal(si
->name
, src
->name
)) return 0;
411 /* check global invalidation */
412 oldval
= item
->validation_a
;
414 status
= notify_peek(pp
->notify_token_global
, &newval
);
415 if (status
!= NOTIFY_STATUS_OK
) return 0;
417 newval
= ntohl(newval
);
418 if (oldval
!= newval
) return 0;
420 oldval
= item
->validation_b
;
422 if (item
->type
== CATEGORY_USER
) status
= notify_peek(pp
->notify_token_user
, &newval
);
423 else if (item
->type
== CATEGORY_GROUP
) status
= notify_peek(pp
->notify_token_group
, &newval
);
424 else if (item
->type
== CATEGORY_SERVICE
) status
= notify_peek(pp
->notify_token_service
, &newval
);
427 if (status
!= NOTIFY_STATUS_OK
) return 0;
429 newval
= ntohl(newval
);
430 if (oldval
!= newval
) return 0;
436 _free_addr_list(char **l
)
440 if (l
== NULL
) return;
441 for (i
= 0; l
[i
] != NULL
; i
++) free(l
[i
]);
445 /* map ipv4 addresses and append to v6 list */
447 _map_v4(char ***v6
, uint32_t n6
, char **v4
, uint32_t n4
)
452 a6
.__u6_addr
.__u6_addr32
[0] = 0x00000000;
453 a6
.__u6_addr
.__u6_addr32
[1] = 0x00000000;
454 a6
.__u6_addr
.__u6_addr32
[2] = htonl(0x0000ffff);
458 *v6
= (char **)calloc(n4
+ 1, sizeof(char *));
462 *v6
= (char **)reallocf(*v6
, (n6
+ n4
+ 1) * sizeof(char *));
465 if (*v6
== NULL
) return -1;
467 for (i
= 0; i
< n4
; i
++)
469 (*v6
)[n6
] = (char *)calloc(1, IPV6_ADDR_LEN
);
470 if ((*v6
)[n6
] == NULL
) return -1;
472 memcpy(&(a6
.__u6_addr
.__u6_addr32
[3]), v4
[i
], IPV4_ADDR_LEN
);
473 memcpy((*v6
)[n6
], &(a6
.__u6_addr
.__u6_addr32
[0]), IPV6_ADDR_LEN
);
482 _xpc_query_key_string(const char *key
, const char *value
)
484 xpc_object_t payload
;
486 if (value
== NULL
) return NULL
;
488 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
489 if (payload
== NULL
) return NULL
;
491 xpc_dictionary_set_string(payload
, key
, value
);
497 _xpc_query_key_id(const char *key
, id_t idValue
)
499 xpc_object_t payload
;
501 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
502 if (payload
== NULL
) return NULL
;
504 xpc_dictionary_set_int64(payload
, key
, idValue
);
510 _xpc_query_key_uuid(const char *key
, uuid_t uu
)
512 xpc_object_t payload
;
514 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
515 if (payload
== NULL
) return NULL
;
517 xpc_dictionary_set_uuid(payload
, key
, uu
);
523 _xpc_query_key_int(const char *key
, int64_t intValue
)
525 xpc_object_t payload
;
527 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
528 if (payload
== NULL
) return NULL
;
530 xpc_dictionary_set_int64(payload
, key
, intValue
);
538 _extract_array(xpc_object_t reply
, const char *key
, unsigned int *len
)
540 xpc_object_t xpc_array
;
543 xpc_array
= xpc_dictionary_get_value(reply
, key
);
544 if (xpc_array
== NULL
|| xpc_get_type(xpc_array
) != XPC_TYPE_ARRAY
) {
545 return calloc(1, sizeof(*result
));
548 result
= calloc(xpc_array_get_count(xpc_array
) + 1, sizeof(*result
));
549 if (result
== NULL
) {
554 /* include trailing NULL */
555 (*len
) = xpc_array_get_count(xpc_array
) + 1;
558 xpc_array_apply(xpc_array
, ^_Bool(size_t idx
, xpc_object_t value
) {
559 result
[idx
] = (char *) xpc_string_get_string_ptr(value
);
567 _extract_string(xpc_object_t reply
, const char *key
)
569 xpc_object_t value
= xpc_dictionary_get_value(reply
, key
);
571 const char *result
= NULL
;
577 type
= xpc_get_type(value
);
578 if (type
== XPC_TYPE_STRING
) {
579 result
= xpc_string_get_string_ptr(value
);
580 } else if (type
== XPC_TYPE_ARRAY
&& xpc_array_get_count(value
) != 0) {
581 result
= xpc_array_get_string(value
, 0);
584 if (result
== NULL
) {
592 _extract_uint32(xpc_object_t reply
, const char *key
)
594 xpc_object_t value
= xpc_dictionary_get_value(reply
, key
);
602 type
= xpc_get_type(value
);
603 if (type
== XPC_TYPE_ARRAY
&& xpc_array_get_count(value
) != 0) {
604 value
= xpc_array_get_value(value
, 0);
605 type
= xpc_get_type(value
);
608 if (type
== XPC_TYPE_STRING
) {
609 result
= (int) strtol(xpc_string_get_string_ptr(value
), NULL
, 10);
610 } else if (type
== XPC_TYPE_INT64
) {
611 result
= (uint32_t) xpc_int64_get_value(value
);
612 } else if (type
== XPC_TYPE_BOOL
) {
613 result
= xpc_bool_get_value(value
);
622 _extract_user(si_mod_t
*si
, xpc_object_t reply
, __unused
const void *ignored
, uint64_t valid_global
, uint64_t valid_cat
)
626 if (si
== NULL
) return NULL
;
627 if (reply
== NULL
) return NULL
;
629 tmp
.pw_name
= (char *) _extract_string(reply
, "pw_name");
630 tmp
.pw_passwd
= (char *) _extract_string(reply
, "pw_passwd");
631 tmp
.pw_uid
= (uid_t
) _extract_uint32(reply
, "pw_uid");
632 tmp
.pw_gid
= (gid_t
) _extract_uint32(reply
, "pw_gid");
633 tmp
.pw_change
= (time_t) _extract_uint32(reply
, "pw_change");
634 tmp
.pw_expire
= (time_t) _extract_uint32(reply
, "pw_expire");
635 tmp
.pw_class
= (char *) _extract_string(reply
, "pw_class");
636 tmp
.pw_gecos
= (char *) _extract_string(reply
, "pw_gecos");
637 tmp
.pw_dir
= (char *) _extract_string(reply
, "pw_dir");
638 tmp
.pw_shell
= (char *) _extract_string(reply
, "pw_shell");
640 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
);
644 _extract_group(si_mod_t
*si
, xpc_object_t reply
, __unused
const void *ignored
, uint64_t valid_global
, uint64_t valid_cat
)
649 if (si
== NULL
) return NULL
;
650 if (reply
== NULL
) return NULL
;
652 tmp
.gr_name
= (char *) _extract_string(reply
, "gr_name");
653 tmp
.gr_passwd
= (char *) _extract_string(reply
, "gr_passwd");
654 tmp
.gr_gid
= (gid_t
) _extract_uint32(reply
, "gr_gid");
655 tmp
.gr_mem
= _extract_array(reply
, "gr_mem", NULL
);
657 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
);
659 if (tmp
.gr_mem
!= NULL
) {
660 free(tmp
.gr_mem
); /* have to free because it's allocated */
667 _extract_netgroup(si_mod_t
*si
, xpc_object_t reply
, const void *ignored
, uint64_t valid_global
, uint64_t valid_cat
)
669 const char *host
, *user
, *domain
;
671 if (si
== NULL
) return NULL
;
672 if (reply
== NULL
) return NULL
;
674 host
= _extract_string(reply
, "host");
675 user
= _extract_string(reply
, "user");
676 domain
= _extract_string(reply
, "domain");
678 return (si_item_t
*)LI_ils_create("L4488sss", (unsigned long)si
, CATEGORY_NETGROUP
, 1, valid_global
, valid_cat
, host
, user
, domain
);
682 _extract_alias(si_mod_t
*si
, xpc_object_t reply
, __unused
const void *ignored
, uint64_t valid_global
, uint64_t valid_cat
)
687 if (si
== NULL
) return NULL
;
688 if (reply
== NULL
) return NULL
;
690 tmp
.alias_name
= (char *) _extract_string(reply
, "alias_name");
691 tmp
.alias_local
= _extract_uint32(reply
, "alias_local");
692 tmp
.alias_members
= _extract_array(reply
, "alias_members", &tmp
.alias_members_len
);
694 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
);
696 if (tmp
.alias_members
!= NULL
) {
697 free(tmp
.alias_members
);
704 _extract_network(si_mod_t
*si
, xpc_object_t reply
, __unused
const void *ignored
, uint64_t valid_global
, uint64_t valid_cat
)
709 if (si
== NULL
) return NULL
;
710 if (reply
== NULL
) return NULL
;
712 tmp
.n_name
= (char *) _extract_string(reply
, "n_name");
713 tmp
.n_aliases
= _extract_array(reply
, "n_aliases", NULL
);
714 tmp
.n_net
= _extract_uint32(reply
, "n_net");
715 tmp
.n_addrtype
= AF_INET
; /* opendirectoryd doesn't return this value, only AF_INET is supported */
717 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
);
719 if (tmp
.n_aliases
!= NULL
) {
727 _extract_service(si_mod_t
*si
, xpc_object_t reply
, __unused
const void *ignored
, uint64_t valid_global
, uint64_t valid_cat
)
732 if (si
== NULL
) return NULL
;
733 if (reply
== NULL
) return NULL
;
735 tmp
.s_name
= (char *) _extract_string(reply
, "s_name");
736 tmp
.s_aliases
= _extract_array(reply
, "s_aliases", NULL
);
737 tmp
.s_port
= (unsigned int) htons(_extract_uint32(reply
, "s_port"));
738 tmp
.s_proto
= (char *) _extract_string(reply
, "s_proto");
740 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
);
742 if (tmp
.s_aliases
!= NULL
) {
750 _extract_protocol(si_mod_t
*si
, xpc_object_t reply
, __unused
const void *ignored
, uint64_t valid_global
, uint64_t valid_cat
)
755 if (si
== NULL
) return NULL
;
756 if (reply
== NULL
) return NULL
;
758 tmp
.p_name
= (char *) _extract_string(reply
, "p_name");
759 tmp
.p_proto
= (int) _extract_uint32(reply
, "p_proto");
760 tmp
.p_aliases
= _extract_array(reply
, "p_aliases", NULL
);
762 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
);
763 if (tmp
.p_aliases
!= NULL
) {
771 _extract_rpc(si_mod_t
*si
, xpc_object_t reply
, __unused
const void *ignored
, uint64_t valid_global
, uint64_t valid_cat
)
776 if (si
== NULL
) return NULL
;
777 if (reply
== NULL
) return NULL
;
779 tmp
.r_name
= (char *) _extract_string(reply
, "r_name");
780 tmp
.r_number
= (int) _extract_uint32(reply
, "r_number");
781 tmp
.r_aliases
= _extract_array(reply
, "r_aliases", NULL
);
783 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
);
784 if (tmp
.r_aliases
!= NULL
) {
792 _extract_fstab(si_mod_t
*si
, xpc_object_t reply
, __unused
const void *ignored
, uint64_t valid_global
, uint64_t valid_cat
)
796 if (si
== NULL
) return NULL
;
797 if (reply
== NULL
) return NULL
;
799 tmp
.fs_file
= (char *) _extract_string(reply
, "fs_file");
800 if (tmp
.fs_file
== NULL
) return NULL
;
802 tmp
.fs_spec
= (char *) _extract_string(reply
, "fs_spec");
803 tmp
.fs_freq
= _extract_uint32(reply
, "fs_freq");
804 tmp
.fs_passno
= _extract_uint32(reply
, "fs_passno");
805 tmp
.fs_mntops
= (char *) _extract_string(reply
, "fs_mntops");
806 tmp
.fs_type
= (char *) _extract_string(reply
, "fs_type");
807 tmp
.fs_vfstype
= (char *) _extract_string(reply
, "fs_vfstype");
809 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
);
813 _extract_mac_mac(si_mod_t
*si
, xpc_object_t reply
, const void *extra
, uint64_t valid_global
, uint64_t valid_cat
)
819 if (si
== NULL
) return NULL
;
820 if (reply
== NULL
) return NULL
;
821 if (extra
== NULL
) return NULL
;
823 value
= _extract_string(reply
, "mac");
824 if (value
== NULL
|| value
[0] == '\0') return NULL
;
826 cmac
= si_standardize_mac_address(value
);
827 if (cmac
== NULL
) return NULL
;
829 out
= (si_item_t
*)LI_ils_create("L4488ss", (unsigned long)si
, CATEGORY_MAC
, 1, valid_global
, valid_cat
, extra
, cmac
);
837 _extract_mac_name(si_mod_t
*si
, xpc_object_t reply
, const void *extra
, uint64_t valid_global
, uint64_t valid_cat
)
842 if (si
== NULL
) return NULL
;
843 if (reply
== NULL
) return NULL
;
844 if (extra
== NULL
) return NULL
;
846 name
= _extract_string(reply
, "name");
848 out
= (si_item_t
*)LI_ils_create("L4488ss", (unsigned long)si
, CATEGORY_MAC
, 1, valid_global
, valid_cat
, name
, extra
);
855 ds_user_byname(si_mod_t
*si
, const char *name
)
857 xpc_object_t payload
;
860 if (!_od_running()) return NULL
;
862 payload
= _xpc_query_key_string("name", name
);
863 if (payload
== NULL
) return NULL
;
865 item
= _ds_item(si
, CATEGORY_USER
, "getpwnam", NULL
, _extract_user
, payload
);
867 xpc_release(payload
);
872 ds_user_byuid(si_mod_t
*si
, uid_t uid
)
874 xpc_object_t payload
;
877 if (!_od_running()) return NULL
;
879 payload
= _xpc_query_key_id("uid", uid
);
880 if (payload
== NULL
) return NULL
;
882 item
= _ds_item(si
, CATEGORY_USER
, "getpwuid", NULL
, _extract_user
, payload
);
884 xpc_release(payload
);
889 ds_user_byuuid(si_mod_t
*si
, uuid_t uuid
)
891 xpc_object_t payload
;
894 if (!_od_running()) return NULL
;
896 payload
= _xpc_query_key_uuid("uuid", uuid
);
897 if (payload
== NULL
) return NULL
;
899 item
= _ds_item(si
, CATEGORY_USER
, "getpwuuid", NULL
, _extract_user
, payload
);
901 xpc_release(payload
);
906 ds_user_all(si_mod_t
*si
)
908 return _ds_list(si
, CATEGORY_USER
, "getpwent", NULL
, _extract_user
);
912 ds_group_byname(si_mod_t
*si
, const char *name
)
914 xpc_object_t payload
;
917 if (!_od_running()) return NULL
;
919 payload
= _xpc_query_key_string("name", name
);
920 if (payload
== NULL
) return NULL
;
922 item
= _ds_item(si
, CATEGORY_GROUP
, "getgrnam", NULL
, _extract_group
, payload
);
924 xpc_release(payload
);
929 ds_group_bygid(si_mod_t
*si
, gid_t gid
)
931 xpc_object_t payload
;
934 if (!_od_running()) return NULL
;
936 payload
= _xpc_query_key_id("gid", gid
);
937 if (payload
== NULL
) return NULL
;
939 item
= _ds_item(si
, CATEGORY_GROUP
, "getgrgid", NULL
, _extract_group
, payload
);
941 xpc_release(payload
);
946 ds_group_byuuid(si_mod_t
*si
, uuid_t uuid
)
948 xpc_object_t payload
;
951 if (!_od_running()) return NULL
;
953 payload
= _xpc_query_key_uuid("uuid", uuid
);
954 if (payload
== NULL
) return NULL
;
956 item
= _ds_item(si
, CATEGORY_GROUP
, "getgruuid", NULL
, _extract_group
, payload
);
958 xpc_release(payload
);
963 ds_group_all(si_mod_t
*si
)
965 if (!_od_running()) return NULL
;
966 return _ds_list(si
, CATEGORY_GROUP
, "getgrent", NULL
, _extract_group
);
970 ds_grouplist(si_mod_t
*si
, const char *name
, uint32_t ngroups
)
972 xpc_object_t payload
, reply
;
973 si_item_t
*item
= NULL
;
975 if (!_od_running()) return NULL
;
976 if (name
== NULL
) return NULL
;
978 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
979 if (payload
== NULL
) return NULL
;
981 xpc_dictionary_set_string(payload
, "name", name
);
982 xpc_dictionary_set_int64(payload
, "ngroups", ngroups
);
984 reply
= _od_rpc_call("getgrouplist", payload
, _od_xpc_pipe
);
987 const gid_t
*gidptr
= xpc_dictionary_get_data(reply
, "groups", &gidptrsz
);
991 _ds_get_validation(si
, &va
, &vb
, CATEGORY_GROUPLIST
);
993 /* see what we were sent */
994 count
= _extract_uint32(reply
, "count");
996 item
= (si_item_t
*)LI_ils_create("L4488s4@", (unsigned long)si
, CATEGORY_GROUPLIST
, 1, va
, vb
, name
, count
,
1003 xpc_release(payload
);
1009 ds_netgroup_byname(si_mod_t
*si
, const char *name
)
1011 xpc_object_t payload
;
1012 si_list_t
*list
= NULL
;
1015 if (!_od_running()) return NULL
;
1017 payload
= _xpc_query_key_string("netgroup", name
);
1018 if (payload
== NULL
) return NULL
;
1020 item
= _ds_item(si
, CATEGORY_NETGROUP
, "getnetgrent", NULL
, _extract_netgroup
, payload
);
1022 list
= si_list_add(list
, item
);
1023 si_item_release(item
);
1026 xpc_release(payload
);
1032 ds_in_netgroup(si_mod_t
*si
, const char *group
, const char *host
, const char *user
, const char *domain
)
1034 xpc_object_t payload
, reply
;
1037 if (!_od_running()) return 0;
1039 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
1040 if (payload
== NULL
) return 0;
1042 xpc_dictionary_set_string(payload
, "netgroup", (group
? group
: ""));
1043 xpc_dictionary_set_string(payload
, "host", (host
? host
: ""));
1044 xpc_dictionary_set_string(payload
, "user", (user
? user
: ""));
1045 xpc_dictionary_set_string(payload
, "domain", (domain
? domain
: ""));
1047 reply
= _od_rpc_call("innetgr", payload
, _od_xpc_pipe
);
1048 if (reply
!= NULL
) {
1049 is_innetgr
= xpc_dictionary_get_bool(reply
, OD_RPC_RESULT
);
1055 xpc_release(payload
);
1061 ds_alias_byname(si_mod_t
*si
, const char *name
)
1063 xpc_object_t payload
;
1066 if (!_od_running()) return NULL
;
1068 payload
= _xpc_query_key_string("name", name
);
1069 if (payload
== NULL
) return NULL
;
1071 item
= _ds_item(si
, CATEGORY_ALIAS
, "alias_getbyname", NULL
, _extract_alias
, payload
);
1073 xpc_release(payload
);
1078 ds_alias_all(si_mod_t
*si
)
1080 if (!_od_running()) return NULL
;
1081 return _ds_list(si
, CATEGORY_ALIAS
, "alias_getent", NULL
, _extract_alias
);
1085 ds_network_byname(si_mod_t
*si
, const char *name
)
1087 xpc_object_t payload
;
1090 if (!_od_running()) return NULL
;
1092 payload
= _xpc_query_key_string("name", name
);
1093 if (payload
== NULL
) return NULL
;
1095 item
= _ds_item(si
, CATEGORY_NETWORK
, "getnetbyname", NULL
, _extract_network
, payload
);
1097 xpc_release(payload
);
1102 ds_network_byaddr(si_mod_t
*si
, uint32_t addr
)
1104 unsigned char f1
, f2
, f3
;
1106 xpc_object_t payload
;
1109 if (!_od_running()) return NULL
;
1117 if (f3
!= 0) snprintf(val
, sizeof(val
), "%u.%u.%u", f3
, f2
, f1
);
1118 else if (f2
!= 0) snprintf(val
, sizeof(val
), "%u.%u", f2
, f1
);
1119 else snprintf(val
, sizeof(val
), "%u", f1
);
1121 payload
= _xpc_query_key_string("net", val
);
1122 if (payload
== NULL
) return NULL
;
1124 item
= _ds_item(si
, CATEGORY_NETWORK
, "getnetbyaddr", NULL
, _extract_network
, payload
);
1126 xpc_release(payload
);
1131 ds_network_all(si_mod_t
*si
)
1133 if (!_od_running()) return NULL
;
1134 return _ds_list(si
, CATEGORY_NETWORK
, "getnetent", NULL
, _extract_network
);
1138 ds_service_byname(si_mod_t
*si
, const char *name
, const char *proto
)
1140 xpc_object_t payload
;
1144 if (!_od_running()) return NULL
;
1145 if (name
== NULL
) name
= "";
1146 if (proto
== NULL
) proto
= "";
1148 /* Check our local service cache (see ds_addrinfo). */
1149 item
= pthread_getspecific(_ds_serv_cache_key
);
1152 s
= (struct servent
*)((uintptr_t)item
+ sizeof(si_item_t
));
1153 if (string_equal(name
, s
->s_name
)) return si_item_retain(item
);
1156 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
1157 if (payload
== NULL
) return NULL
;
1159 xpc_dictionary_set_string(payload
, "name", name
);
1160 xpc_dictionary_set_string(payload
, "proto", proto
);
1162 item
= _ds_item(si
, CATEGORY_SERVICE
, "getservbyname", NULL
, _extract_service
, payload
);
1164 xpc_release(payload
);
1170 ds_service_byport(si_mod_t
*si
, int port
, const char *proto
)
1172 xpc_object_t payload
;
1175 if (!_od_running()) return NULL
;
1177 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
1178 if (payload
== NULL
) return NULL
;
1180 /* swap to native order, API passes network order */
1181 xpc_dictionary_set_int64(payload
, "port", ntohs(port
));
1182 xpc_dictionary_set_string(payload
, "proto", (proto
? proto
: ""));
1184 item
= _ds_item(si
, CATEGORY_SERVICE
, "getservbyport", NULL
, _extract_service
, payload
);
1186 xpc_release(payload
);
1192 ds_service_all(si_mod_t
*si
)
1194 if (!_od_running()) return NULL
;
1195 return _ds_list(si
, CATEGORY_SERVICE
, "getservent", NULL
, _extract_service
);
1199 ds_protocol_byname(si_mod_t
*si
, const char *name
)
1201 xpc_object_t payload
;
1204 if (!_od_running()) return NULL
;
1206 payload
= _xpc_query_key_string("name", name
);
1207 if (payload
== NULL
) return NULL
;
1209 item
= _ds_item(si
, CATEGORY_PROTOCOL
, "getprotobyname", NULL
, _extract_protocol
, payload
);
1211 xpc_release(payload
);
1216 ds_protocol_bynumber(si_mod_t
*si
, int number
)
1218 xpc_object_t payload
;
1221 if (!_od_running()) return NULL
;
1223 payload
= _xpc_query_key_int("number", number
);
1224 if (payload
== NULL
) return NULL
;
1226 item
= _ds_item(si
, CATEGORY_PROTOCOL
, "getprotobynumber", NULL
, _extract_protocol
, payload
);
1228 xpc_release(payload
);
1233 ds_protocol_all(si_mod_t
*si
)
1235 return _ds_list(si
, CATEGORY_PROTOCOL
, "getprotoent", NULL
, _extract_protocol
);
1239 ds_rpc_byname(si_mod_t
*si
, const char *name
)
1241 xpc_object_t payload
;
1244 if (!_od_running()) return NULL
;
1246 payload
= _xpc_query_key_string("name", name
);
1247 if (payload
== NULL
) return NULL
;
1249 item
= _ds_item(si
, CATEGORY_RPC
, "getrpcbyname", NULL
, _extract_rpc
, payload
);
1251 xpc_release(payload
);
1256 ds_rpc_bynumber(si_mod_t
*si
, int number
)
1258 xpc_object_t payload
;
1261 if (!_od_running()) return NULL
;
1263 payload
= _xpc_query_key_int("number", number
);
1264 if (payload
== NULL
) return NULL
;
1266 item
= _ds_item(si
, CATEGORY_RPC
, "getrpcbynumber", NULL
, _extract_rpc
, payload
);
1268 xpc_release(payload
);
1273 ds_rpc_all(si_mod_t
*si
)
1275 return _ds_list(si
, CATEGORY_RPC
, "getrpcent", NULL
, _extract_rpc
);
1279 ds_fs_byspec(si_mod_t
*si
, const char *name
)
1281 xpc_object_t payload
;
1284 if (!_od_running()) return NULL
;
1286 payload
= _xpc_query_key_string("name", name
);
1287 if (payload
== NULL
) return NULL
;
1289 item
= _ds_item(si
, CATEGORY_FS
, "getfsbyname", NULL
, _extract_fstab
, payload
);
1291 xpc_release(payload
);
1296 ds_fs_all(si_mod_t
*si
)
1298 return _ds_list(si
, CATEGORY_FS
, "getfsent", NULL
, _extract_fstab
);
1302 ds_fs_byfile(si_mod_t
*si
, const char *name
)
1309 if (!_od_running()) return NULL
;
1310 if (name
== NULL
) return NULL
;
1312 list
= ds_fs_all(si
);
1313 if (list
== NULL
) return NULL
;
1316 for (i
= 0; (i
< list
->count
) && (item
== NULL
); i
++)
1318 f
= (struct fstab
*)((uintptr_t)(list
->entry
[i
]) + sizeof(si_item_t
));
1319 if (string_equal(name
, f
->fs_file
)) item
= si_item_retain(list
->entry
[i
]);
1322 si_list_release(list
);
1327 ds_mac_byname(si_mod_t
*si
, const char *name
)
1329 xpc_object_t payload
;
1332 if (!_od_running()) return NULL
;
1334 payload
= _xpc_query_key_string("name", name
);
1335 if (payload
== NULL
) return NULL
;
1337 item
= _ds_item(si
, CATEGORY_MAC
, "getmacbyname", name
, _extract_mac_mac
, payload
);
1339 xpc_release(payload
);
1344 ds_mac_bymac(si_mod_t
*si
, const char *mac
)
1346 xpc_object_t payload
;
1350 if (!_od_running()) return NULL
;
1352 cmac
= si_standardize_mac_address(mac
);
1353 if (cmac
== NULL
) return NULL
;
1355 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
1356 if (payload
== NULL
) return NULL
;
1358 payload
= _xpc_query_key_string("mac", cmac
);
1359 item
= _ds_item(si
, CATEGORY_MAC
, "gethostbymac", cmac
, _extract_mac_name
, payload
);
1362 xpc_release(payload
);
1370 si_module_static_ds(void)
1372 static const struct si_mod_vtable_s ds_vtable
=
1374 .sim_is_valid
= &_ds_is_valid
,
1376 .sim_user_byname
= &ds_user_byname
,
1377 .sim_user_byuid
= &ds_user_byuid
,
1378 .sim_user_byuuid
= &ds_user_byuuid
,
1379 .sim_user_all
= &ds_user_all
,
1381 .sim_group_byname
= &ds_group_byname
,
1382 .sim_group_bygid
= &ds_group_bygid
,
1383 .sim_group_byuuid
= &ds_group_byuuid
,
1384 .sim_group_all
= &ds_group_all
,
1386 .sim_grouplist
= &ds_grouplist
,
1388 .sim_netgroup_byname
= &ds_netgroup_byname
,
1389 .sim_in_netgroup
= &ds_in_netgroup
,
1391 .sim_alias_byname
= &ds_alias_byname
,
1392 .sim_alias_all
= &ds_alias_all
,
1394 /* host lookups not supported */
1395 .sim_host_byname
= NULL
,
1396 .sim_host_byaddr
= NULL
,
1397 .sim_host_all
= NULL
,
1399 .sim_network_byname
= &ds_network_byname
,
1400 .sim_network_byaddr
= &ds_network_byaddr
,
1401 .sim_network_all
= &ds_network_all
,
1403 .sim_service_byname
= &ds_service_byname
,
1404 .sim_service_byport
= &ds_service_byport
,
1405 .sim_service_all
= &ds_service_all
,
1407 .sim_protocol_byname
= &ds_protocol_byname
,
1408 .sim_protocol_bynumber
= &ds_protocol_bynumber
,
1409 .sim_protocol_all
= &ds_protocol_all
,
1411 .sim_rpc_byname
= &ds_rpc_byname
,
1412 .sim_rpc_bynumber
= &ds_rpc_bynumber
,
1413 .sim_rpc_all
= &ds_rpc_all
,
1415 .sim_fs_byspec
= &ds_fs_byspec
,
1416 .sim_fs_byfile
= &ds_fs_byfile
,
1417 .sim_fs_all
= &ds_fs_all
,
1419 .sim_mac_byname
= &ds_mac_byname
,
1420 .sim_mac_bymac
= &ds_mac_bymac
,
1422 /* si_mac_all not supported */
1423 .sim_mac_all
= NULL
,
1425 /* si_addrinfo not supported */
1426 .sim_wants_addrinfo
= NULL
,
1427 .sim_addrinfo
= NULL
,
1430 static si_mod_t si
=
1434 .flags
= SI_MOD_FLAG_STATIC
,
1437 .vtable
= &ds_vtable
,
1440 static dispatch_once_t once
;
1441 dispatch_once(&once
, ^{
1442 pthread_key_create(&_ds_serv_cache_key
, _ds_serv_cache_free
);
1444 si
.name
= strdup("ds");
1445 ds_si_private_t
*pp
= calloc(1, sizeof(ds_si_private_t
));
1449 pp
->notify_token_global
= -1;
1450 pp
->notify_token_user
= -1;
1451 pp
->notify_token_group
= -1;
1452 pp
->notify_token_service
= -1;
1456 * Don't register for notifications if the cache is disabled.
1457 * notifyd (notably) disables the cache to prevent deadlocks.
1459 if (gL1CacheEnabled
!= 0)
1462 * Errors in registering for cache invalidation notifications are ignored.
1463 * If there are failures, the tokens remain set to -1 which just causes
1464 * cached items to be invalidated.
1466 notify_register_check(kNotifyDSCacheInvalidation
, &(pp
->notify_token_global
));
1467 notify_register_check(kNotifyDSCacheInvalidationUser
, &(pp
->notify_token_user
));
1468 notify_register_check(kNotifyDSCacheInvalidationGroup
, &(pp
->notify_token_group
));
1469 notify_register_check(kNotifyDSCacheInvalidationService
, &(pp
->notify_token_service
));
1478 #endif /* DS_AVAILABLE */