1#include <asl.h> 2#include <stdio.h> 3#include <unistd.h> 4#include <sys/stat.h> 5 6#include <security/pam_appl.h> 7#include <security/pam_modules.h> 8#include <security/openpam.h> 9 10 11#include <CoreFoundation/CoreFoundation.h> 12#include <DirectoryService/DirectoryService.h> 13#include <OpenDirectory/OpenDirectory.h> 14#include <OpenDirectory/OpenDirectoryPriv.h> 15#include <ServerInformation/ServerInformation.h> 16 17#include "Common.h" 18 19#if !defined(kDSValueAuthAuthorityDisabledUser) 20#define kDSValueAuthAuthorityDisabledUser ";DisabledUser;" 21#endif 22 23#define kOSInstall_mpkg "/System/Installation/Packages/OSInstall.mpkg" 24#define kOSInstall_collection "/System/Installation/Packages/OSInstall.collection" 25 26enum { 27 kWaitSeconds = 1, 28 kMaxIterationCount = 30 29}; 30 31int 32cfboolean_get_value(CFTypeRef p) 33{ 34 int value = 0; 35 int retval = 0; 36 37 if (NULL == p) { 38 goto cleanup; 39 } 40 41 if (CFBooleanGetTypeID() == CFGetTypeID(p)) 42 retval = CFBooleanGetValue(p); 43 else if (CFNumberGetTypeID() == CFGetTypeID(p) && CFNumberGetValue(p, kCFNumberIntType, &value)) 44 retval = value; 45 else 46 retval = 0; 47 48cleanup: 49 if (PAM_SUCCESS != retval) 50 openpam_log(PAM_LOG_ERROR, "failed: %d", retval); 51 52 return retval; 53} 54 55int 56cstring_to_cfstring(const char *val, CFStringRef *buffer) 57{ 58 int retval = PAM_BUF_ERR; 59 60 if (NULL == val || NULL == buffer) { 61 openpam_log(PAM_LOG_DEBUG, "NULL argument passed"); 62 retval = PAM_SERVICE_ERR; 63 goto cleanup; 64 } 65 66 *buffer = CFStringCreateWithCString(kCFAllocatorDefault, val, kCFStringEncodingUTF8); 67 if (NULL == *buffer) { 68 openpam_log(PAM_LOG_DEBUG, "CFStringCreateWithCString() failed"); 69 retval = PAM_BUF_ERR; 70 goto cleanup; 71 } 72 73 retval = PAM_SUCCESS; 74 75cleanup: 76 if (PAM_SUCCESS != retval) 77 openpam_log(PAM_LOG_ERROR, "failed: %d", retval); 78 79 return retval; 80} 81 82int 83cfstring_to_cstring(const CFStringRef val, char **buffer) 84{ 85 CFIndex maxlen = 0; 86 int retval = PAM_BUF_ERR; 87 88 if (NULL == val || NULL == buffer) { 89 openpam_log(PAM_LOG_DEBUG, "NULL argument passed"); 90 retval = PAM_SERVICE_ERR; 91 goto cleanup; 92 } 93 94 maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(val), kCFStringEncodingUTF8); 95 *buffer = calloc(maxlen + 1, sizeof(char)); 96 if (NULL == *buffer) { 97 openpam_log(PAM_LOG_DEBUG, "malloc() failed"); 98 retval = PAM_BUF_ERR; 99 goto cleanup; 100 } 101 102 if (CFStringGetCString(val, *buffer, maxlen + 1, kCFStringEncodingUTF8)) { 103 retval = PAM_SUCCESS; 104 } else { 105 openpam_log(PAM_LOG_DEBUG, "CFStringGetCString failed."); 106 free(*buffer); 107 *buffer = NULL; 108 } 109 110cleanup: 111 if (PAM_SUCCESS != retval) 112 openpam_log(PAM_LOG_ERROR, "failed: %d", retval); 113 114 return retval; 115} 116 117#ifdef OPENDIRECTORY_CACHE 118static void 119cleanup_cache(pam_handle_t *pamh, void *data, int pam_end_status) 120{ 121 CFRelease((CFTypeRef)data); 122} 123#endif /* OPENDIRECTORY_CACHE */ 124 125int 126od_record_create(pam_handle_t *pamh, ODRecordRef *record, CFStringRef cfUser) 127{ 128 int retval = PAM_SERVICE_ERR; 129 const int attr_num = 5; 130 131 ODNodeRef cfNode = NULL; 132 CFErrorRef cferror = NULL; 133 CFArrayRef attrs = NULL; 134 CFTypeRef cfVals[attr_num]; 135 136 if (NULL == record || NULL == cfUser) { 137 openpam_log(PAM_LOG_DEBUG, "NULL argument passed"); 138 retval = PAM_SERVICE_ERR; 139 goto cleanup; 140 } 141 142#ifdef OPENDIRECTORY_CACHE 143#define CFRECORDNAME_CACHE "CFRecordName" 144#define CFRECORDNAME_NAME CFSTR("name") 145#define CFRECORDNAME_RECORD CFSTR("record") 146 147 CFDictionaryRef cfdict; 148 CFStringRef cachedUser; 149 150 if (pam_get_data(pamh, CFRECORDNAME_CACHE, (void *)&cfdict) == PAM_SUCCESS && 151 (CFGetTypeID(cfdict) == CFDictionaryGetTypeID()) && 152 (cachedUser = CFDictionaryGetValue(cfdict, CFRECORDNAME_NAME)) != NULL && 153 CFGetTypeID(cachedUser) == CFStringGetTypeID() && 154 CFStringCompare(cfUser, cachedUser, 0) == kCFCompareEqualTo && 155 (*record = (ODRecordRef)CFDictionaryGetValue(cfdict, CFRECORDNAME_RECORD)) != NULL) 156 { 157 CFRetain(*record); 158 return PAM_SUCCESS; 159 } 160#endif /* OPENDIRECTORY_CACHE */ 161 162 int current_iterations = 0; 163 164 cfNode = ODNodeCreateWithNodeType(kCFAllocatorDefault, 165 kODSessionDefault, 166 eDSAuthenticationSearchNodeName, 167 &cferror); 168 if (NULL == cfNode || NULL != cferror) { 169 openpam_log(PAM_LOG_ERROR, "ODNodeCreateWithNodeType failed."); 170 retval = PAM_SERVICE_ERR; 171 goto cleanup; 172 } 173 174 cfVals[0] = kODAttributeTypeAuthenticationAuthority; 175 cfVals[1] = kODAttributeTypeHomeDirectory; 176 cfVals[2] = kODAttributeTypeNFSHomeDirectory; 177 cfVals[3] = kODAttributeTypeUserShell; 178 cfVals[4] = kODAttributeTypeUniqueID; 179 attrs = CFArrayCreate(kCFAllocatorDefault, cfVals, (CFIndex)attr_num, &kCFTypeArrayCallBacks); 180 if (NULL == attrs) { 181 openpam_log(PAM_LOG_DEBUG, "CFArrayCreate() failed"); 182 retval = PAM_BUF_ERR; 183 goto cleanup; 184 } 185 186 retval = PAM_SERVICE_ERR; 187 while (current_iterations <= kMaxIterationCount) { 188 CFIndex unreachable_count = 0; 189 CFArrayRef unreachable_nodes = ODNodeCopyUnreachableSubnodeNames(cfNode, NULL); 190 if (unreachable_nodes) { 191 unreachable_count = CFArrayGetCount(unreachable_nodes); 192 CFRelease(unreachable_nodes); 193 openpam_log(PAM_LOG_DEBUG, "%lu OD nodes unreachable.", unreachable_count); 194 } 195 196 *record = ODNodeCopyRecord(cfNode, kODRecordTypeUsers, cfUser, attrs, &cferror); 197 if (*record) 198 break; 199 if (0 == unreachable_count) 200 break; 201 202 openpam_log(PAM_LOG_DEBUG, "Waiting %d seconds for nodes to become reachable", kWaitSeconds); 203 sleep(kWaitSeconds); 204 ++current_iterations; 205 } 206 207 if (*record) { 208#ifdef OPENDIRECTORY_CACHE 209 const void *keys[] = { CFRECORDNAME_NAME, CFRECORDNAME_RECORD }; 210 const void *values[] = { cfUser, *record }; 211 CFDictionaryRef dict; 212 213 dict = CFDictionaryCreate(NULL, keys, values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 214 if (dict) 215 pam_set_data(pamh, CFRECORDNAME_CACHE, (void *)dict, cleanup_cache); 216#endif /* OPENDIRECTORY_CACHE */ 217 retval = PAM_SUCCESS; 218 } else { 219 retval = PAM_USER_UNKNOWN; 220 } 221 222 if (current_iterations > 0) { 223 char *wt = NULL, *found = NULL; 224 int retval2; 225 226 if (*record) 227 found = "failure"; 228 else 229 found = "success"; 230 231 retval2 = asprintf(&wt, "%d", kWaitSeconds * current_iterations); 232 if (-1 == retval2) { 233 openpam_log(PAM_LOG_DEBUG, "Failed to convert current wait time to string."); 234 retval = PAM_BUF_ERR; 235 goto cleanup; 236 } 237 238 239 aslmsg m = asl_new(ASL_TYPE_MSG); 240 asl_set(m, "com.apple.message.domain", "com.apple.pam_modules.odAvailableWaitTime" ); 241 asl_set(m, "com.apple.message.signature", "wait_time"); 242 asl_set(m, "com.apple.message.value", wt); 243 asl_set(m, "com.apple.message.result", found); 244 asl_log(NULL, m, ASL_LEVEL_NOTICE, "OD nodes online delay: %ss. User record lookup: %s.", wt, found); 245 asl_free(m); 246 free(wt); 247 } 248 249cleanup: 250 if (NULL != attrs) { 251 CFRelease(attrs); 252 } 253 254 if (NULL != cferror) { 255 CFRelease(cferror); 256 } 257 258 if (NULL != cfNode) { 259 CFRelease(cfNode); 260 } 261 262 if (PAM_SUCCESS != retval) { 263 openpam_log(PAM_LOG_ERROR, "failed: %d", retval); 264 if (NULL != *record) { 265 CFRelease(*record); 266 *record = NULL; 267 } 268 } 269 270 return retval; 271} 272 273int 274od_record_create_cstring(pam_handle_t *pamh, ODRecordRef *record, const char *user) 275{ 276 int retval = PAM_SUCCESS; 277 CFStringRef cfUser = NULL; 278 279 if (NULL == record || NULL == user) { 280 openpam_log(PAM_LOG_DEBUG, "NULL argument passed"); 281 retval = PAM_SERVICE_ERR; 282 goto cleanup; 283 } 284 285 if (PAM_SUCCESS != (retval = cstring_to_cfstring(user, &cfUser)) || 286 PAM_SUCCESS != (retval = od_record_create(pamh, record, cfUser))) { 287 openpam_log(PAM_LOG_DEBUG, "od_record_create() failed"); 288 goto cleanup; 289 } 290 291cleanup: 292 if (PAM_SUCCESS != retval) { 293 openpam_log(PAM_LOG_ERROR, "failed: %d", retval); 294 if (NULL != *record) { 295 CFRelease(*record); 296 } 297 } 298 299 if (NULL != cfUser) { 300 CFRelease(cfUser); 301 } 302 303 return retval; 304} 305 306/* Can return NULL */ 307int 308od_record_attribute_create_cfarray(ODRecordRef record, CFStringRef attrib, CFArrayRef *out) 309{ 310 int retval = PAM_SUCCESS; 311 312 if (NULL == record || NULL == attrib || NULL == out) { 313 openpam_log(PAM_LOG_DEBUG, "NULL argument passed"); 314 retval = PAM_SERVICE_ERR; 315 goto cleanup; 316 } 317 318 *out = ODRecordCopyValues(record, attrib, NULL); 319 320cleanup: 321 if (PAM_SUCCESS != retval) { 322 if (NULL != out) { 323 CFRelease(out); 324 } 325 } 326 return retval; 327} 328 329/* Can return NULL */ 330int 331od_record_attribute_create_cfstring(ODRecordRef record, CFStringRef attrib, CFStringRef *out) 332{ 333 int retval = PAM_SERVICE_ERR; 334 CFTypeRef cval = NULL; 335 CFArrayRef vals = NULL; 336 CFIndex i = 0, count = 0; 337 338 if (NULL == record || NULL == attrib || NULL == out) { 339 openpam_log(PAM_LOG_DEBUG, "NULL argument passed"); 340 retval = PAM_SERVICE_ERR; 341 goto cleanup; 342 } 343 344 *out = NULL; 345 retval = od_record_attribute_create_cfarray(record, attrib, &vals); 346 if (PAM_SUCCESS != retval) { 347 openpam_log(PAM_LOG_DEBUG, "od_record_attribute_create_cfarray() failed"); 348 goto cleanup; 349 } 350 if (NULL == vals) { 351 retval = PAM_SUCCESS; 352 goto cleanup; 353 } 354 355 count = CFArrayGetCount(vals); 356 if (1 != count) { 357 char *attr_cstr = NULL; 358 cfstring_to_cstring(attrib, &attr_cstr); 359 openpam_log(PAM_LOG_DEBUG, "returned %lx attributes for %s", count, attr_cstr); 360 free(attr_cstr); 361 } 362 363 for (i = 0; i < count; ++i) { 364 cval = CFArrayGetValueAtIndex(vals, i); 365 if (NULL == cval) { 366 continue; 367 } 368 if (CFGetTypeID(cval) == CFStringGetTypeID()) { 369 *out = CFStringCreateCopy(kCFAllocatorDefault, cval); 370 if (NULL == *out) { 371 openpam_log(PAM_LOG_DEBUG, "CFStringCreateCopy() failed"); 372 retval = PAM_BUF_ERR; 373 goto cleanup; 374 } 375 break; 376 } else { 377 openpam_log(PAM_LOG_DEBUG, "attribute is not a cfstring"); 378 retval = PAM_PERM_DENIED; 379 goto cleanup; 380 } 381 } 382 retval = PAM_SUCCESS; 383 384cleanup: 385 if (PAM_SUCCESS != retval) { 386 if (NULL != out) { 387 CFRelease(out); 388 } 389 } 390 if (NULL != vals) { 391 CFRelease(vals); 392 } 393 394 return retval; 395} 396 397/* Can return NULL */ 398int 399od_record_attribute_create_cstring(ODRecordRef record, CFStringRef attrib, char **out) 400{ 401 int retval = PAM_SERVICE_ERR; 402 CFStringRef val = NULL; 403 404 if (NULL == record || NULL == attrib || NULL == out) { 405 openpam_log(PAM_LOG_DEBUG, "NULL argument passed"); 406 retval = PAM_SERVICE_ERR; 407 goto cleanup; 408 } 409 410 retval = od_record_attribute_create_cfstring(record, attrib, &val); 411 if (PAM_SUCCESS != retval) { 412 openpam_log(PAM_LOG_DEBUG, "od_record_attribute_create_cfstring() failed"); 413 goto cleanup; 414 } 415 416 if (NULL != val) { 417 retval = cfstring_to_cstring(val, out); 418 if (PAM_SUCCESS != retval) { 419 openpam_log(PAM_LOG_DEBUG, "cfstring_to_cstring() failed"); 420 goto cleanup; 421 } 422 } 423 424cleanup: 425 if (PAM_SUCCESS != retval) { 426 free(out); 427 } 428 429 if (NULL != val) { 430 CFRelease(val); 431 } 432 433 return retval; 434} 435 436int 437od_record_check_pwpolicy(ODRecordRef record) 438{ 439 CFDictionaryRef policy = NULL; 440 const void *isDisabled; 441 const void *newPasswordRequired; 442 int retval = PAM_SERVICE_ERR; 443 444 if (NULL == record) { 445 openpam_log(PAM_LOG_DEBUG, "NULL argument passed"); 446 retval = PAM_SERVICE_ERR; 447 goto cleanup; 448 } 449 450 if (NULL == (policy = ODRecordCopyPasswordPolicy(kCFAllocatorDefault, record, NULL)) || 451 NULL == (isDisabled = CFDictionaryGetValue(policy, CFSTR("isDisabled"))) || 452 !cfboolean_get_value(isDisabled)) 453 retval = PAM_SUCCESS; 454 else 455 retval = PAM_PERM_DENIED; 456 if (NULL != policy && 457 NULL != (newPasswordRequired = CFDictionaryGetValue(policy, CFSTR("newPasswordRequired"))) && 458 cfboolean_get_value(newPasswordRequired)) 459 retval = PAM_NEW_AUTHTOK_REQD; 460 461 if (NULL != policy) { 462 CFRelease(policy); 463 } 464 465cleanup: 466 openpam_log(PAM_LOG_DEBUG, "retval: %d", retval); 467 return retval; 468} 469 470int 471od_record_check_authauthority(ODRecordRef record) 472{ 473 int retval = PAM_PERM_DENIED; 474 CFStringRef authauth = NULL; 475 476 if (NULL == record) { 477 openpam_log(PAM_LOG_DEBUG, "NULL argument passed"); 478 retval = PAM_SERVICE_ERR; 479 goto cleanup; 480 } 481 482 retval = od_record_attribute_create_cfstring(record, kODAttributeTypeAuthenticationAuthority, &authauth); 483 if (PAM_SUCCESS != retval) { 484 openpam_log(PAM_LOG_DEBUG, "od_record_attribute_create_cfstring() failed"); 485 goto cleanup; 486 } 487 if (NULL == authauth) { 488 retval = PAM_SUCCESS; 489 goto cleanup; 490 } 491 if (!CFStringHasPrefix(authauth, CFSTR(kDSValueAuthAuthorityDisabledUser))) { 492 retval = PAM_SUCCESS; 493 } 494 495cleanup: 496 if (PAM_SUCCESS != retval) { 497 openpam_log(PAM_LOG_ERROR, "failed: %d", retval); 498 } 499 500 if (authauth) { 501 CFRelease(authauth); 502 } 503 504 return retval; 505} 506 507int 508od_record_check_homedir(ODRecordRef record) 509{ 510 int retval = PAM_SERVICE_ERR; 511 CFStringRef tmp = NULL; 512 513 if (NULL == record) { 514 openpam_log(PAM_LOG_DEBUG, "NULL argument passed"); 515 retval = PAM_SERVICE_ERR; 516 goto cleanup; 517 } 518 519 retval = od_record_attribute_create_cfstring(record, kODAttributeTypeNFSHomeDirectory, &tmp); 520 if (PAM_SUCCESS != retval) { 521 openpam_log(PAM_LOG_DEBUG, "od_record_attribute_create_cfstring() failed"); 522 goto cleanup; 523 } 524 525 /* Allow NULL home directories */ 526 if (NULL == tmp) { 527 retval = PAM_SUCCESS; 528 goto cleanup; 529 } 530 531 /* Do not allow login with '/dev/null' home */ 532 if (kCFCompareEqualTo == CFStringCompare(tmp, CFSTR("/dev/null"), 0)) { 533 openpam_log(PAM_LOG_DEBUG, "home directory is /dev/null"); 534 retval = PAM_PERM_DENIED; 535 goto cleanup; 536 } 537 538 if (kCFCompareEqualTo == CFStringCompare(tmp, CFSTR("99"), 0)) { 539 openpam_log(PAM_LOG_DEBUG, "home directory is 99"); 540 retval = PAM_PERM_DENIED; 541 goto cleanup; 542 } 543 544 retval = PAM_SUCCESS; 545 546cleanup: 547 if (PAM_SUCCESS != retval) 548 openpam_log(PAM_LOG_ERROR, "failed: %d", retval); 549 550 if (NULL != tmp) { 551 CFRelease(tmp); 552 } 553 554 return retval; 555} 556 557int 558od_record_check_shell(ODRecordRef record) 559{ 560 int retval = PAM_PERM_DENIED; 561 CFStringRef cfstr = NULL; 562 563 if (NULL == record) { 564 openpam_log(PAM_LOG_DEBUG, "NULL argument passed"); 565 retval = PAM_SERVICE_ERR; 566 goto cleanup; 567 } 568 569 retval = od_record_attribute_create_cfstring(record, kODAttributeTypeUserShell, &cfstr); 570 if (PAM_SUCCESS != retval) { 571 openpam_log(PAM_LOG_DEBUG, "od_record_attribute_create_cfstring() failed"); 572 goto cleanup; 573 } 574 575 if (NULL == cfstr) { 576 retval = PAM_SUCCESS; 577 goto cleanup; 578 } 579 580 if (CFStringCompare(cfstr, CFSTR("/usr/bin/false"), 0) == kCFCompareEqualTo) { 581 openpam_log(PAM_LOG_DEBUG, "user shell is /bin/false"); 582 retval = PAM_PERM_DENIED; 583 } 584 585cleanup: 586 if (PAM_SUCCESS != retval) 587 openpam_log(PAM_LOG_ERROR, "failed: %d", retval); 588 589 if (NULL != cfstr) { 590 CFRelease(cfstr); 591 } 592 593 return retval; 594} 595 596int 597od_string_from_record(ODRecordRef record, CFStringRef attrib, char **out) 598{ 599 int retval = PAM_SERVICE_ERR; 600 CFStringRef val = NULL; 601 602 if (NULL == record) { 603 openpam_log(PAM_LOG_DEBUG, "%s - NULL ODRecord passed.", __func__); 604 goto cleanup; 605 } 606 607 retval = od_record_attribute_create_cfstring(record, attrib, &val); 608 if (PAM_SUCCESS != retval) { 609 goto cleanup; 610 } 611 612 if (val) 613 retval = cfstring_to_cstring(val, out); 614 615cleanup: 616 if (val) 617 CFRelease(val); 618 619 return retval; 620} 621 622int 623extract_homemount(char *in, char **out_url, char **out_path) 624{ 625 // Directory Services people have assured me that this won't change 626 static const char URL_OPEN[] = "<url>"; 627 static const char URL_CLOSE[] = "</url>"; 628 static const char PATH_OPEN[] = "<path>"; 629 static const char PATH_CLOSE[] = "</path>"; 630 631 char *server_URL = NULL; 632 char *path = NULL; 633 char *record_start = NULL; 634 char *record_end = NULL; 635 636 int retval = PAM_SERVICE_ERR; 637 638 if (NULL == in) 639 goto fin; 640 641 record_start = in; 642 server_URL = strstr(record_start, URL_OPEN); 643 if (NULL == server_URL) 644 goto fin; 645 server_URL += sizeof(URL_OPEN)-1; 646 while ('\0' != *server_URL && isspace(*server_URL)) 647 server_URL++; 648 record_end = strstr(server_URL, URL_CLOSE); 649 if (NULL == record_end) 650 goto fin; 651 while (record_end >= server_URL && '\0' != *record_end && isspace(*(record_end-1))) 652 record_end--; 653 if (NULL == record_end) 654 goto fin; 655 *record_end = '\0'; 656 if (NULL == (*out_url = strdup(server_URL))) 657 goto fin; 658 659 record_start = record_end+1; 660 path = strstr(record_start, PATH_OPEN); 661 if (NULL == path) 662 goto ok; 663 path += sizeof(PATH_OPEN)-1; 664 while ('\0' != *path && isspace(*path)) 665 path++; 666 record_end = strstr(path, PATH_CLOSE); 667 if (NULL == record_end) 668 goto fin; 669 while (record_end >= path && '\0' != *record_end && isspace(*(record_end-1))) 670 record_end--; 671 if (NULL == record_end) 672 goto fin; 673 *record_end = '\0'; 674 if (NULL == (*out_path = strdup(path))) 675 goto fin; 676 677ok: 678 retval = PAM_SUCCESS; 679fin: 680 return retval; 681} 682 683int 684od_extract_home(pam_handle_t *pamh, const char *username, char **server_URL, char **path, char **homedir) 685{ 686 int retval = PAM_SERVICE_ERR; 687 char *tmp = NULL; 688 ODRecordRef record = NULL; 689 690 retval = od_record_create_cstring(pamh, &record, username); 691 if (PAM_SUCCESS != retval) { 692 goto cleanup; 693 } 694 695 retval = od_string_from_record(record, kODAttributeTypeHomeDirectory, &tmp); 696 if (retval) { 697 openpam_log(PAM_LOG_DEBUG, "%s - get kODAttributeTypeHomeDirectory : %d", 698 __func__, retval); 699 goto cleanup; 700 } 701 extract_homemount(tmp, server_URL, path); 702 openpam_log(PAM_LOG_DEBUG, "%s - Server URL : %s", __func__, *server_URL); 703 openpam_log(PAM_LOG_DEBUG, "%s - Path to mount: %s", __func__, *path); 704 705 retval = od_string_from_record(record, kODAttributeTypeNFSHomeDirectory, homedir); 706 openpam_log(PAM_LOG_DEBUG, "%s - Home dir : %s", __func__, *homedir); 707 if (retval) 708 goto cleanup; 709 710 retval = PAM_SUCCESS; 711 712cleanup: 713 if (tmp) 714 free(tmp); 715 if (record) 716 CFRelease(record); 717 718 return retval; 719} 720 721/* extract the principal from OpenDirectory */ 722int 723od_principal_for_user(pam_handle_t *pamh, const char *user, char **od_principal) 724{ 725 int retval = PAM_SERVICE_ERR; 726 ODRecordRef record = NULL; 727 CFStringRef principal = NULL; 728 CFArrayRef authparts = NULL, vals = NULL; 729 CFIndex i = 0, count = 0; 730 731 if (NULL == user || NULL == od_principal) { 732 openpam_log(PAM_LOG_DEBUG, "NULL argument passed"); 733 retval = PAM_SERVICE_ERR; 734 goto cleanup; 735 } 736 737 retval = od_record_create_cstring(pamh, &record, user); 738 if (PAM_SUCCESS != retval) { 739 openpam_log(PAM_LOG_DEBUG, "od_record_attribute_create_cfstring() failed"); 740 goto cleanup; 741 } 742 743 retval = od_record_attribute_create_cfarray(record, kODAttributeTypeAuthenticationAuthority, &vals); 744 if (PAM_SUCCESS != retval) { 745 openpam_log(PAM_LOG_DEBUG, "od_record_attribute_create_cfarray() failed"); 746 goto cleanup; 747 } 748 if (NULL == vals) { 749 openpam_log(PAM_LOG_DEBUG, "no authauth availale for user."); 750 retval = PAM_PERM_DENIED; 751 goto cleanup; 752 } 753 754 count = CFArrayGetCount(vals); 755 for (i = 0; i < count; i++) 756 { 757 const void *val = CFArrayGetValueAtIndex(vals, i); 758 if (NULL == val || CFGetTypeID(val) != CFStringGetTypeID()) 759 break; 760 761 authparts = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, val, CFSTR(";")); 762 if (NULL == authparts) 763 continue; 764 765 if ((CFArrayGetCount(authparts) < 5) || 766 (CFStringCompare(CFArrayGetValueAtIndex(authparts, 1), CFSTR("Kerberosv5"), kCFCompareEqualTo)) || 767 (CFStringHasPrefix(CFArrayGetValueAtIndex(authparts, 4), CFSTR("LKDC:")))) { 768 if (NULL != authparts) { 769 CFRelease(authparts); 770 authparts = NULL; 771 } 772 continue; 773 } else { 774 break; 775 } 776 } 777 778 if (NULL == authparts) { 779 openpam_log(PAM_LOG_DEBUG, "No authentication authority returned"); 780 retval = PAM_PERM_DENIED; 781 goto cleanup; 782 } 783 784 principal = CFArrayGetValueAtIndex(authparts, 3); 785 if (NULL == principal) { 786 openpam_log(PAM_LOG_DEBUG, "no principal found in authentication authority"); 787 retval = PAM_PERM_DENIED; 788 goto cleanup; 789 } 790 791 retval = cfstring_to_cstring(principal, od_principal); 792 if (PAM_SUCCESS != retval) { 793 openpam_log(PAM_LOG_DEBUG, "cfstring_to_cstring() failed"); 794 goto cleanup; 795 } 796 797 798cleanup: 799 if (PAM_SUCCESS != retval) { 800 openpam_log(PAM_LOG_DEBUG, "failed: %d", retval); 801 } 802 803 if (NULL != record) { 804 CFRelease(record); 805 } 806 807 if (NULL != authparts) { 808 CFRelease(authparts); 809 } 810 811 if (NULL != vals) { 812 CFRelease(vals); 813 } 814 815 return retval; 816} 817 818void 819pam_cf_cleanup(__unused pam_handle_t *pamh, void *data, __unused int pam_end_status) 820{ 821 if (data) { 822 CFStringRef *cfstring = data; 823 CFRelease(*cfstring); 824 } 825} 826