Libinfo-476.tar.gz
[apple/libinfo.git] / membership.subproj / membership.c
1 /*
2 * Copyright (c) 2004-2015 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23 #include <stdlib.h>
24 #include <sys/errno.h>
25 #include <sys/types.h>
26 #include <pwd.h>
27 #include <grp.h>
28 #include <mach/mach.h>
29 #include "membership.h"
30 #include "membershipPriv.h"
31 #include <servers/bootstrap.h>
32 #include <libkern/OSByteOrder.h>
33 #ifdef DS_AVAILABLE
34 #include <xpc/xpc.h>
35 #include <xpc/private.h>
36 #include <opendirectory/odipc.h>
37 #include <pthread.h>
38 #include <mach-o/dyld_priv.h>
39 #endif
40
41 static const uuid_t _user_compat_prefix = {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00};
42 static const uuid_t _group_compat_prefix = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00};
43
44 #define COMPAT_PREFIX_LEN (sizeof(uuid_t) - sizeof(id_t))
45
46 #ifdef DS_AVAILABLE
47
48 int _si_opendirectory_disabled;
49 static xpc_pipe_t __mbr_pipe; /* use accessor */
50 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
51 __private_extern__ xpc_object_t _od_rpc_call(const char *procname, xpc_object_t payload, xpc_pipe_t (*get_pipe)(bool));
52
53 #endif
54
55 #ifdef DS_AVAILABLE
56 static void
57 _mbr_fork_child(void)
58 {
59 if (__mbr_pipe != NULL) {
60 xpc_pipe_invalidate(__mbr_pipe);
61 /* disable release due to 10649340, it will cause a minor leak for each fork without exec */
62 // xpc_release(__mbr_pipe);
63 __mbr_pipe = NULL;
64 }
65
66 pthread_mutex_unlock(&mutex);
67 }
68 #endif
69
70 #ifdef DS_AVAILABLE
71 static void
72 _mbr_fork_prepare(void)
73 {
74 pthread_mutex_lock(&mutex);
75 }
76 #endif
77
78 #ifdef DS_AVAILABLE
79 static void
80 _mbr_fork_parent(void)
81 {
82 pthread_mutex_unlock(&mutex);
83 }
84 #endif
85
86 #ifdef DS_AVAILABLE
87 XPC_RETURNS_RETAINED
88 static xpc_pipe_t
89 _mbr_xpc_pipe(bool resetPipe)
90 {
91 static dispatch_once_t once;
92 xpc_pipe_t pipe = NULL;
93
94 dispatch_once(&once, ^(void) {
95 char *xbs_disable;
96
97 /* if this is a build environment we ignore opendirectoryd */
98 xbs_disable = getenv("XBS_DISABLE_LIBINFO");
99 if (xbs_disable != NULL && strcmp(xbs_disable, "YES") == 0) {
100 _si_opendirectory_disabled = 1;
101 return;
102 }
103
104 pthread_atfork(_mbr_fork_prepare, _mbr_fork_parent, _mbr_fork_child);
105 });
106
107 if (_si_opendirectory_disabled == 1) {
108 return NULL;
109 }
110
111 pthread_mutex_lock(&mutex);
112 if (resetPipe) {
113 xpc_release(__mbr_pipe);
114 __mbr_pipe = NULL;
115 }
116
117 if (__mbr_pipe == NULL) {
118 if (!dyld_process_is_restricted() && getenv("OD_DEBUG_MODE") != NULL) {
119 __mbr_pipe = xpc_pipe_create(kODMachMembershipPortNameDebug, 0);
120 } else {
121 __mbr_pipe = xpc_pipe_create(kODMachMembershipPortName, XPC_PIPE_FLAG_PRIVILEGED);
122 }
123 }
124
125 if (__mbr_pipe != NULL) pipe = xpc_retain(__mbr_pipe);
126 pthread_mutex_unlock(&mutex);
127
128 return pipe;
129 }
130 #endif
131
132 static bool
133 _mbr_od_available(void)
134 {
135 #if DS_AVAILABLE
136 xpc_pipe_t pipe = _mbr_xpc_pipe(false);
137 if (pipe != NULL) {
138 xpc_release(pipe);
139 return true;
140 }
141 #endif
142 return false;
143 }
144
145 int
146 mbr_identifier_translate(int id_type, const void *identifier, size_t identifier_size, int target_type, void **result, int *rec_type)
147 {
148 #if DS_AVAILABLE
149 xpc_object_t payload, reply;
150 #endif
151 id_t tempID;
152 size_t identifier_len;
153 int rc = EIO;
154
155 if (identifier == NULL || result == NULL || identifier_size == 0) return EIO;
156
157 if (identifier_size == -1) {
158 identifier_size = strlen(identifier);
159 } else {
160 /* 10898647: For types that are known to be strings, send the smallest necessary amount of data. */
161 switch (id_type) {
162 case ID_TYPE_USERNAME:
163 case ID_TYPE_GROUPNAME:
164 case ID_TYPE_GROUP_NFS:
165 case ID_TYPE_USER_NFS:
166 case ID_TYPE_X509_DN:
167 case ID_TYPE_KERBEROS:
168 case ID_TYPE_NAME:
169 identifier_len = strlen(identifier);
170 if (identifier_size > identifier_len) {
171 identifier_size = identifier_len;
172 }
173 break;
174 }
175 }
176
177 switch (target_type) {
178 case ID_TYPE_GID:
179 case ID_TYPE_UID:
180 case ID_TYPE_UID_OR_GID:
181 /* shortcut UUIDs using compatibilty prefixes */
182 if (id_type == ID_TYPE_UUID) {
183 const uint8_t *uu = identifier;
184
185 if (identifier_size != sizeof(uuid_t)) return EINVAL;
186
187 if (memcmp(uu, _user_compat_prefix, COMPAT_PREFIX_LEN) == 0) {
188 id_t *tempRes = malloc(sizeof(*tempRes));
189 if (tempRes == NULL) return ENOMEM;
190 memcpy(&tempID, &uu[COMPAT_PREFIX_LEN], sizeof(tempID));
191 (*tempRes) = ntohl(tempID);
192 (*result) = tempRes;
193 if (rec_type != NULL) {
194 (*rec_type) = MBR_REC_TYPE_USER;
195 }
196 return 0;
197 } else if (memcmp(uu, _group_compat_prefix, COMPAT_PREFIX_LEN) == 0) {
198 id_t *tempRes = malloc(sizeof(*tempRes));
199 if (tempRes == NULL) return ENOMEM;
200 memcpy(&tempID, &uu[COMPAT_PREFIX_LEN], sizeof(tempID));
201 (*tempRes) = ntohl(tempID);
202 (*result) = tempRes;
203 if (rec_type != NULL) {
204 (*rec_type) = MBR_REC_TYPE_GROUP;
205 }
206 return 0;
207 }
208 }
209 break;
210
211 case ID_TYPE_UUID:
212 /* if this is a UID or GID translation, we shortcut UID/GID 0 */
213 /* or if no OD, we return compatibility UUIDs */
214 switch (id_type) {
215 case ID_TYPE_UID:
216 if (identifier_size != sizeof(tempID)) return EINVAL;
217
218 tempID = *((id_t *) identifier);
219 if ((tempID == 0) || (_mbr_od_available() == false)) {
220 uint8_t *tempUU = malloc(sizeof(uuid_t));
221 if (tempUU == NULL) return ENOMEM;
222 uuid_copy(tempUU, _user_compat_prefix);
223 *((id_t *) &tempUU[COMPAT_PREFIX_LEN]) = htonl(tempID);
224 (*result) = tempUU;
225 if (rec_type != NULL) {
226 (*rec_type) = MBR_REC_TYPE_USER;
227 }
228 return 0;
229 }
230 break;
231
232 case ID_TYPE_GID:
233 if (identifier_size != sizeof(tempID)) return EINVAL;
234
235 tempID = *((id_t *) identifier);
236 if ((tempID == 0) || (_mbr_od_available() == false)) {
237 uint8_t *tempUU = malloc(sizeof(uuid_t));
238 if (tempUU == NULL) return ENOMEM;
239 uuid_copy(tempUU, _group_compat_prefix);
240 *((id_t *) &tempUU[COMPAT_PREFIX_LEN]) = htonl(tempID);
241 (*result) = tempUU;
242 if (rec_type != NULL) {
243 (*rec_type) = MBR_REC_TYPE_GROUP;
244 }
245 return 0;
246 }
247 break;
248 }
249 break;
250 }
251
252 #if DS_AVAILABLE
253 payload = xpc_dictionary_create(NULL, NULL, 0);
254 if (payload == NULL) return EIO;
255
256 xpc_dictionary_set_int64(payload, "requesting", target_type);
257 xpc_dictionary_set_int64(payload, "type", id_type);
258 xpc_dictionary_set_data(payload, "identifier", identifier, identifier_size);
259
260 reply = _od_rpc_call("mbr_identifier_translate", payload, _mbr_xpc_pipe);
261 if (reply != NULL) {
262 const void *reply_id;
263 size_t idLen;
264
265 rc = (int) xpc_dictionary_get_int64(reply, "error");
266 if (rc == 0) {
267 reply_id = xpc_dictionary_get_data(reply, "identifier", &idLen);
268 if (reply_id != NULL) {
269 char *identifier = malloc(idLen);
270 if (identifier == NULL) return ENOMEM;
271
272 memcpy(identifier, reply_id, idLen); // should already be NULL terminated, etc.
273 (*result) = identifier;
274
275 if (rec_type != NULL) {
276 (*rec_type) = (int) xpc_dictionary_get_int64(reply, "rectype");
277 }
278 } else {
279 (*result) = NULL;
280 rc = ENOENT;
281 }
282 }
283
284 xpc_release(reply);
285 }
286
287 xpc_release(payload);
288 #endif
289
290 return rc;
291 }
292
293 int
294 mbr_uid_to_uuid(uid_t id, uuid_t uu)
295 {
296 return mbr_identifier_to_uuid(ID_TYPE_UID, &id, sizeof(id), uu);
297 }
298
299 int
300 mbr_gid_to_uuid(gid_t id, uuid_t uu)
301 {
302 return mbr_identifier_to_uuid(ID_TYPE_GID, &id, sizeof(id), uu);
303 }
304
305 int
306 mbr_uuid_to_id(const uuid_t uu, uid_t *id, int *id_type)
307 {
308 id_t *result;
309 int local_type;
310 int rc;
311
312 rc = mbr_identifier_translate(ID_TYPE_UUID, uu, sizeof(uuid_t), ID_TYPE_UID_OR_GID, (void **) &result, &local_type);
313 if (rc == 0) {
314 switch (local_type) {
315 case MBR_REC_TYPE_GROUP:
316 (*id_type) = ID_TYPE_GID;
317 break;
318
319 case MBR_REC_TYPE_USER:
320 (*id_type) = ID_TYPE_UID;
321 break;
322
323 default:
324 (*id_type) = -1;
325 break;
326 }
327
328 (*id) = (*result);
329 free(result);
330 }
331
332 return rc;
333 }
334
335 int
336 mbr_sid_to_uuid(const nt_sid_t *sid, uuid_t uu)
337 {
338 #ifdef DS_AVAILABLE
339 return mbr_identifier_to_uuid(ID_TYPE_SID, sid, sizeof(*sid), uu);
340 #else
341 return EIO;
342 #endif
343 }
344
345 int
346 mbr_identifier_to_uuid(int id_type, const void *identifier, size_t identifier_size, uuid_t uu)
347 {
348 uint8_t *result;
349 int rc;
350
351 rc = mbr_identifier_translate(id_type, identifier, identifier_size, ID_TYPE_UUID, (void **) &result, NULL);
352 if (rc == 0) {
353 uuid_copy(uu, result);
354 free(result);
355 }
356 else if ((rc == EIO) && (_mbr_od_available() == false)) {
357 switch (id_type) {
358 case ID_TYPE_USERNAME:
359 {
360 struct passwd *pw = getpwnam(identifier);
361 if (pw) {
362 rc = mbr_identifier_translate(ID_TYPE_UID, &(pw->pw_uid), sizeof(id_t), ID_TYPE_UUID, (void **) &result, NULL);
363 if (rc == 0) {
364 uuid_copy(uu, result);
365 free(result);
366 }
367 }
368 break;
369 }
370 case ID_TYPE_GROUPNAME:
371 {
372 struct group *grp = getgrnam(identifier);
373 if (grp) {
374 rc = mbr_identifier_translate(ID_TYPE_GID, &(grp->gr_gid), sizeof(id_t), ID_TYPE_UUID, (void **) &result, NULL);
375 if (rc == 0) {
376 uuid_copy(uu, result);
377 free(result);
378 }
379 }
380 break;
381 }
382
383 default:
384 break;
385 }
386 }
387
388 return rc;
389 }
390
391 int
392 mbr_uuid_to_sid_type(const uuid_t uu, nt_sid_t *sid, int *id_type)
393 {
394 #ifdef DS_AVAILABLE
395 void *result;
396 int local_type;
397 int rc;
398
399 rc = mbr_identifier_translate(ID_TYPE_UUID, uu, sizeof(uuid_t), ID_TYPE_SID, &result, &local_type);
400 if (rc == 0) {
401 memcpy(sid, result, sizeof(nt_sid_t));
402 if (id_type != NULL) {
403 /* remap ID types */
404 switch (local_type) {
405 case MBR_REC_TYPE_USER:
406 (*id_type) = SID_TYPE_USER;
407 break;
408
409 case MBR_REC_TYPE_GROUP:
410 (*id_type) = SID_TYPE_GROUP;
411 break;
412
413 default:
414 break;
415 }
416 }
417
418 free(result);
419 }
420
421 return rc;
422 #else
423 return EIO;
424 #endif
425 }
426
427 int
428 mbr_uuid_to_sid(const uuid_t uu, nt_sid_t *sid)
429 {
430 #ifdef DS_AVAILABLE
431 int type, status;
432
433 type = 0;
434
435 status = mbr_uuid_to_sid_type(uu, sid, &type);
436 if (status != 0) return status;
437
438 return 0;
439 #else
440 return EIO;
441 #endif
442 }
443
444 int
445 mbr_check_membership(const uuid_t user, const uuid_t group, int *ismember)
446 {
447 return mbr_check_membership_ext(ID_TYPE_UUID, user, sizeof(uuid_t), ID_TYPE_UUID, group, 0, ismember);
448 }
449
450 int
451 mbr_check_membership_refresh(const uuid_t user, uuid_t group, int *ismember)
452 {
453 return mbr_check_membership_ext(ID_TYPE_UUID, user, sizeof(uuid_t), ID_TYPE_UUID, group, 1, ismember);
454 }
455
456 int
457 mbr_check_membership_ext(int userid_type, const void *userid, size_t userid_size, int groupid_type, const void *groupid, int refresh, int *isMember)
458 {
459 #ifdef DS_AVAILABLE
460 xpc_object_t payload, reply;
461 int rc = 0;
462
463 payload = xpc_dictionary_create(NULL, NULL, 0);
464 if (payload == NULL) return ENOMEM;
465
466 xpc_dictionary_set_int64(payload, "user_idtype", userid_type);
467 xpc_dictionary_set_data(payload, "user_id", userid, userid_size);
468 xpc_dictionary_set_int64(payload, "group_idtype", groupid_type);
469 xpc_dictionary_set_bool(payload, "refresh", refresh);
470
471 switch (groupid_type) {
472 case ID_TYPE_GROUPNAME:
473 case ID_TYPE_GROUP_NFS:
474 xpc_dictionary_set_data(payload, "group_id", groupid, strlen(groupid));
475 break;
476
477 case ID_TYPE_GID:
478 xpc_dictionary_set_data(payload, "group_id", groupid, sizeof(id_t));
479 break;
480
481 case ID_TYPE_SID:
482 xpc_dictionary_set_data(payload, "group_id", groupid, sizeof(nt_sid_t));
483 break;
484
485 case ID_TYPE_UUID:
486 xpc_dictionary_set_data(payload, "group_id", groupid, sizeof(uuid_t));
487 break;
488
489 default:
490 rc = EINVAL;
491 break;
492 }
493
494 if (rc == 0) {
495 reply = _od_rpc_call("mbr_check_membership", payload, _mbr_xpc_pipe);
496 if (reply != NULL) {
497 rc = (int) xpc_dictionary_get_int64(reply, "error");
498 (*isMember) = xpc_dictionary_get_bool(reply, "ismember");
499 xpc_release(reply);
500 } else {
501 rc = EIO;
502 }
503 }
504
505 xpc_release(payload);
506
507 return rc;
508 #else
509 return EIO;
510 #endif
511 }
512
513 int
514 mbr_check_membership_by_id(uuid_t user, gid_t group, int *ismember)
515 {
516 return mbr_check_membership_ext(ID_TYPE_UUID, user, sizeof(uuid_t), ID_TYPE_GID, &group, 0, ismember);
517 }
518
519 int
520 mbr_reset_cache()
521 {
522 #ifdef DS_AVAILABLE
523 _od_rpc_call("mbr_cache_flush", NULL, _mbr_xpc_pipe);
524 return 0;
525 #else
526 return EIO;
527 #endif
528 }
529
530 int
531 mbr_user_name_to_uuid(const char *name, uuid_t uu)
532 {
533 return mbr_identifier_to_uuid(ID_TYPE_USERNAME, name, -1, uu);
534 }
535
536 int
537 mbr_group_name_to_uuid(const char *name, uuid_t uu)
538 {
539 return mbr_identifier_to_uuid(ID_TYPE_GROUPNAME, name, -1, uu);
540 }
541
542 int
543 mbr_check_service_membership(const uuid_t user, const char *servicename, int *ismember)
544 {
545 #ifdef DS_AVAILABLE
546 xpc_object_t payload, reply;
547 int result = EIO;
548
549 if (ismember == NULL || servicename == NULL) return EINVAL;
550
551 payload = xpc_dictionary_create(NULL, NULL, 0);
552 if (payload == NULL) return EIO;
553
554 xpc_dictionary_set_data(payload, "user_id", user, sizeof(uuid_t));
555 xpc_dictionary_set_int64(payload, "user_idtype", ID_TYPE_UUID);
556 xpc_dictionary_set_string(payload, "service", servicename);
557
558 reply = _od_rpc_call("mbr_check_service_membership", payload, _mbr_xpc_pipe);
559 if (reply != NULL) {
560 result = (int) xpc_dictionary_get_int64(reply, "error");
561 (*ismember) = xpc_dictionary_get_bool(reply, "ismember");
562
563 xpc_release(reply);
564 } else {
565 (*ismember) = 0;
566 }
567
568 xpc_release(payload);
569
570 return result;
571 #else
572 return EIO;
573 #endif
574 }
575
576 #ifdef DS_AVAILABLE
577 static char *
578 ConvertBytesToDecimal(char *buffer, unsigned long long value)
579 {
580 char *temp;
581 buffer[24] = '\0';
582 buffer[23] = '0';
583
584 if (value == 0)
585 return &buffer[23];
586
587 temp = &buffer[24];
588 while (value != 0)
589 {
590 temp--;
591 *temp = '0' + (value % 10);
592 value /= 10;
593 }
594
595 return temp;
596 }
597 #endif
598
599 int
600 mbr_sid_to_string(const nt_sid_t *sid, char *string)
601 {
602 #ifdef DS_AVAILABLE
603 char *current = string;
604 long long temp = 0;
605 int i;
606 char tempBuffer[25];
607
608 if (sid->sid_authcount > NTSID_MAX_AUTHORITIES) return EINVAL;
609
610 for (i = 0; i < 6; i++)
611 temp = (temp << 8) | sid->sid_authority[i];
612
613 current[0] = 'S';
614 current[1] = '-';
615 current += 2;
616 strcpy(current, ConvertBytesToDecimal(tempBuffer, sid->sid_kind));
617 current = current + strlen(current);
618 *current = '-';
619 current++;
620 strcpy(current, ConvertBytesToDecimal(tempBuffer, temp));
621
622 for(i=0; i < sid->sid_authcount; i++)
623 {
624 current = current + strlen(current);
625 *current = '-';
626 current++;
627 strcpy(current, ConvertBytesToDecimal(tempBuffer, sid->sid_authorities[i]));
628 }
629
630 return 0;
631 #else
632 return EIO;
633 #endif
634 }
635
636 int
637 mbr_string_to_sid(const char *string, nt_sid_t *sid)
638 {
639 #ifdef DS_AVAILABLE
640 char *current = (char *)string+2;
641 int count = 0;
642 long long temp;
643
644 if (string == NULL) return EINVAL;
645
646 memset(sid, 0, sizeof(nt_sid_t));
647 if (string[0] != 'S' || string[1] != '-') return EINVAL;
648
649 sid->sid_kind = strtol(current, &current, 10);
650 if (*current == '\0') return EINVAL;
651 current++;
652 temp = strtoll(current, &current, 10);
653
654 /* convert to BigEndian before copying */
655 temp = OSSwapHostToBigInt64(temp);
656 memcpy(sid->sid_authority, ((char*)&temp)+2, 6);
657 while (*current != '\0' && count < NTSID_MAX_AUTHORITIES)
658 {
659 current++;
660 errno = 0;
661 sid->sid_authorities[count] = (u_int32_t)strtoll(current, &current, 10);
662 if ((sid->sid_authorities[count] == 0) && (errno == EINVAL)) {
663 return EINVAL;
664 }
665 count++;
666 }
667
668 if (*current != '\0') return EINVAL;
669
670 sid->sid_authcount = count;
671
672 return 0;
673 #else
674 return EIO;
675 #endif
676 }
677
678 int
679 mbr_uuid_to_string(const uuid_t uu, char *string)
680 {
681 uuid_unparse_upper(uu, string);
682
683 return 0;
684 }
685
686 int
687 mbr_string_to_uuid(const char *string, uuid_t uu)
688 {
689 return uuid_parse(string, uu);
690 }
691
692 int
693 mbr_set_identifier_ttl(int id_type, const void *identifier, size_t identifier_size, unsigned int seconds)
694 {
695 #ifdef DS_AVAILABLE
696 xpc_object_t payload, reply;
697 int rc = 0;
698
699 payload = xpc_dictionary_create(NULL, NULL, 0);
700 if (payload == NULL) return ENOMEM;
701
702 xpc_dictionary_set_int64(payload, "type", id_type);
703 xpc_dictionary_set_data(payload, "identifier", identifier, identifier_size);
704 xpc_dictionary_set_int64(payload, "ttl", seconds);
705
706 if (rc == 0) {
707 reply = _od_rpc_call("mbr_set_identifier_ttl", payload, _mbr_xpc_pipe);
708 if (reply != NULL) {
709 rc = (int) xpc_dictionary_get_int64(reply, "error");
710 xpc_release(reply);
711 } else {
712 rc = EIO;
713 }
714 }
715
716 xpc_release(payload);
717
718 return rc;
719 #else
720 return EIO;
721 #endif
722 }