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, ¤t, 10); 610 if (*current == '\0') return EINVAL; 611 current++; 612 temp = strtoll(current, ¤t, 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, ¤t, 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