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