2 * Copyright (c) 2004-2018 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@
23 #include "libinfo_common.h"
26 #include <sys/errno.h>
27 #include <sys/types.h>
31 #include <mach/mach.h>
32 #include "membership.h"
33 #include "membershipPriv.h"
34 #include <servers/bootstrap.h>
35 #include <libkern/OSByteOrder.h>
38 #include <xpc/private.h>
39 #include <os/activity.h>
40 #include <opendirectory/odipc.h>
42 #include <mach-o/dyld_priv.h>
45 static const uuid_t _user_compat_prefix
= {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00};
46 static const uuid_t _group_compat_prefix
= {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00};
48 #define COMPAT_PREFIX_LEN (sizeof(uuid_t) - sizeof(id_t))
51 #define MBR_OS_ACTIVITY(_desc) \
52 os_activity_t activity __attribute__((__cleanup__(_mbr_auto_os_release))) = os_activity_create(_desc, OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT); \
53 os_activity_scope(activity)
55 #define MBR_OS_ACTIVITY(_desc)
60 int _si_opendirectory_disabled
;
61 static xpc_pipe_t __mbr_pipe
; /* use accessor */
62 static pthread_mutex_t mutex
= PTHREAD_MUTEX_INITIALIZER
;
63 __private_extern__ xpc_object_t
_od_rpc_call(const char *procname
, xpc_object_t payload
, xpc_pipe_t (*get_pipe
)(bool));
71 if (__mbr_pipe
!= NULL
) {
72 xpc_pipe_invalidate(__mbr_pipe
);
73 /* disable release due to 10649340, it will cause a minor leak for each fork without exec */
74 // xpc_release(__mbr_pipe);
78 pthread_mutex_unlock(&mutex
);
84 _mbr_fork_prepare(void)
86 pthread_mutex_lock(&mutex
);
92 _mbr_fork_parent(void)
94 pthread_mutex_unlock(&mutex
);
100 _mbr_auto_os_release(os_activity_t
*activity
)
102 os_release(*activity
);
108 _mbr_xpc_pipe(bool resetPipe
)
110 static dispatch_once_t once
;
111 xpc_pipe_t pipe
= NULL
;
113 dispatch_once(&once
, ^(void) {
116 /* if this is a build environment we ignore opendirectoryd */
117 xbs_disable
= getenv("XBS_DISABLE_LIBINFO");
118 if (xbs_disable
!= NULL
&& strcmp(xbs_disable
, "YES") == 0) {
119 _si_opendirectory_disabled
= 1;
123 pthread_atfork(_mbr_fork_prepare
, _mbr_fork_parent
, _mbr_fork_child
);
126 if (_si_opendirectory_disabled
== 1) {
130 pthread_mutex_lock(&mutex
);
132 xpc_release(__mbr_pipe
);
136 if (__mbr_pipe
== NULL
) {
137 if (!dyld_process_is_restricted() && getenv("OD_DEBUG_MODE") != NULL
) {
138 __mbr_pipe
= xpc_pipe_create(kODMachMembershipPortNameDebug
, 0);
140 __mbr_pipe
= xpc_pipe_create(kODMachMembershipPortName
, XPC_PIPE_FLAG_PRIVILEGED
);
144 if (__mbr_pipe
!= NULL
) pipe
= xpc_retain(__mbr_pipe
);
145 pthread_mutex_unlock(&mutex
);
152 _mbr_od_available(void)
155 xpc_pipe_t pipe
= _mbr_xpc_pipe(false);
165 parse_compatibility_uuid(const uuid_t uu
, id_t
*result
, int *rec_type
)
169 if (memcmp(uu
, _user_compat_prefix
, COMPAT_PREFIX_LEN
) == 0) {
170 memcpy(&tempID
, &uu
[COMPAT_PREFIX_LEN
], sizeof(tempID
));
171 (*result
) = ntohl(tempID
);
172 if (rec_type
!= NULL
) {
173 (*rec_type
) = MBR_REC_TYPE_USER
;
176 } else if (memcmp(uu
, _group_compat_prefix
, COMPAT_PREFIX_LEN
) == 0) {
177 memcpy(&tempID
, &uu
[COMPAT_PREFIX_LEN
], sizeof(tempID
));
178 (*result
) = ntohl(tempID
);
179 if (rec_type
!= NULL
) {
180 (*rec_type
) = MBR_REC_TYPE_GROUP
;
189 compatibility_name_for_id(id_t id
, int rec_type
, char **result
)
193 if ((bufsize
= sysconf(_SC_GETPW_R_SIZE_MAX
)) == -1)
196 if (rec_type
== MBR_REC_TYPE_USER
) {
197 char buffer
[bufsize
];
198 struct passwd pwd
, *pwdp
= NULL
;
200 if (getpwuid_r(id
, &pwd
, buffer
, bufsize
, &pwdp
) != 0 || pwdp
== NULL
) {
203 (*result
) = strdup(pwd
.pw_name
);
204 return (*result
) != NULL
;
205 } else if (rec_type
== MBR_REC_TYPE_GROUP
) {
206 char buffer
[bufsize
];
207 struct group grp
, *grpp
= NULL
;
209 if (getgrgid_r(id
, &grp
, buffer
, bufsize
, &grpp
) != 0 || grpp
== NULL
) {
212 (*result
) = strdup(grp
.gr_name
);
213 return (*result
) != NULL
;
219 compatibility_name_for_uuid(const uuid_t uu
, char **result
, int *rec_type
)
224 if (parse_compatibility_uuid(uu
, &id
, &temp_type
) &&
225 compatibility_name_for_id(id
, temp_type
, result
)) {
226 if (rec_type
!= NULL
) {
227 (*rec_type
) = temp_type
;
238 mbr_identifier_translate(int id_type
, const void *identifier
, size_t identifier_size
, int target_type
, void **result
, int *rec_type
)
241 xpc_object_t payload
, reply
;
244 size_t identifier_len
;
247 if (identifier
== NULL
|| result
== NULL
|| identifier_size
== 0) return EIO
;
249 if (identifier_size
== -1) {
250 identifier_size
= strlen(identifier
);
252 /* 10898647: For types that are known to be strings, send the smallest necessary amount of data. */
254 case ID_TYPE_USERNAME
:
255 case ID_TYPE_GROUPNAME
:
256 case ID_TYPE_GROUP_NFS
:
257 case ID_TYPE_USER_NFS
:
258 case ID_TYPE_X509_DN
:
259 case ID_TYPE_KERBEROS
:
261 identifier_len
= strlen(identifier
);
262 if (identifier_size
> identifier_len
) {
263 identifier_size
= identifier_len
;
269 switch (target_type
) {
272 case ID_TYPE_UID_OR_GID
:
273 /* shortcut UUIDs using compatibility prefixes */
274 if (id_type
== ID_TYPE_UUID
) {
277 if (identifier_size
!= sizeof(uuid_t
)) return EINVAL
;
279 tempRes
= malloc(sizeof(*tempRes
));
280 if (tempRes
== NULL
) return ENOMEM
;
282 if (parse_compatibility_uuid(identifier
, tempRes
, rec_type
)) {
291 /* if this is a UID or GID translation, we shortcut UID/GID 0 */
292 /* or if no OD, we return compatibility UUIDs */
295 if (identifier_size
!= sizeof(tempID
)) return EINVAL
;
297 tempID
= *((id_t
*) identifier
);
298 if ((tempID
== 0) || (_mbr_od_available() == false)) {
299 uint8_t *tempUU
= malloc(sizeof(uuid_t
));
300 if (tempUU
== NULL
) return ENOMEM
;
301 uuid_copy(tempUU
, _user_compat_prefix
);
302 *((id_t
*) &tempUU
[COMPAT_PREFIX_LEN
]) = htonl(tempID
);
304 if (rec_type
!= NULL
) {
305 (*rec_type
) = MBR_REC_TYPE_USER
;
312 if (identifier_size
!= sizeof(tempID
)) return EINVAL
;
314 tempID
= *((id_t
*) identifier
);
315 if ((tempID
== 0) || (_mbr_od_available() == false)) {
316 uint8_t *tempUU
= malloc(sizeof(uuid_t
));
317 if (tempUU
== NULL
) return ENOMEM
;
318 uuid_copy(tempUU
, _group_compat_prefix
);
319 *((id_t
*) &tempUU
[COMPAT_PREFIX_LEN
]) = htonl(tempID
);
321 if (rec_type
!= NULL
) {
322 (*rec_type
) = MBR_REC_TYPE_GROUP
;
330 case ID_TYPE_USERNAME
:
331 case ID_TYPE_GROUPNAME
:
334 /* Convert compatibility UUIDs to names in-process. */
335 if (id_type
== ID_TYPE_UUID
) {
336 if (identifier_size
!= sizeof(uuid_t
)) return EINVAL
;
337 if (compatibility_name_for_uuid(identifier
, (char **)result
, rec_type
)) {
340 } else if (id_type
== ID_TYPE_UID
) {
341 if (identifier_size
!= sizeof(tempID
)) return EINVAL
;
343 tempID
= *((id_t
*) identifier
);
344 if (compatibility_name_for_id(tempID
, MBR_REC_TYPE_USER
, (char **)result
)) {
345 if (rec_type
!= NULL
) {
346 (*rec_type
) = MBR_REC_TYPE_USER
;
350 } else if (id_type
== ID_TYPE_GID
) {
351 if (identifier_size
!= sizeof(tempID
)) return EINVAL
;
353 tempID
= *((id_t
*) identifier
);
354 if (compatibility_name_for_id(tempID
, MBR_REC_TYPE_GROUP
, (char **)result
)) {
355 if (rec_type
!= NULL
) {
356 (*rec_type
) = MBR_REC_TYPE_GROUP
;
366 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
367 if (payload
== NULL
) return EIO
;
369 MBR_OS_ACTIVITY("Membership API: translate identifier");
371 xpc_dictionary_set_int64(payload
, "requesting", target_type
);
372 xpc_dictionary_set_int64(payload
, "type", id_type
);
373 xpc_dictionary_set_data(payload
, "identifier", identifier
, identifier_size
);
375 reply
= _od_rpc_call("mbr_identifier_translate", payload
, _mbr_xpc_pipe
);
377 const void *reply_id
;
380 rc
= (int) xpc_dictionary_get_int64(reply
, "error");
382 reply_id
= xpc_dictionary_get_data(reply
, "identifier", &idLen
);
383 if (reply_id
!= NULL
) {
384 char *identifier
= malloc(idLen
);
385 if (identifier
== NULL
) return ENOMEM
;
387 memcpy(identifier
, reply_id
, idLen
); // should already be NULL terminated, etc.
388 (*result
) = identifier
;
390 if (rec_type
!= NULL
) {
391 (*rec_type
) = (int) xpc_dictionary_get_int64(reply
, "rectype");
402 xpc_release(payload
);
410 mbr_uid_to_uuid(uid_t id
, uuid_t uu
)
412 return mbr_identifier_to_uuid(ID_TYPE_UID
, &id
, sizeof(id
), uu
);
417 mbr_gid_to_uuid(gid_t id
, uuid_t uu
)
419 return mbr_identifier_to_uuid(ID_TYPE_GID
, &id
, sizeof(id
), uu
);
424 mbr_uuid_to_id(const uuid_t uu
, uid_t
*id
, int *id_type
)
430 rc
= mbr_identifier_translate(ID_TYPE_UUID
, uu
, sizeof(uuid_t
), ID_TYPE_UID_OR_GID
, (void **) &result
, &local_type
);
432 switch (local_type
) {
433 case MBR_REC_TYPE_GROUP
:
434 (*id_type
) = ID_TYPE_GID
;
437 case MBR_REC_TYPE_USER
:
438 (*id_type
) = ID_TYPE_UID
;
455 mbr_sid_to_uuid(const nt_sid_t
*sid
, uuid_t uu
)
458 return mbr_identifier_to_uuid(ID_TYPE_SID
, sid
, sizeof(*sid
), uu
);
466 mbr_identifier_to_uuid(int id_type
, const void *identifier
, size_t identifier_size
, uuid_t uu
)
471 rc
= mbr_identifier_translate(id_type
, identifier
, identifier_size
, ID_TYPE_UUID
, (void **) &result
, NULL
);
473 uuid_copy(uu
, result
);
476 else if ((rc
== EIO
) && (_mbr_od_available() == false)) {
478 case ID_TYPE_USERNAME
:
480 struct passwd
*pw
= getpwnam(identifier
);
482 rc
= mbr_identifier_translate(ID_TYPE_UID
, &(pw
->pw_uid
), sizeof(id_t
), ID_TYPE_UUID
, (void **) &result
, NULL
);
484 uuid_copy(uu
, result
);
490 case ID_TYPE_GROUPNAME
:
492 struct group
*grp
= getgrnam(identifier
);
494 rc
= mbr_identifier_translate(ID_TYPE_GID
, &(grp
->gr_gid
), sizeof(id_t
), ID_TYPE_UUID
, (void **) &result
, NULL
);
496 uuid_copy(uu
, result
);
513 mbr_uuid_to_sid_type(const uuid_t uu
, nt_sid_t
*sid
, int *id_type
)
520 rc
= mbr_identifier_translate(ID_TYPE_UUID
, uu
, sizeof(uuid_t
), ID_TYPE_SID
, &result
, &local_type
);
522 memcpy(sid
, result
, sizeof(nt_sid_t
));
523 if (id_type
!= NULL
) {
525 switch (local_type
) {
526 case MBR_REC_TYPE_USER
:
527 (*id_type
) = SID_TYPE_USER
;
530 case MBR_REC_TYPE_GROUP
:
531 (*id_type
) = SID_TYPE_GROUP
;
550 mbr_uuid_to_sid(const uuid_t uu
, nt_sid_t
*sid
)
557 status
= mbr_uuid_to_sid_type(uu
, sid
, &type
);
558 if (status
!= 0) return status
;
568 mbr_check_membership(const uuid_t user
, const uuid_t group
, int *ismember
)
570 return mbr_check_membership_ext(ID_TYPE_UUID
, user
, sizeof(uuid_t
), ID_TYPE_UUID
, group
, 0, ismember
);
575 mbr_check_membership_refresh(const uuid_t user
, uuid_t group
, int *ismember
)
577 return mbr_check_membership_ext(ID_TYPE_UUID
, user
, sizeof(uuid_t
), ID_TYPE_UUID
, group
, 1, ismember
);
582 mbr_check_membership_ext(int userid_type
, const void *userid
, size_t userid_size
, int groupid_type
, const void *groupid
, int refresh
, int *isMember
)
585 xpc_object_t payload
, reply
;
588 MBR_OS_ACTIVITY("Membership API: Validating user is a member of group");
589 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
590 if (payload
== NULL
) return ENOMEM
;
592 xpc_dictionary_set_int64(payload
, "user_idtype", userid_type
);
593 xpc_dictionary_set_data(payload
, "user_id", userid
, userid_size
);
594 xpc_dictionary_set_int64(payload
, "group_idtype", groupid_type
);
595 xpc_dictionary_set_bool(payload
, "refresh", refresh
);
597 switch (groupid_type
) {
598 case ID_TYPE_GROUPNAME
:
599 case ID_TYPE_GROUP_NFS
:
600 xpc_dictionary_set_data(payload
, "group_id", groupid
, strlen(groupid
));
604 xpc_dictionary_set_data(payload
, "group_id", groupid
, sizeof(id_t
));
608 xpc_dictionary_set_data(payload
, "group_id", groupid
, sizeof(nt_sid_t
));
612 xpc_dictionary_set_data(payload
, "group_id", groupid
, sizeof(uuid_t
));
621 reply
= _od_rpc_call("mbr_check_membership", payload
, _mbr_xpc_pipe
);
623 rc
= (int) xpc_dictionary_get_int64(reply
, "error");
624 (*isMember
) = xpc_dictionary_get_bool(reply
, "ismember");
631 xpc_release(payload
);
641 mbr_check_membership_by_id(uuid_t user
, gid_t group
, int *ismember
)
643 return mbr_check_membership_ext(ID_TYPE_UUID
, user
, sizeof(uuid_t
), ID_TYPE_GID
, &group
, 0, ismember
);
651 MBR_OS_ACTIVITY("Membership API: Flush the membership cache");
652 xpc_object_t result
= _od_rpc_call("mbr_cache_flush", NULL
, _mbr_xpc_pipe
);
664 mbr_user_name_to_uuid(const char *name
, uuid_t uu
)
666 return mbr_identifier_to_uuid(ID_TYPE_USERNAME
, name
, -1, uu
);
671 mbr_group_name_to_uuid(const char *name
, uuid_t uu
)
673 return mbr_identifier_to_uuid(ID_TYPE_GROUPNAME
, name
, -1, uu
);
678 mbr_check_service_membership(const uuid_t user
, const char *servicename
, int *ismember
)
681 xpc_object_t payload
, reply
;
684 if (ismember
== NULL
|| servicename
== NULL
) return EINVAL
;
686 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
687 if (payload
== NULL
) return EIO
;
689 MBR_OS_ACTIVITY("Membership API: Validating user is allowed by service");
691 xpc_dictionary_set_data(payload
, "user_id", user
, sizeof(uuid_t
));
692 xpc_dictionary_set_int64(payload
, "user_idtype", ID_TYPE_UUID
);
693 xpc_dictionary_set_string(payload
, "service", servicename
);
695 reply
= _od_rpc_call("mbr_check_service_membership", payload
, _mbr_xpc_pipe
);
697 result
= (int) xpc_dictionary_get_int64(reply
, "error");
698 (*ismember
) = xpc_dictionary_get_bool(reply
, "ismember");
705 xpc_release(payload
);
715 ConvertBytesToDecimal(char *buffer
, unsigned long long value
)
728 *temp
= '0' + (value
% 10);
738 mbr_sid_to_string(const nt_sid_t
*sid
, char *string
)
741 char *current
= string
;
746 if (sid
->sid_authcount
> NTSID_MAX_AUTHORITIES
) return EINVAL
;
748 for (i
= 0; i
< 6; i
++)
749 temp
= (temp
<< 8) | sid
->sid_authority
[i
];
754 strcpy(current
, ConvertBytesToDecimal(tempBuffer
, sid
->sid_kind
));
755 current
= current
+ strlen(current
);
758 strcpy(current
, ConvertBytesToDecimal(tempBuffer
, temp
));
760 for(i
=0; i
< sid
->sid_authcount
; i
++)
762 current
= current
+ strlen(current
);
765 strcpy(current
, ConvertBytesToDecimal(tempBuffer
, sid
->sid_authorities
[i
]));
776 mbr_string_to_sid(const char *string
, nt_sid_t
*sid
)
779 char *current
= (char *)string
+2;
783 if (string
== NULL
) return EINVAL
;
785 memset(sid
, 0, sizeof(nt_sid_t
));
786 if (string
[0] != 'S' || string
[1] != '-') return EINVAL
;
788 sid
->sid_kind
= strtol(current
, ¤t
, 10);
789 if (*current
== '\0') return EINVAL
;
791 temp
= strtoll(current
, ¤t
, 10);
793 /* convert to BigEndian before copying */
794 temp
= OSSwapHostToBigInt64(temp
);
795 memcpy(sid
->sid_authority
, ((char*)&temp
)+2, 6);
796 while (*current
!= '\0' && count
< NTSID_MAX_AUTHORITIES
)
800 sid
->sid_authorities
[count
] = (u_int32_t
)strtoll(current
, ¤t
, 10);
801 if ((sid
->sid_authorities
[count
] == 0) && (errno
== EINVAL
)) {
807 if (*current
!= '\0') return EINVAL
;
809 sid
->sid_authcount
= count
;
818 mbr_uuid_to_string(const uuid_t uu
, char *string
)
820 uuid_unparse_upper(uu
, string
);
826 mbr_string_to_uuid(const char *string
, uuid_t uu
)
828 return uuid_parse(string
, uu
);
833 mbr_set_identifier_ttl(int id_type
, const void *identifier
, size_t identifier_size
, unsigned int seconds
)
836 xpc_object_t payload
, reply
;
839 payload
= xpc_dictionary_create(NULL
, NULL
, 0);
840 if (payload
== NULL
) return ENOMEM
;
842 MBR_OS_ACTIVITY("Membership API: Change the TTL of a given identifier in SystemCache");
844 xpc_dictionary_set_int64(payload
, "type", id_type
);
845 xpc_dictionary_set_data(payload
, "identifier", identifier
, identifier_size
);
846 xpc_dictionary_set_int64(payload
, "ttl", seconds
);
849 reply
= _od_rpc_call("mbr_set_identifier_ttl", payload
, _mbr_xpc_pipe
);
851 rc
= (int) xpc_dictionary_get_int64(reply
, "error");
858 xpc_release(payload
);