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