2 * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
24 #include <sys/errno.h>
25 #include <mach/mach.h>
26 #include "membership.h"
27 #include "membershipPriv.h"
28 #include <servers/bootstrap.h>
29 #include <libkern/OSByteOrder.h>
32 #include <xpc/private.h>
33 #include <opendirectory/odipc.h>
37 /* <rdar://problem/10675978> */
38 __attribute__((weak_import
))
39 xpc_pipe_t
xpc_pipe_create(const char *name
, uint64_t flags
);
41 __attribute__((weak_import
))
42 void xpc_pipe_invalidate(xpc_pipe_t pipe
);
44 __attribute__((weak_import
))
45 int xpc_pipe_routine(xpc_pipe_t pipe
, xpc_object_t message
, xpc_object_t
*reply
);
47 #endif /* DS_AVAILABLE */
49 static const uuid_t _user_compat_prefix
= {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00};
50 static const uuid_t _group_compat_prefix
= {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00};
52 #define COMPAT_PREFIX_LEN (sizeof(uuid_t) - sizeof(id_t))
56 int _si_opendirectory_disabled
;
57 static xpc_pipe_t __mbr_pipe
; /* use accessor */
58 static pthread_mutex_t mutex
= PTHREAD_MUTEX_INITIALIZER
;
59 __private_extern__ xpc_object_t
_od_rpc_call(const char *procname
, xpc_object_t payload
, xpc_pipe_t (*get_pipe
)(bool));
67 if (__mbr_pipe
!= NULL
) {
68 xpc_pipe_invalidate(__mbr_pipe
);
69 /* disable release due to 10649340, it will cause a minor leak for each fork without exec */
70 // xpc_release(__mbr_pipe);
74 pthread_mutex_unlock(&mutex
);
80 _mbr_fork_prepare(void)
82 pthread_mutex_lock(&mutex
);
88 _mbr_fork_parent(void)
90 pthread_mutex_unlock(&mutex
);
97 _mbr_xpc_pipe(bool resetPipe
)
99 static dispatch_once_t once
;
100 xpc_pipe_t pipe
= NULL
;
103 if (xpc_pipe_create
== NULL
) {
104 _si_opendirectory_disabled
= 1;
109 dispatch_once(&once
, ^(void) {
112 /* if this is a build environment we ignore opendirectoryd */
113 rc_xbs
= getenv("RC_XBS");
114 if (rc_xbs
!= NULL
&& strcmp(rc_xbs
, "YES") == 0) {
115 _si_opendirectory_disabled
= 1;
119 pthread_atfork(_mbr_fork_prepare
, _mbr_fork_parent
, _mbr_fork_child
);
122 if (_si_opendirectory_disabled
== 1) {
126 pthread_mutex_lock(&mutex
);
128 xpc_release(__mbr_pipe
);
132 if (__mbr_pipe
== NULL
) {
133 if (!issetugid() && getenv("OD_DEBUG_MODE") != NULL
) {
134 __mbr_pipe
= xpc_pipe_create(kODMachMembershipPortNameDebug
, 0);
136 __mbr_pipe
= xpc_pipe_create(kODMachMembershipPortName
, XPC_PIPE_FLAG_PRIVILEGED
);
140 if (__mbr_pipe
!= NULL
) pipe
= xpc_retain(__mbr_pipe
);
141 pthread_mutex_unlock(&mutex
);
148 _mbr_od_available(void)
151 xpc_pipe_t pipe
= _mbr_xpc_pipe(false);
161 mbr_identifier_translate(int id_type
, const void *identifier
, size_t identifier_size
, int target_type
, void **result
, int *rec_type
)
164 xpc_object_t payload
, reply
;
167 size_t identifier_len
;
170 if (identifier
== NULL
|| result
== NULL
|| identifier_size
== 0) return EIO
;
172 if (identifier_size
== -1) {
173 identifier_size
= strlen(identifier
);
175 /* 10898647: For types that are known to be strings, send the smallest necessary amount of data. */
177 case ID_TYPE_USERNAME
:
178 case ID_TYPE_GROUPNAME
:
179 case ID_TYPE_GROUP_NFS
:
180 case ID_TYPE_USER_NFS
:
181 case ID_TYPE_X509_DN
:
182 case ID_TYPE_KERBEROS
:
184 identifier_len
= strlen(identifier
);
185 if (identifier_size
> identifier_len
) {
186 identifier_size
= identifier_len
;
192 switch (target_type
) {
195 case ID_TYPE_UID_OR_GID
:
196 /* shortcut UUIDs using compatibilty prefixes */
197 if (id_type
== ID_TYPE_UUID
) {
198 const uint8_t *uu
= identifier
;
200 if (identifier_size
!= sizeof(uuid_t
)) return EINVAL
;
202 if (memcmp(uu
, _user_compat_prefix
, COMPAT_PREFIX_LEN
) == 0) {
203 id_t
*tempRes
= malloc(sizeof(*tempRes
));
204 memcpy(&tempID
, &uu
[COMPAT_PREFIX_LEN
], sizeof(tempID
));
205 (*tempRes
) = ntohl(tempID
);
207 if (rec_type
!= NULL
) {
208 (*rec_type
) = MBR_REC_TYPE_USER
;
211 } else if (memcmp(uu
, _group_compat_prefix
, COMPAT_PREFIX_LEN
) == 0) {
212 id_t
*tempRes
= malloc(sizeof(*tempRes
));
213 memcpy(&tempID
, &uu
[COMPAT_PREFIX_LEN
], sizeof(tempID
));
214 (*tempRes
) = ntohl(tempID
);
216 if (rec_type
!= NULL
) {
217 (*rec_type
) = MBR_REC_TYPE_GROUP
;
225 /* if this is a UID or GID translation, we shortcut UID/GID 0 */
226 /* or if no OD, we return compatibility UUIDs */
229 if (identifier_size
!= sizeof(tempID
)) return EINVAL
;
231 tempID
= *((id_t
*) identifier
);
232 if ((tempID
== 0) || (_mbr_od_available() == false)) {
233 uint8_t *tempUU
= malloc(sizeof(uuid_t
));
234 uuid_copy(tempUU
, _user_compat_prefix
);
235 *((id_t
*) &tempUU
[COMPAT_PREFIX_LEN
]) = htonl(tempID
);
237 if (rec_type
!= NULL
) {
238 (*rec_type
) = MBR_REC_TYPE_USER
;
245 if (identifier_size
!= sizeof(tempID
)) return EINVAL
;
247 tempID
= *((id_t
*) identifier
);
248 if ((tempID
== 0) || (_mbr_od_available() == false)) {
249 uint8_t *tempUU
= malloc(sizeof(uuid_t
));
250 uuid_copy(tempUU
, _group_compat_prefix
);
251 *((id_t
*) &tempUU
[COMPAT_PREFIX_LEN
]) = htonl(tempID
);
253 if (rec_type
!= NULL
) {
254 (*rec_type
) = MBR_REC_TYPE_GROUP
;
264 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
265 if (payload
== NULL
) return EIO
;
267 xpc_dictionary_set_int64(payload
, "requesting", target_type
);
268 xpc_dictionary_set_int64(payload
, "type", id_type
);
269 xpc_dictionary_set_data(payload
, "identifier", identifier
, identifier_size
);
271 reply
= _od_rpc_call("mbr_identifier_translate", payload
, _mbr_xpc_pipe
);
273 const void *reply_id
;
276 rc
= (int) xpc_dictionary_get_int64(reply
, "error");
278 reply_id
= xpc_dictionary_get_data(reply
, "identifier", &idLen
);
279 if (reply_id
!= NULL
) {
280 char *identifier
= malloc(idLen
);
282 memcpy(identifier
, reply_id
, idLen
); // should already be NULL terminated, etc.
283 (*result
) = identifier
;
285 if (rec_type
!= NULL
) {
286 (*rec_type
) = (int) xpc_dictionary_get_int64(reply
, "rectype");
297 xpc_release(payload
);
304 mbr_uid_to_uuid(uid_t id
, uuid_t uu
)
306 return mbr_identifier_to_uuid(ID_TYPE_UID
, &id
, sizeof(id
), uu
);
310 mbr_gid_to_uuid(gid_t id
, uuid_t uu
)
312 return mbr_identifier_to_uuid(ID_TYPE_GID
, &id
, sizeof(id
), uu
);
316 mbr_uuid_to_id(const uuid_t uu
, uid_t
*id
, int *id_type
)
322 rc
= mbr_identifier_translate(ID_TYPE_UUID
, uu
, sizeof(uuid_t
), ID_TYPE_UID_OR_GID
, (void **) &result
, &local_type
);
324 switch (local_type
) {
325 case MBR_REC_TYPE_GROUP
:
326 (*id_type
) = ID_TYPE_GID
;
329 case MBR_REC_TYPE_USER
:
330 (*id_type
) = ID_TYPE_UID
;
346 mbr_sid_to_uuid(const nt_sid_t
*sid
, uuid_t uu
)
349 return mbr_identifier_to_uuid(ID_TYPE_SID
, sid
, sizeof(*sid
), uu
);
356 mbr_identifier_to_uuid(int id_type
, const void *identifier
, size_t identifier_size
, uuid_t uu
)
361 rc
= mbr_identifier_translate(id_type
, identifier
, identifier_size
, ID_TYPE_UUID
, (void **) &result
, NULL
);
363 uuid_copy(uu
, result
);
371 mbr_uuid_to_sid_type(const uuid_t uu
, nt_sid_t
*sid
, int *id_type
)
378 rc
= mbr_identifier_translate(ID_TYPE_UUID
, uu
, sizeof(uuid_t
), ID_TYPE_SID
, &result
, &local_type
);
380 memcpy(sid
, result
, sizeof(nt_sid_t
));
381 if (id_type
!= NULL
) {
383 switch (local_type
) {
384 case MBR_REC_TYPE_USER
:
385 (*id_type
) = SID_TYPE_USER
;
388 case MBR_REC_TYPE_GROUP
:
389 (*id_type
) = SID_TYPE_GROUP
;
407 mbr_uuid_to_sid(const uuid_t uu
, nt_sid_t
*sid
)
414 status
= mbr_uuid_to_sid_type(uu
, sid
, &type
);
415 if (status
!= 0) return status
;
424 mbr_check_membership(const uuid_t user
, const uuid_t group
, int *ismember
)
426 return mbr_check_membership_ext(ID_TYPE_UUID
, user
, sizeof(uuid_t
), ID_TYPE_UUID
, group
, 0, ismember
);
430 mbr_check_membership_refresh(const uuid_t user
, uuid_t group
, int *ismember
)
432 return mbr_check_membership_ext(ID_TYPE_UUID
, user
, sizeof(uuid_t
), ID_TYPE_UUID
, group
, 1, ismember
);
436 mbr_check_membership_ext(int userid_type
, const void *userid
, size_t userid_size
, int groupid_type
, const void *groupid
, int refresh
, int *isMember
)
439 xpc_object_t payload
, reply
;
442 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
443 if (payload
== NULL
) return ENOMEM
;
445 xpc_dictionary_set_int64(payload
, "user_idtype", userid_type
);
446 xpc_dictionary_set_data(payload
, "user_id", userid
, userid_size
);
447 xpc_dictionary_set_int64(payload
, "group_idtype", groupid_type
);
449 switch (groupid_type
) {
450 case ID_TYPE_GROUPNAME
:
451 case ID_TYPE_GROUP_NFS
:
452 xpc_dictionary_set_data(payload
, "group_id", groupid
, strlen(groupid
));
456 xpc_dictionary_set_data(payload
, "group_id", groupid
, sizeof(id_t
));
460 xpc_dictionary_set_data(payload
, "group_id", groupid
, sizeof(nt_sid_t
));
464 xpc_dictionary_set_data(payload
, "group_id", groupid
, sizeof(uuid_t
));
473 reply
= _od_rpc_call("mbr_check_membership", payload
, _mbr_xpc_pipe
);
475 rc
= (int) xpc_dictionary_get_int64(reply
, "error");
476 (*isMember
) = xpc_dictionary_get_bool(reply
, "ismember");
483 xpc_release(payload
);
492 mbr_check_membership_by_id(uuid_t user
, gid_t group
, int *ismember
)
494 return mbr_check_membership_ext(ID_TYPE_UUID
, user
, sizeof(uuid_t
), ID_TYPE_GID
, &group
, 0, ismember
);
501 _od_rpc_call("mbr_cache_flush", NULL
, _mbr_xpc_pipe
);
509 mbr_user_name_to_uuid(const char *name
, uuid_t uu
)
511 return mbr_identifier_to_uuid(ID_TYPE_USERNAME
, name
, -1, uu
);
515 mbr_group_name_to_uuid(const char *name
, uuid_t uu
)
517 return mbr_identifier_to_uuid(ID_TYPE_GROUPNAME
, name
, -1, uu
);
521 mbr_check_service_membership(const uuid_t user
, const char *servicename
, int *ismember
)
524 xpc_object_t payload
, reply
;
527 if (ismember
== NULL
|| servicename
== NULL
) return EINVAL
;
529 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
530 if (payload
== NULL
) return EIO
;
532 xpc_dictionary_set_data(payload
, "user_id", user
, sizeof(uuid_t
));
533 xpc_dictionary_set_int64(payload
, "user_idtype", ID_TYPE_UUID
);
534 xpc_dictionary_set_string(payload
, "service", servicename
);
536 reply
= _od_rpc_call("mbr_check_service_membership", payload
, _mbr_xpc_pipe
);
538 result
= (int) xpc_dictionary_get_int64(reply
, "error");
539 (*ismember
) = xpc_dictionary_get_bool(reply
, "ismember");
546 xpc_release(payload
);
556 ConvertBytesToDecimal(char *buffer
, unsigned long long value
)
569 *temp
= '0' + (value
% 10);
578 mbr_sid_to_string(const nt_sid_t
*sid
, char *string
)
581 char *current
= string
;
586 if (sid
->sid_authcount
> NTSID_MAX_AUTHORITIES
) return EINVAL
;
588 for (i
= 0; i
< 6; i
++)
589 temp
= (temp
<< 8) | sid
->sid_authority
[i
];
594 strcpy(current
, ConvertBytesToDecimal(tempBuffer
, sid
->sid_kind
));
595 current
= current
+ strlen(current
);
598 strcpy(current
, ConvertBytesToDecimal(tempBuffer
, temp
));
600 for(i
=0; i
< sid
->sid_authcount
; i
++)
602 current
= current
+ strlen(current
);
605 strcpy(current
, ConvertBytesToDecimal(tempBuffer
, sid
->sid_authorities
[i
]));
615 mbr_string_to_sid(const char *string
, nt_sid_t
*sid
)
618 char *current
= (char *)string
+2;
622 if (string
== NULL
) return EINVAL
;
624 memset(sid
, 0, sizeof(nt_sid_t
));
625 if (string
[0] != 'S' || string
[1] != '-') return EINVAL
;
627 sid
->sid_kind
= strtol(current
, ¤t
, 10);
628 if (*current
== '\0') return EINVAL
;
630 temp
= strtoll(current
, ¤t
, 10);
632 /* convert to BigEndian before copying */
633 temp
= OSSwapHostToBigInt64(temp
);
634 memcpy(sid
->sid_authority
, ((char*)&temp
)+2, 6);
635 while (*current
!= '\0' && count
< NTSID_MAX_AUTHORITIES
)
639 sid
->sid_authorities
[count
] = (u_int32_t
)strtoll(current
, ¤t
, 10);
640 if ((sid
->sid_authorities
[count
] == 0) && (errno
== EINVAL
)) {
646 if (*current
!= '\0') return EINVAL
;
648 sid
->sid_authcount
= count
;
657 mbr_uuid_to_string(const uuid_t uu
, char *string
)
659 uuid_unparse_upper(uu
, string
);
665 mbr_string_to_uuid(const char *string
, uuid_t uu
)
667 return uuid_parse(string
, uu
);
671 mbr_set_identifier_ttl(int id_type
, const void *identifier
, size_t identifier_size
, unsigned int seconds
)
674 xpc_object_t payload
, reply
;
677 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
678 if (payload
== NULL
) return ENOMEM
;
680 xpc_dictionary_set_int64(payload
, "type", id_type
);
681 xpc_dictionary_set_data(payload
, "identifier", identifier
, identifier_size
);
682 xpc_dictionary_set_int64(payload
, "ttl", seconds
);
685 reply
= _od_rpc_call("mbr_set_identifier_ttl", payload
, _mbr_xpc_pipe
);
687 rc
= (int) xpc_dictionary_get_int64(reply
, "error");
694 xpc_release(payload
);