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
38static const uuid_t _user_compat_prefix = {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00};
39static 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
45int _si_opendirectory_disabled;
46static xpc_pipe_t __mbr_pipe; /* use accessor */
47static 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
53static 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
68static void
69_mbr_fork_prepare(void)
70{
71	pthread_mutex_lock(&mutex);
72}
73#endif
74
75#ifdef DS_AVAILABLE
76static void
77_mbr_fork_parent(void)
78{
79	pthread_mutex_unlock(&mutex);
80}
81#endif
82
83#ifdef DS_AVAILABLE
84XPC_RETURNS_RETAINED
85static 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
129static 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
142int
143mbr_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
285int
286mbr_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
291int
292mbr_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
297int
298mbr_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
327int
328mbr_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
337int
338mbr_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
352int
353mbr_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
388int
389mbr_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
405int
406mbr_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
411int
412mbr_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
417int
418mbr_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
473int
474mbr_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
479int
480mbr_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
490int
491mbr_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
496int
497mbr_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
502int
503mbr_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
537static char *
538ConvertBytesToDecimal(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
559int
560mbr_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
596int
597mbr_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
638int
639mbr_uuid_to_string(const uuid_t uu, char *string)
640{
641	uuid_unparse_upper(uu, string);
642
643	return 0;
644}
645
646int
647mbr_string_to_uuid(const char *string, uuid_t uu)
648{
649	return uuid_parse(string, uu);
650}
651
652int
653mbr_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}
683