1/* tls_st.c - Handle tls/ssl using SecureTransport */ 2 3#include "portable.h" 4 5#ifdef HAVE_SECURE_TRANSPORT 6 7#include "ldap_config.h" 8 9#include <stdio.h> 10 11#include <ac/stdlib.h> 12#include <ac/errno.h> 13#include <ac/socket.h> 14#include <ac/string.h> 15#include <ac/ctype.h> 16#include <ac/time.h> 17#include <ac/unistd.h> 18#include <ac/param.h> 19#include <ac/dirent.h> 20 21#include "ldap-int.h" 22#include "ldap-tls.h" 23 24#include <Security/Security.h> 25#include <CoreDaemon/CoreDaemon.h> 26#include <syslog.h> 27#include <sys/stat.h> 28 29#define SYSTEM_KEYCHAIN_PATH "/Library/Keychains/System.keychain" 30 31#define CFRelease_and_null(obj) do { CFRelease(obj); (obj) = NULL; } while (0) 32 33typedef struct tlsst_ctx { 34 int refcount; 35 SSLProtocol protocol_min; 36 int require_cert; 37 int crl_check; 38 SSLCipherSuite *ciphers; 39 size_t ciphers_count; 40 CFArrayRef identity_certs; 41 CFArrayRef trusted_certs; 42 CFDataRef dhparams; 43#ifdef LDAP_R_COMPILE 44 ldap_pvt_thread_mutex_t refmutex; 45#endif 46} tlsst_ctx; 47 48typedef struct tlsst_session { 49 SSLContextRef ssl; 50 tlsst_ctx *ctx; 51 char *last_error; 52 53 CFMutableDataRef subject_data; 54 int subject_result; 55 CFMutableDataRef issuer_data; 56 int issuer_result; 57 58 Sockbuf_IO_Desc *sbiod; 59 60 unsigned char sslv2_detect_bytes[3]; 61 62 unsigned int is_server : 1; 63 unsigned int subject_cached : 1; 64 unsigned int issuer_cached : 1; 65 unsigned int want_read : 1; 66 unsigned int want_write : 1; 67 unsigned int cert_received : 1; 68 unsigned int cert_trusted : 1; 69 unsigned int sslv2_detect_length : 2; 70} tlsst_session; 71 72static int tlsst_session_strength(tls_session *_sess); 73 74static struct { 75 int ldap_protocol; 76 SSLProtocol st_protocol; 77} tlsst_protocol_map[] = { 78 LDAP_OPT_X_TLS_PROTOCOL_SSL2, kSSLProtocol2, 79 LDAP_OPT_X_TLS_PROTOCOL_SSL3, kSSLProtocol3, 80 LDAP_OPT_X_TLS_PROTOCOL_TLS1_0, kTLSProtocol1, 81 LDAP_OPT_X_TLS_PROTOCOL_TLS1_1, kTLSProtocol11, 82 LDAP_OPT_X_TLS_PROTOCOL_TLS1_2, kTLSProtocol12 83}; 84 85static SSLProtocol 86tlsst_protocol_map_ldap2st(int ldap_protocol, const char *setting_name) 87{ 88 for (int i = 0; i < sizeof tlsst_protocol_map / sizeof tlsst_protocol_map[0]; i++) 89 if (ldap_protocol == tlsst_protocol_map[i].ldap_protocol) 90 return tlsst_protocol_map[i].st_protocol; 91 92 syslog(LOG_ERR, "TLS: unknown %s setting %d", setting_name, ldap_protocol); 93 return kSSLProtocolUnknown; 94} 95 96static struct { 97 int ldap_authenticate; 98 SSLAuthenticate st_authenticate; 99} tlsst_auth_map[] = { 100 LDAP_OPT_X_TLS_NEVER, kNeverAuthenticate, 101 LDAP_OPT_X_TLS_HARD, kAlwaysAuthenticate, 102 LDAP_OPT_X_TLS_DEMAND, kAlwaysAuthenticate, 103 LDAP_OPT_X_TLS_ALLOW, kTryAuthenticate, 104 LDAP_OPT_X_TLS_TRY, kTryAuthenticate 105}; 106 107static SSLAuthenticate 108tlsst_auth_map_ldap2st(int ldap_authenticate) 109{ 110 for (int i = 0; i < sizeof tlsst_auth_map / sizeof tlsst_auth_map[0]; i++) 111 if (ldap_authenticate == tlsst_auth_map[i].ldap_authenticate) 112 return tlsst_auth_map[i].st_authenticate; 113 114 return kAlwaysAuthenticate; 115} 116 117static const char * 118tlsst_protocol_name(SSLProtocol protocol) 119{ 120 switch (protocol) { 121 case kSSLProtocol2: 122 return "SSLv2"; 123 case kSSLProtocol3: 124 case kSSLProtocol3Only: /* shouldn't happen but present only to make the no-default work (see below) */ 125 return "SSLv3"; 126 case kTLSProtocol1: 127 case kTLSProtocol1Only: /* shouldn't happen but present only to make the no-default work (see below) */ 128 return "TLSv1"; 129 case kTLSProtocol11: 130 return "TLSv1.1"; 131 case kTLSProtocol12: 132 return "TLSv1.2"; 133 case kDTLSProtocol1: /* shouldn't happen but present only to make the no-default work (see below) */ 134 return "DTLSv1"; 135 case kSSLProtocolUnknown: 136 case kSSLProtocolAll: /* shouldn't happen but present only to make the no-default work (see below) */ 137 return "unknown"; 138 /* no default for this switch so the compiler will let us know when a new value is added to this enum so we can add a string for it */ 139 } 140} 141 142static void 143tlsst_error_string(CFStringRef str, long num, char *buffer, size_t bufsize) 144{ 145 if (CFStringGetCString(str, buffer, bufsize, kCFStringEncodingUTF8)) { 146 /* add the num to the error string unless it's already in there */ 147 CFStringRef numeric = CFStringCreateWithFormat(NULL, NULL, CFSTR("%ld"), num); 148 CFRange where = CFStringFind(str, numeric, 0); 149 CFRelease_and_null(numeric); 150 151 if (where.location == kCFNotFound) { 152 char spnp[32]; 153 snprintf(spnp, sizeof spnp, " (%ld)", num); 154 strlcat(buffer, spnp, bufsize); 155 } 156 } else 157 snprintf(buffer, bufsize, "error %ld", num); 158} 159 160static char * 161tlsst_oss2buf(OSStatus oss, char *buffer, size_t bufsize) 162{ 163 if (oss >= errSecErrnoBase && oss <= errSecErrnoLimit) 164 strlcpy(buffer, strerror(oss - errSecErrnoBase), bufsize); 165 else { 166 CFStringRef str = SecCopyErrorMessageString(oss, NULL); 167 if (str != NULL) { 168 tlsst_error_string(str, oss, buffer, bufsize); 169 CFRelease_and_null(str); 170 } else 171 snprintf(buffer, bufsize, "error %ld", (long) oss); 172 } 173 174 return buffer; 175} 176 177static char * 178tlsst_err2buf(CFErrorRef error, char *buffer, size_t bufsize) 179{ 180 CFIndex code = error ? CFErrorGetCode(error) : 0; 181 CFStringRef str = error ? CFErrorCopyDescription(error) : NULL; 182 if (str != NULL) { 183 tlsst_error_string(str, code, buffer, bufsize); 184 CFRelease_and_null(str); 185 } else 186 snprintf(buffer, sizeof buffer, "error %ld", (long) code); 187 188 return buffer; 189} 190 191static void 192tlsst_report_oss(OSStatus oss, const char *format, ...) 193{ 194 char msgbuf[512]; 195 va_list args; 196 va_start(args, format); 197 vsnprintf(msgbuf, sizeof msgbuf, format, args); 198 va_end(args); 199 200 char errbuf[512]; 201 tlsst_oss2buf(oss, errbuf, sizeof errbuf); 202 203 syslog(LOG_ERR, "%s: %s", msgbuf, errbuf); 204} 205 206static void 207tlsst_report_error(CFErrorRef error, const char *format, ...) 208{ 209 char msgbuf[512]; 210 va_list args; 211 va_start(args, format); 212 vsnprintf(msgbuf, sizeof msgbuf, format, args); 213 va_end(args); 214 215 char errbuf[512]; 216 tlsst_err2buf(error, errbuf, sizeof errbuf); 217 218 syslog(LOG_ERR, "%s: %s", msgbuf, errbuf); 219} 220 221static const char *tlsst_tr2str(SecTrustResultType trustResult) 222{ 223 switch (trustResult) { 224 case kSecTrustResultInvalid: 225 return "kSecTrustResultInvalid"; 226 case kSecTrustResultProceed: 227 return "kSecTrustResultProceed"; 228/* deprecated 229 case kSecTrustResultConfirm: 230 return "kSecTrustResultConfirm"; */ 231 case kSecTrustResultDeny: 232 return "kSecTrustResultDeny"; 233 case kSecTrustResultUnspecified: 234 return "kSecTrustResultUnspecified"; 235 case kSecTrustResultRecoverableTrustFailure: 236 return "kSecTrustResultRecoverableTrustFailure"; 237 case kSecTrustResultFatalTrustFailure: 238 return "kSecTrustResultFatalTrustFailure"; 239 case kSecTrustResultOtherError: 240 return "kSecTrustResultOtherError"; 241 default: 242 return "unknown SecTrustResult"; 243 } 244} 245 246static SSLCipherSuite * 247tlsst_ciphers_get(const char *cipherspec, size_t *ciphers_count_r, const char *setting_name) 248{ 249 Debug(LDAP_DEBUG_TRACE, "tlsst_ciphers_get(%s, %s)\n", cipherspec ?: "(null)", setting_name, 0); 250 251 if (cipherspec == NULL || *cipherspec == 0) 252 cipherspec = "DEFAULT"; 253 254 *ciphers_count_r = 0; 255 SSLCipherSuite *ciphers = XSCipherSpecParse(cipherspec, ciphers_count_r); 256 if (*ciphers_count_r == 0) 257 syslog(LOG_ERR, "TLS: could not parse cipher spec %s (check %s setting)\n", cipherspec, setting_name); 258 259 return ciphers; 260} 261 262static void 263tlsst_ciphers_list(const SSLCipherSuite *ciphers, size_t count) 264{ 265 // log lines are truncated at 1024 characters so log ciphers one per line rather than all on one line 266 for (size_t i = 0; i < count; i++) { 267 const char *name = XSCipherToName(ciphers[i]); 268 if (name == NULL) 269 name = "unknown"; 270 Debug(LDAP_DEBUG_ARGS, "TLS: [%lu] %04x %s\n", i, ciphers[i], name); 271 } 272} 273 274static int 275tlsst_ciphers_set(SSLContextRef ssl, const SSLCipherSuite *want_set, size_t num_want, const char *setting_name) 276{ 277 Debug(LDAP_DEBUG_TRACE, "tlsst_ciphers_set(%lu, %s)\n", num_want, setting_name, 0); 278 279 /* these are the ciphers we want */ 280 Debug(LDAP_DEBUG_ARGS, "TLS: %lu cipher%s wanted:\n", num_want, num_want == 1 ? "" : "s", 0); 281 tlsst_ciphers_list(want_set, num_want); 282 283 /* these are the available ciphers */ 284 size_t max_avail = 0; 285 OSStatus ret = SSLGetNumberSupportedCiphers(ssl, &max_avail); 286 if (ret) { 287 tlsst_report_oss(ret, "TLS: SSLGetNumberSupportedCiphers() failed"); 288 return num_want == 0 ? 0 : -1; 289 } 290 291 SSLCipherSuite *avail_set = LDAP_CALLOC(max_avail, sizeof *avail_set); 292 size_t num_avail = max_avail; 293 ret = SSLGetSupportedCiphers(ssl, avail_set, &num_avail); 294 if (ret) { 295 tlsst_report_oss(ret, "TLS: SSLGetSupportedCiphers() failed"); 296 LDAP_FREE(avail_set); 297 return num_want == 0 ? 0 : -1; 298 } 299 Debug(LDAP_DEBUG_ARGS, "TLS: %lu cipher%s supported:\n", num_avail, num_avail == 1 ? "" : "s", 0); 300 tlsst_ciphers_list(avail_set, num_avail); 301 if (num_want == 0) { 302 LDAP_FREE(avail_set); 303 return 0; 304 } 305 306 /* Compute intersection of the two sets preserving the order of 307 want_set. Perhaps this O(n^2) operation should be computed 308 using binary searches through avail_set, or done once and the 309 result reused (but beware hidden constraints of 310 SecureTransport's default enabled ciphers list). */ 311 size_t max_intersect = MIN(num_want, num_avail); 312 SSLCipherSuite *intersect_set = LDAP_CALLOC(max_intersect, sizeof *intersect_set); 313 size_t num_intersect = 0; 314 for (size_t w = 0; w < num_want; w++) { 315 for (size_t a = 0; a < num_avail; a++) { 316 if (want_set[w] == avail_set[a]) { 317 intersect_set[num_intersect++] = want_set[w]; 318 break; 319 } 320 } 321 } 322 LDAP_FREE(avail_set); 323 avail_set = NULL; 324 325 if (num_intersect == 0) { 326 syslog(LOG_ERR, "TLS: None of the desired SSL ciphers are supported (check %s setting)", setting_name); 327 LDAP_FREE(intersect_set); 328 return -1; 329 } 330 331 ret = SSLSetEnabledCiphers(ssl, intersect_set, num_intersect); 332 if (ret) { 333 tlsst_report_oss(ret, "TLS: SSLSetEnabledCiphers(%lu) failed (check %s setting)", num_intersect, setting_name); 334 LDAP_FREE(intersect_set); 335 return -1; 336 } 337 LDAP_FREE(intersect_set); 338 339 if (ldap_debug == 0) 340 return 0; 341 342 size_t max_enabled = 0; 343 ret = SSLGetNumberEnabledCiphers(ssl, &max_enabled); 344 if (ret) { 345 tlsst_report_oss(ret, "TLS: SSLGetNumberEnabledCiphers() failed"); 346 return 0; /* non-fatal */ 347 } 348 SSLCipherSuite *enabled_set = LDAP_CALLOC(max_enabled, sizeof *enabled_set); 349 size_t num_enabled = max_enabled; 350 ret = SSLGetEnabledCiphers(ssl, enabled_set, &num_enabled); 351 if (ret) { 352 tlsst_report_oss(ret, "TLS: SSLGetEnabledCiphers() failed"); 353 LDAP_FREE(enabled_set); 354 return 0; /* non-fatal */ 355 } 356 357 Debug(LDAP_DEBUG_ARGS, "TLS: %lu cipher%s enabled\n", num_enabled, num_enabled == 1 ? "" : "s", 0); 358 tlsst_ciphers_list(enabled_set, num_enabled); 359 LDAP_FREE(enabled_set); 360 361 return 0; 362} 363 364static CFTypeRef 365tlsst_item_get(const char *name, const char *setting_name, CFTypeRef class) 366{ 367 Debug(LDAP_DEBUG_TRACE, "tlsst_item_get(%s, %s)\n", name, setting_name, 0); 368 369 CFTypeRef result = NULL; 370 371 SecKeychainRef keychain = NULL; 372 OSStatus ret = SecKeychainOpen(SYSTEM_KEYCHAIN_PATH, &keychain); 373 if (ret == 0) { 374 CFArrayRef searchList = CFArrayCreate(NULL, (const void **) &keychain, 1, &kCFTypeArrayCallBacks); 375 if (searchList != NULL) { 376 CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 8, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 377 if (query != NULL) { 378 CFDictionaryAddValue(query, kSecMatchSearchList, searchList); 379 CFDictionaryAddValue(query, kSecClass, class); 380 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne); 381 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue); 382 CFStringRef label = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8); 383 if (label != NULL) { 384 CFDictionaryAddValue(query, kSecMatchSubjectWholeString, label); 385 386 ret = SecItemCopyMatching(query, &result); 387 if (ret) { 388 tlsst_report_oss(ret, "TLS: SecItemCopyMatching(%s) failed (check %s setting)", name, setting_name); 389 if (result != NULL) 390 CFRelease_and_null(result); 391 } else if (result == NULL) 392 syslog(LOG_ERR, "TLS: SecItemCopyMatching(%s) returned no matches (check %s setting)", name, setting_name); 393 394 CFRelease_and_null(label); 395 } else 396 syslog(LOG_ERR, "TLS: CFStringCreateWithCString(%s) failed (check %s setting)", name, setting_name); 397 398 CFRelease_and_null(query); 399 } else 400 syslog(LOG_ERR, "TLS: CFDictionaryCreateMutable() failed"); 401 402 CFRelease_and_null(searchList); 403 } else 404 syslog(LOG_ERR, "TLS: CFArrayCreate() failed"); 405 406 CFRelease_and_null(keychain); 407 } else 408 tlsst_report_oss(ret, "TLS: SecKeychainOpen(%s) failed", SYSTEM_KEYCHAIN_PATH); 409 410 return result; /* caller must release */ 411} 412 413static Boolean 414tlsst_identity_validate(SecIdentityRef identity, const char *setting_name) 415{ 416 Debug(LDAP_DEBUG_TRACE, "tlsst_identity_validate(%s)\n", setting_name, 0, 0); 417 418 /* use the private key to sign some test data */ 419 420 SecKeyRef key = NULL; 421 OSStatus ret = SecIdentityCopyPrivateKey(identity, &key); 422 if (ret || key == NULL) { 423 char errbuf[512]; 424 Debug(LDAP_DEBUG_ANY, "TLS: SecIdentityCopyPrivateKey() failed: %s (check %s setting)\n", tlsst_oss2buf(ret, errbuf, sizeof errbuf), setting_name, 0); 425 if (key) 426 CFRelease_and_null(key); 427 return FALSE; 428 } 429 430 CFErrorRef error = NULL; 431 SecTransformRef xform = SecSignTransformCreate(key, &error); 432 CFRelease_and_null(key); 433 if (error || xform == NULL) { 434 char errbuf[512]; 435 Debug(LDAP_DEBUG_ANY, "TLS: SecSignTransformCreate() failed: %s (check %s setting)\n", tlsst_err2buf(error, errbuf, sizeof errbuf), setting_name, 0); 436 if (error) 437 CFRelease_and_null(error); 438 if (xform) 439 CFRelease_and_null(xform); 440 return FALSE; 441 } 442 443 CFDataRef data = CFDataCreate(NULL, (const UInt8 *) "John Hancock", 12); 444 if (data == NULL) { 445 Debug(LDAP_DEBUG_ANY, "TLS: CFDataCreate() failed\n", 0, 0, 0); 446 CFRelease_and_null(xform); 447 return FALSE; 448 } 449 450 Boolean ok = SecTransformSetAttribute(xform, kSecTransformInputAttributeName, data, &error); 451 if (error || !ok) { 452 char errbuf[512]; 453 Debug(LDAP_DEBUG_ANY, "TLS: SecTransformSetAttribute(sign) failed: %s (check %s setting)\n", tlsst_err2buf(error, errbuf, sizeof errbuf), setting_name, 0); 454 if (error) 455 CFRelease_and_null(error); 456 CFRelease_and_null(data); 457 CFRelease_and_null(xform); 458 return FALSE; 459 } 460 461 CFTypeRef result = SecTransformExecute(xform, &error); 462 CFRelease_and_null(xform); 463 if (error || result == NULL || CFGetTypeID(result) != CFDataGetTypeID()) { 464 char errbuf[512]; 465 Debug(LDAP_DEBUG_ANY, "TLS: SecTransformExecute(sign) failed (%lu, %lu): %s\n", result ? CFGetTypeID(result) : 0, CFDataGetTypeID(), tlsst_err2buf(error, errbuf, sizeof errbuf)); 466 if (error) 467 CFRelease_and_null(error); 468 if (result) 469 CFRelease_and_null(result); 470 CFRelease_and_null(data); 471 return FALSE; 472 } 473 474 /* verify the signature using the public key */ 475 476 CFDataRef signature = (CFDataRef) result; 477 result = NULL; 478 479 SecCertificateRef cert = NULL; 480 ret = SecIdentityCopyCertificate(identity, &cert); 481 if (ret || cert == NULL) { 482 char errbuf[512]; 483 Debug(LDAP_DEBUG_ANY, "TLS: SecIdentityCopyCertificate() failed: %s (check %s setting)\n", tlsst_oss2buf(ret, errbuf, sizeof errbuf), setting_name, 0); 484 if (cert) 485 CFRelease_and_null(cert); 486 CFRelease_and_null(signature); 487 CFRelease_and_null(data); 488 return FALSE; 489 } 490 491 ret = SecCertificateCopyPublicKey(cert, &key); 492 CFRelease_and_null(cert); 493 if (ret || key == NULL) { 494 char errbuf[512]; 495 Debug(LDAP_DEBUG_ANY, "TLS: SecCertificateCopyPublicKey() failed: %s (check %s setting)\n", tlsst_oss2buf(ret, errbuf, sizeof errbuf), setting_name, 0); 496 if (key) 497 CFRelease_and_null(key); 498 CFRelease_and_null(signature); 499 CFRelease_and_null(data); 500 return FALSE; 501 } 502 503 xform = SecVerifyTransformCreate(key, signature, &error); 504 CFRelease_and_null(key); 505 CFRelease_and_null(signature); 506 if (error || xform == NULL) { 507 char errbuf[512]; 508 Debug(LDAP_DEBUG_ANY, "TLS: SecVerifyTransformCreate() failed: %s (check %s setting)\n", tlsst_err2buf(error, errbuf, sizeof errbuf), setting_name, 0); 509 if (error) 510 CFRelease_and_null(error); 511 if (xform) 512 CFRelease_and_null(xform); 513 CFRelease_and_null(data); 514 return FALSE; 515 } 516 517 ok = SecTransformSetAttribute(xform, kSecTransformInputAttributeName, data, &error); 518 CFRelease_and_null(data); 519 if (error || !ok) { 520 char errbuf[512]; 521 Debug(LDAP_DEBUG_ANY, "TLS: SecTransformSetAttribute(verify) failed: %s (check %s setting)\n", tlsst_err2buf(error, errbuf, sizeof errbuf), setting_name, 0); 522 if (error) 523 CFRelease_and_null(error); 524 CFRelease_and_null(xform); 525 return FALSE; 526 } 527 528 result = SecTransformExecute(xform, &error); 529 CFRelease_and_null(xform); 530 if (error || result == NULL || CFGetTypeID(result) != CFBooleanGetTypeID()) { 531 char errbuf[512]; 532 Debug(LDAP_DEBUG_ANY, "TLS: SecTransformExecute(verify) failed (%lu, %lu): %s\n", result ? CFGetTypeID(result) : 0, CFBooleanGetTypeID(), tlsst_err2buf(error, errbuf, sizeof errbuf)); 533 if (error) 534 CFRelease_and_null(error); 535 if (result) 536 CFRelease_and_null(result); 537 return FALSE; 538 } 539 540 ok = CFBooleanGetValue((CFBooleanRef) result); 541 CFRelease_and_null(result); 542 return ok; 543} 544 545static CFArrayRef 546tlsst_identity_certs_get(const char *identity, const char *setting_name) 547{ 548 Debug(LDAP_DEBUG_TRACE, "tlsst_identity_certs_get(%s, %s)\n", identity ?: "(null)", setting_name, 0); 549 550 if (identity == NULL || *identity == '\0') 551 return NULL; 552 553 CFArrayRef certs = NULL; 554 555 SecIdentityRef identRef = SecIdentityCopyPreferred(CFSTR("OPENDIRECTORY_SSL_IDENTITY"), NULL, NULL); 556 if (identRef != NULL) 557 syslog(LOG_INFO, "TLS: OPENDIRECTORY_SSL_IDENTITY identity preference overrode configured %s \"%s\"", setting_name, identity); 558 else { 559 /* The identity name may be preceded by "APPLE:". If so, strip that part. */ 560 const char *sep = strchr(identity, ':'); 561 if (sep) 562 identity = sep + 1; 563 CFTypeRef ref = tlsst_item_get(identity, setting_name, kSecClassIdentity); 564 if (ref != NULL) { 565 if (CFGetTypeID(ref) == SecIdentityGetTypeID()) { 566 identRef = (SecIdentityRef) ref; 567 CFRetain(identRef); 568 } else 569 syslog(LOG_ERR, "TLS: Keychain item for \"%s\" is not an identity (check %s setting)", identity, setting_name); 570 571 CFRelease_and_null(ref); 572 } 573 } 574 575 if (identRef != NULL) { 576 /* the private key must be usable */ 577 if (tlsst_identity_validate(identRef, setting_name)) { 578 certs = CFArrayCreate(NULL, (const void **) &identRef, 1, &kCFTypeArrayCallBacks); 579 if (certs == NULL) 580 syslog(LOG_ERR, "TLS: CFArrayCreate() failed"); 581 } else 582 syslog(LOG_ERR, "TLS: Can't get or use private key for %s \"%s\"; is it application-restricted?", setting_name, identity); 583 584 CFRelease_and_null(identRef); 585 } 586 587 return certs; /* caller must release */ 588} 589 590static CFArrayRef 591tlsst_trusted_certs_get(const char *trusted_certs, const char *setting_name) 592{ 593 Debug(LDAP_DEBUG_TRACE, "tlsst_trusted_certs_get(%s, %s)\n", trusted_certs ?: "(null)", setting_name, 0); 594 595 if (trusted_certs == NULL || *trusted_certs == 0) 596 return NULL; 597 598 CFMutableArrayRef certs = NULL; 599 Boolean ok = TRUE; 600 601 char *trusted_names = strdup(trusted_certs); 602 const char *separators = "|"; 603 char *mark = NULL; 604 for (char *tok = strtok_r(trusted_names, separators, &mark); tok != NULL; tok = strtok_r(NULL, separators, &mark)) { 605 CFTypeRef ref = tlsst_item_get(tok, setting_name, kSecClassCertificate); 606 if (ref != NULL) { 607 if (CFGetTypeID(ref) == SecCertificateGetTypeID()) { 608 if (certs == NULL) 609 certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 610 if (certs != NULL) 611 CFArrayAppendValue(certs, ref); 612 else { 613 syslog(LOG_ERR, "TLS: CFArrayCreateMutable() failed"); 614 ok = FALSE; 615 } 616 } else { 617 syslog(LOG_ERR, "TLS: Keychain item for \"%s\" is not a certificate (check %s setting)", tok, setting_name); 618 ok = FALSE; 619 } 620 621 CFRelease_and_null(ref); 622 } else 623 ok = FALSE; 624 625 if (!ok) 626 break; 627 } 628 free(trusted_names); 629 630 if (!ok && certs) 631 CFRelease_and_null(certs); 632 633 return certs; /* caller must release */ 634} 635 636static void 637tlsst_clear_error(tlsst_session *sess) 638{ 639 if (sess->last_error != NULL) { 640 free(sess->last_error); 641 sess->last_error = NULL; 642 } 643} 644 645static void 646tlsst_save_error(tlsst_session *sess, OSStatus oss, const char *func_name, const char *comment) 647{ 648 tlsst_clear_error(sess); 649 650 char errbuf[512]; 651 tlsst_oss2buf(oss, errbuf, sizeof errbuf); 652 653 asprintf(&sess->last_error, "%s failed: %s%s", func_name, errbuf, comment ?: ""); 654} 655 656static int 657tlsst_socket_flags(tlsst_session *sess) 658{ 659 Debug(LDAP_DEBUG_TRACE, "tlsst_socket_flags sess(%p)\n", sess, 0, 0); 660 661 int flags = fcntl( sess->sbiod->sbiod_sb->sb_fd, F_GETFL); 662 663 if (flags != -1) { 664 Debug(LDAP_DEBUG_TRACE, "tlsst_socket_flags ->(%ld)\n", flags, 0, 0); 665 } else { 666 Debug(LDAP_DEBUG_TRACE, "tlsst_socket_flags error(%ld)\n", sock_errno(), 0, 0); 667 } 668 669 return flags; 670} 671 672/* read encrypted data */ 673static OSStatus 674tlsst_socket_read(SSLConnectionRef conn, void *data, size_t *size) 675{ 676 Debug(LDAP_DEBUG_TRACE, "tlsst_socket_read(%p, %lu)\n", data, *size, 0); 677 678 tlsst_session *sess = (tlsst_session *) conn; 679 OSStatus result = 0; 680 size_t requested_size = *size; 681 682 ber_slen_t r = LBER_SBIOD_READ_NEXT(sess->sbiod, data, *size); 683 if (r > 0) { 684 if (r < requested_size) { /* retrieve remaining encrypted data */ 685 result = errSSLWouldBlock; 686 Debug(LDAP_DEBUG_TRACE, "tlsst_socket_read - received (%ld) bytes of (%ld) requested bytes - (%ld) encrypted bytes remaining\n", r, requested_size, requested_size - r); 687 } 688 689 *size = r; 690 691 for (int i = 0; sess->sslv2_detect_length < sizeof sess->sslv2_detect_bytes && i < r; i++) 692 sess->sslv2_detect_bytes[sess->sslv2_detect_length++] = *((unsigned char *) data + i); 693 } else if (r == 0) { 694 *size = 0; 695 result = errSSLClosedGraceful; 696 } else { 697 *size = 0; 698 int err = sock_errno(); 699 if (err == EINTR || err == EAGAIN || err == EWOULDBLOCK) 700 result = errSSLWouldBlock; 701 else 702 result = errSSLClosedAbort; 703 } 704 705 if (result == errSSLWouldBlock) 706 sess->want_read = TRUE; 707 708 return result; 709} 710 711/* write encrypted data */ 712static OSStatus 713tlsst_socket_write(SSLConnectionRef conn, const void *data, size_t *size) 714{ 715 Debug(LDAP_DEBUG_TRACE, "tlsst_socket_write(%p, %lu)\n", data, *size, 0); 716 717 tlsst_session *sess = (tlsst_session *) conn; 718 OSStatus result = 0; 719 size_t requested_size = *size; 720 721 ber_slen_t w = LBER_SBIOD_WRITE_NEXT(sess->sbiod, (void *) data, *size); 722 if (w > 0) { 723 if (w < requested_size) { /* write remaining encrypted data */ 724 result = errSSLWouldBlock; 725 Debug(LDAP_DEBUG_TRACE, "tlsst_socket_write - written (%ld) bytes of (%ld) requested bytes - (%ld) encrypted bytes remaining\n", w, requested_size, requested_size - w); 726 } 727 728 *size = w; 729 } else if (w == 0) { 730 *size = 0; 731 result = errSSLWouldBlock; 732 } else { 733 *size = 0; 734 int err = sock_errno(); 735 if (err == EINTR || err == EAGAIN || err == EWOULDBLOCK) 736 result = errSSLWouldBlock; 737 else 738 result = errSSLClosedAbort; 739 } 740 741 if (result == errSSLWouldBlock) 742 sess->want_write = TRUE; 743 744 return result; 745} 746 747static int 748tlsst_session_handshake(tlsst_session *sess) 749{ 750 Debug(LDAP_DEBUG_TRACE, "tlsst_session_handshake()\n", 0, 0, 0); 751 752 OSStatus ret; 753 const char *comment = NULL; 754 755 for (;;) { 756 sess->want_read = FALSE; 757 sess->want_write = FALSE; 758 ret = SSLHandshake(sess->ssl); 759 if (ret == 0) 760 break; 761 if (ret == errSSLWouldBlock) { 762 int flags = tlsst_socket_flags(sess); 763 764 if (flags != -1) { 765 if (flags & O_NONBLOCK) /* non blocking i/o - break and allow caller to return when data is available */ 766 break; 767 } 768 /* blocking i/o <or> error retrieving socket options */ 769 /* retry SSLHandshake */ 770 continue; 771 } 772 char errbuf[512]; 773 Debug(LDAP_DEBUG_ANY, "TLS: during handshake: %s\n", tlsst_oss2buf(ret, errbuf, sizeof errbuf), 0, 0); 774 if (ret == errSSLPeerAuthCompleted) { 775 sess->cert_received = TRUE; 776 777 SecTrustRef trust = NULL; 778 ret = SSLCopyPeerTrust(sess->ssl, &trust); 779 if (ret == 0 && trust) { 780 if (sess->ctx->trusted_certs) { 781 ret = SecTrustSetAnchorCertificates(trust, sess->ctx->trusted_certs); 782 if (ret) 783 tlsst_report_oss(ret, "TLS: during handshake: SecTrustSetAnchorCertificates(%lu) failed (check %s setting)", CFArrayGetCount(sess->ctx->trusted_certs), 784 sess->is_server ? "olcTLSTrustedCerts" : "TLS_TRUSTED_CERTS"); 785 } 786 if (ret == 0 && sess->ctx->crl_check != LDAP_OPT_X_TLS_CRL_NONE) { 787 ret = SecTrustSetOptions(trust, kSecTrustOptionRequireRevPerCert | kSecTrustOptionFetchIssuerFromNet); 788 if (ret) 789 tlsst_report_oss(ret, "TLS: during handshake: SecTrustSetOptions() failed (check %s setting)", sess->is_server ? "TLSCRLCheck" : "TLS_CRLCHECK"); 790 } 791 if (ret == 0) { 792 SecTrustResultType trustResult = kSecTrustResultInvalid; 793 ret = SecTrustEvaluate(trust, &trustResult); 794 if (ret == 0) { 795 /* LDAP_OPT_X_TLS_... 796 * kSecTrustResult... NEVER ALLOW TRY DEMAND HARD 797 * Invalid ok ok fail fail fail 798 * Proceed ok ok ok ok ok 799 * Confirm (deprecated) ok ok ok fail fail 800 * Deny ok ok ok fail fail 801 * Unspecified ok ok ok ok ok 802 * RecoverableTrustFailure ok ok ok fail fail 803 * FatalTrustFailure ok ok fail fail fail 804 * OtherError ok ok fail fail fail 805 */ 806 if (trustResult == kSecTrustResultProceed || 807 trustResult == kSecTrustResultUnspecified) { 808 Debug(LDAP_DEBUG_ANY, "TLS: during handshake: Peer certificate is trusted\n", 0, 0, 0); 809 sess->cert_trusted = TRUE; 810 /* call SSLHandshake() again */ 811 } else if (sess->ctx->require_cert == LDAP_OPT_X_TLS_NEVER || 812 sess->ctx->require_cert == LDAP_OPT_X_TLS_ALLOW || 813 (sess->ctx->require_cert == LDAP_OPT_X_TLS_TRY && 814 (/* trustResult == kSecTrustResultConfirm || */ 815 trustResult == kSecTrustResultDeny || 816 trustResult == kSecTrustResultRecoverableTrustFailure))) { 817 Debug(LDAP_DEBUG_ANY, "TLS: during handshake: Allowing untrusted peer certificate\n", 0, 0, 0); 818 /* call SSLHandshake() again */ 819 } else { 820 Debug(LDAP_DEBUG_ANY, "TLS: during handshake: Peer certificate is not trusted: %s\n", tlsst_tr2str(trustResult), 0, 0); 821 ret = errSSLPeerBadCert; 822 } 823 } else { 824 char errbuf[512]; 825 Debug(LDAP_DEBUG_ANY, "TLS: during handshake: SecTrustEvaluate() failed: %s\n", tlsst_oss2buf(ret, errbuf, sizeof errbuf), 0, 0); 826 } 827 } 828 829 CFRelease(trust); 830 } else { 831 char errbuf[512]; 832 Debug(LDAP_DEBUG_ANY, "TLS: during handshake: SSLCopyPeerTrust() failed: %s\n", tlsst_oss2buf(ret, errbuf, sizeof errbuf), 0, 0); 833 } 834 if (ret) 835 break; 836 } else { 837 if (ret == errSSLProtocol && sess->is_server && sess->sslv2_detect_length >= 3 && (sess->sslv2_detect_bytes[0] & 0x80) == 0x80 && sess->sslv2_detect_bytes[2] == 0x01) 838 comment = "; possible attempt to connect via obsolete SSLv2 protocol"; 839 break; 840 } 841 } 842 if (ret) { 843 tlsst_save_error(sess, ret, "SSLHandshake()", comment); 844 return -1; 845 } 846 847 if (DebugTest(LDAP_DEBUG_ANY)) { 848 SSLProtocol protocol = kSSLProtocolUnknown; 849 if (SSLGetNegotiatedProtocolVersion(sess->ssl, &protocol) != 0) 850 protocol = kSSLProtocolUnknown; 851 const char *pname = tlsst_protocol_name(protocol); 852 853 SSLCipherSuite cipher = SSL_NO_SUCH_CIPHERSUITE; 854 if (SSLGetNegotiatedCipher(sess->ssl, &cipher) != 0) 855 cipher = SSL_NO_SUCH_CIPHERSUITE; 856 const char *cname = NULL; 857 if (cipher != SSL_NO_SUCH_CIPHERSUITE) 858 cname = XSCipherToName(cipher); 859 if (cname == NULL) 860 cname = "unknown"; 861 862 Debug(LDAP_DEBUG_ANY, "TLS: %s session established using %d-bit %s cipher\n", pname, tlsst_session_strength((tls_session *) sess), cname); 863 } 864 865 tlsst_clear_error(sess); 866 return 0; 867} 868 869static SecCertificateRef 870tlsst_copy_peer_cert(tlsst_session *sess) 871{ 872 Debug(LDAP_DEBUG_TRACE, "tlsst_copy_peer_cert()\n", 0, 0, 0); 873 874 SecCertificateRef result = NULL; 875 876 if (sess->cert_received && sess->cert_trusted) { 877 SecTrustRef trust = NULL; 878 (void) SSLCopyPeerTrust(sess->ssl, &trust); 879 if (trust != NULL) { 880 if (SecTrustGetCertificateCount(trust) > 0) { 881 result = SecTrustGetCertificateAtIndex(trust, 0); 882 CFRetain(result); 883 } 884 885 CFRelease_and_null(trust); 886 } 887 } 888 889 return result; // caller must release 890} 891 892static CFDataRef 893tlsst_builtin_dhparams(void) 894{ 895 Debug(LDAP_DEBUG_TRACE, "tlsst_builtin_dhparams()\n", 0, 0, 0); 896 897 // output of command "openssl dhparam 1024" 898 static const char dhparams_pem[] = 899 // -----BEGIN DH PARAMETERS----- 900 "MIGHAoGBAOi2AYTgWEzB4TI07BKz4Z6H3oFKvCz77YAaPizFwUW5Jy7JDV6vXO8n" 901 "RrjSCuZ8V4TyfewkDW/iju5Rkgsy44UO9fGDLWjNG8fom92fuXBdNcbO8zAvG97B" 902 "mojNok6fpxvsFoUWWLmrlVPr/gtWANZAmSDr78ovtstQcdf5+6a7AgEC"; 903 // -----END DH PARAMETERS----- 904 905 CFDataRef result = NULL; 906 907 CFDataRef pemData = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *) dhparams_pem, strlen(dhparams_pem), kCFAllocatorNull); 908 if (pemData != NULL) { 909 SecTransformRef decoder = SecDecodeTransformCreate(kSecBase64Encoding, NULL); 910 if (decoder != NULL) { 911 SecTransformSetAttribute(decoder, kSecTransformInputAttributeName, pemData, NULL); 912 result = SecTransformExecute(decoder, NULL); 913 914 CFRelease(decoder); 915 } 916 917 CFRelease_and_null(pemData); 918 } 919 920 return result; // caller must release 921} 922 923static int 924tlsst_init(void) 925{ 926 Debug(LDAP_DEBUG_TRACE, "tlsst_init()\n", 0, 0, 0); 927 928 /* initialize PRNG */ 929 /* except we can't: <rdar://problem/10587132> */ 930 struct stat st; 931 if (stat("/dev/random", &st) != 0) { 932 syslog(LOG_ERR, "TLS: /dev/random not available. SSL will not work. Don't chroot."); 933 return -1; 934 } 935 936 /* load error strings */ 937 char buf[256]; 938 (void) tlsst_oss2buf(errSSLWouldBlock, buf, sizeof buf); 939 940 SecKeychainSetUserInteractionAllowed(false); 941 SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); 942 943 return 0; 944} 945 946static void 947tlsst_destroy(void) 948{ 949 Debug(LDAP_DEBUG_TRACE, "tlsst_destroy()\n", 0, 0, 0); 950} 951 952static tls_ctx * 953tlsst_ctx_new(struct ldapoptions *lo) 954{ 955 tlsst_ctx *ctx = LDAP_CALLOC(1, sizeof *ctx); 956 Debug(LDAP_DEBUG_TRACE, "tlsst_ctx_new() = %p\n", ctx, 0, 0); 957 if (ctx) { 958 ctx->refcount = 1; 959#ifdef LDAP_R_COMPILE 960 ldap_pvt_thread_mutex_init(&ctx->refmutex); 961#endif 962 } 963 return (tls_ctx *) ctx; 964} 965 966static void 967tlsst_ctx_ref(tls_ctx *_ctx) 968{ 969 tlsst_ctx *ctx = (tlsst_ctx *) _ctx; 970 Debug(LDAP_DEBUG_TRACE, "tlsst_ctx_ref(%p)\n", ctx, 0, 0); 971 LDAP_MUTEX_LOCK(&ctx->refmutex); 972 ++ctx->refcount; 973 LDAP_MUTEX_UNLOCK(&ctx->refmutex); 974} 975 976static void 977tlsst_ctx_free(tls_ctx *_ctx) 978{ 979 tlsst_ctx *ctx = (tlsst_ctx *) _ctx; 980 Debug(LDAP_DEBUG_TRACE, "tlsst_ctx_free(%p)\n", ctx, 0, 0); 981 if (ctx) { 982 LDAP_MUTEX_LOCK(&ctx->refmutex); 983 int refcount = --ctx->refcount; 984 LDAP_MUTEX_UNLOCK(&ctx->refmutex); 985 if (refcount == 0) { 986 if (ctx->ciphers != NULL) { 987 free(ctx->ciphers); 988 ctx->ciphers = NULL; 989 } 990 if (ctx->identity_certs != NULL) 991 CFRelease_and_null(ctx->identity_certs); 992 if (ctx->trusted_certs != NULL) 993 CFRelease_and_null(ctx->trusted_certs); 994 if (ctx->dhparams != NULL) 995 CFRelease_and_null(ctx->dhparams); 996 997 LDAP_FREE(ctx); 998 ctx = NULL; 999 } 1000 } 1001} 1002 1003static int 1004tlsst_ctx_init(struct ldapoptions *lo, struct ldaptls *lt, int is_server) 1005{ 1006 tlsst_ctx *ctx = lo->ldo_tls_ctx; 1007 Debug(LDAP_DEBUG_TRACE, "tlsst_ctx_init(%p)\n", ctx, 0, 0); 1008 int result = -1; 1009 1010 if (lo->ldo_tls_protocol_min) { 1011 ctx->protocol_min = tlsst_protocol_map_ldap2st(lo->ldo_tls_protocol_min, is_server ? "TLSProtocolMin" : "TLS_PROTOCOL_MIN"); 1012 if (ctx->protocol_min == kSSLProtocolUnknown) 1013 return -1; 1014 else if (ctx->protocol_min == kSSLProtocol2) { 1015 syslog(LOG_ERR, "TLS: SSLv2 is no longer supported (check %s setting)", is_server ? "TLSProtocolMin" : "TLS_PROTOCOL_MIN"); 1016 return -1; 1017 } 1018 } else 1019 ctx->protocol_min = kSSLProtocolUnknown; 1020 ctx->require_cert = lo->ldo_tls_require_cert; 1021 ctx->crl_check = lo->ldo_tls_crlcheck; 1022 ctx->ciphers = tlsst_ciphers_get(lo->ldo_tls_ciphersuite, &ctx->ciphers_count, is_server ? "TLSCipherSuite" : "TLS_CIPHER_SUITE"); 1023 if (ctx->ciphers_count == 0) 1024 return -1; 1025 if (lo->ldo_tls_identity) { 1026 ctx->identity_certs = tlsst_identity_certs_get(lo->ldo_tls_identity, is_server ? "olcTLSIdentity" : "TLS_IDENTITY"); 1027 if (ctx->identity_certs == NULL) 1028 return -1; 1029 } 1030 if (lo->ldo_tls_trusted_certs) { 1031 ctx->trusted_certs = tlsst_trusted_certs_get(lo->ldo_tls_trusted_certs, is_server ? "olcTLSTrustedCerts" : "TLS_TRUSTED_CERTS"); 1032 if (ctx->trusted_certs == NULL) 1033 return -1; 1034 } 1035 if (lo->ldo_tls_dhfile) { 1036 CFStringRef path = CFStringCreateWithCString(NULL, lo->ldo_tls_dhfile, kCFStringEncodingUTF8); 1037 if (path != NULL) { 1038 CFArrayRef dhparams = XSDHParamCreateFromFile(path); 1039 if (dhparams != NULL) { 1040 // use only the strongest one <rdar://problem/10595552> 1041 CFDataRef maxdh = NULL; 1042 CFIndex maxdhsize = 0; 1043 for (CFIndex i = 0; i < CFArrayGetCount(dhparams); ++i) { 1044 CFDataRef dh = CFArrayGetValueAtIndex(dhparams, i); 1045 CFIndex size = XSDHParamGetSize(dh); 1046 if (maxdhsize < size) { 1047 maxdh = dh; 1048 maxdhsize = size; 1049 } 1050 } 1051 1052 if (maxdh != NULL) { 1053 CFRetain(maxdh); 1054 ctx->dhparams = maxdh; 1055 1056 result = 0; 1057 } else 1058 syslog(LOG_ERR, "TLS: DH file %s contains no DH params (check TLSDHParamFile setting)", lo->ldo_tls_dhfile); 1059 1060 CFRelease_and_null(dhparams); 1061 } else 1062 syslog(LOG_ERR, "TLS: Unable to parse DH file %s (check TLSDHParamFile setting)", lo->ldo_tls_dhfile); 1063 1064 CFRelease_and_null(path); 1065 } else 1066 syslog(LOG_ERR, "TLS: CFStringCreateWithCString(%s) failed (check TLSDHParamFile setting)", lo->ldo_tls_dhfile); 1067 } else { 1068 ctx->dhparams = tlsst_builtin_dhparams(); 1069 result = 0; 1070 } 1071 1072 return result; 1073} 1074 1075static tls_session * 1076tlsst_session_new(tls_ctx *_ctx, int is_server) 1077{ 1078 tlsst_ctx *ctx = (tlsst_ctx *) _ctx; 1079 Debug(LDAP_DEBUG_TRACE, "tlsst_session_new(%p)\n", ctx, 0, 0); 1080 1081 struct stat st; 1082 if (stat("/dev/random", &st) != 0) { 1083 syslog(LOG_ERR, "TLS: /dev/random not available. SSL will not work. Don't chroot."); 1084 return NULL; 1085 } 1086 1087 SSLContextRef ssl = SSLCreateContext(NULL, is_server ? kSSLServerSide : kSSLClientSide, kSSLStreamType); 1088 if (ssl == NULL) { 1089 syslog(LOG_ERR, "TLS: SSLCreateContext() failed"); 1090 return NULL; 1091 } 1092 1093 OSStatus ret; 1094 if (is_server) { 1095 ret = SSLSetClientSideAuthenticate(ssl, tlsst_auth_map_ldap2st(ctx->require_cert)); 1096 if (ret) { 1097 tlsst_report_oss(ret, "TLS: SSLSetClientSideAuthentication(%d) failed", (int) tlsst_auth_map_ldap2st(ctx->require_cert)); 1098 CFRelease_and_null(ssl); 1099 return NULL; 1100 } 1101 1102 if (ctx->dhparams != NULL) { 1103 ret = SSLSetDiffieHellmanParams(ssl, CFDataGetBytePtr(ctx->dhparams), CFDataGetLength(ctx->dhparams)); 1104 if (ret) { 1105 tlsst_report_oss(ret, "TLS: SSLSetDiffieHellmanParams() failed"); 1106 CFRelease_and_null(ssl); 1107 return NULL; 1108 } 1109 } 1110 } 1111 1112 if (ctx->ciphers != NULL) { 1113 if (tlsst_ciphers_set(ssl, ctx->ciphers, ctx->ciphers_count, is_server ? "TLSCipherSuite" : "TLS_CIPHER_SUITE") < 0) { 1114 CFRelease_and_null(ssl); 1115 return NULL; 1116 } 1117 } 1118 1119 if (ctx->identity_certs != NULL) { 1120 ret = SSLSetCertificate(ssl, ctx->identity_certs); 1121 if (ret) { 1122 tlsst_report_oss(ret, "TLS: SSLSetCertificate() failed (check %s setting)", is_server ? "olcTLSIdentity" : "TLS_IDENTITY"); 1123 CFRelease_and_null(ssl); 1124 return NULL; 1125 } 1126 } 1127 1128 if (ctx->protocol_min != kSSLProtocolUnknown) { 1129 ret = SSLSetProtocolVersionMin(ssl, ctx->protocol_min); 1130 if (ret) { 1131 tlsst_report_oss(ret, "TLS: SSLSetProtocolVersionMin(%d) failed (check %s setting)", (int) ctx->protocol_min, is_server ? "TLSProtocolMin" : "TLS_PROTOCOL_MIN"); 1132 CFRelease_and_null(ssl); 1133 return NULL; 1134 } 1135 } 1136 1137 ret = SSLSetIOFuncs(ssl, tlsst_socket_read, tlsst_socket_write); 1138 if (ret) { 1139 tlsst_report_oss(ret, "TLS: SSLSetIOFuncs() failed"); 1140 CFRelease_and_null(ssl); 1141 return NULL; 1142 } 1143 1144 ret = SSLSetSessionOption(ssl, is_server ? kSSLSessionOptionBreakOnClientAuth : kSSLSessionOptionBreakOnServerAuth, TRUE); 1145 if (ret) { 1146 tlsst_report_oss(ret, "TLS: SSLSetSessionOption(BreakOnAuth) failed"); 1147 CFRelease_and_null(ssl); 1148 return NULL; 1149 } 1150 1151 tlsst_session *sess = LDAP_CALLOC(1, sizeof *sess); 1152 1153 ret = SSLSetConnection(ssl, (SSLConnectionRef) sess); 1154 if (ret) { 1155 tlsst_report_oss(ret, "TLS: SSLSetSessionOption(BreakOnAuth) failed"); 1156 CFRelease_and_null(ssl); 1157 LDAP_FREE(sess); 1158 return NULL; 1159 } 1160 1161 sess->ctx = ctx; 1162 tlsst_ctx_ref((tls_ctx *) ctx); 1163 sess->ssl = ssl; 1164 sess->is_server = is_server; 1165 1166 Debug(LDAP_DEBUG_TRACE, "tlsst_session_new(%p) = %p\n", ctx, sess, 0); 1167 return (tls_session *) sess; 1168} 1169 1170static void 1171tlsst_session_free(tlsst_session *sess) 1172{ 1173 Debug(LDAP_DEBUG_TRACE, "tlsst_session_free(%p)\n", sess, 0, 0); 1174 CFRelease_and_null(sess->ssl); 1175 tlsst_ctx_free((tls_ctx *) sess->ctx); 1176 sess->ctx = NULL; 1177 tlsst_clear_error(sess); 1178 if (sess->subject_data != NULL) 1179 CFRelease_and_null(sess->subject_data); 1180 if (sess->issuer_data != NULL) 1181 CFRelease_and_null(sess->issuer_data); 1182 LDAP_FREE(sess); 1183} 1184 1185static int 1186tlsst_session_connect(LDAP *ld, tls_session *_sess) 1187{ 1188 tlsst_session *sess = (tlsst_session *) _sess; 1189 Debug(LDAP_DEBUG_TRACE, "tlsst_session_connect(%p)\n", sess, 0, 0); 1190 1191 return tlsst_session_handshake(sess); 1192} 1193 1194static int 1195tlsst_session_accept(tls_session *_sess) 1196{ 1197 tlsst_session *sess = (tlsst_session *) _sess; 1198 Debug(LDAP_DEBUG_TRACE, "tlsst_session_accept(%p)\n", sess, 0, 0); 1199 1200 return tlsst_session_handshake(sess); 1201} 1202 1203static int 1204tlsst_session_upflags(Sockbuf *sb, tls_session *_sess, int rc) 1205{ 1206 tlsst_session *sess = (tlsst_session *) _sess; 1207 Debug(LDAP_DEBUG_TRACE, "tlsst_session_upflags(%p)\n", sess, 0, 0); 1208 1209 sb->sb_trans_needs_read = sess->want_read; 1210 sb->sb_trans_needs_write = sess->want_write; 1211 1212 return sess->want_read || sess->want_write; 1213} 1214 1215static char * 1216tlsst_session_errmsg(tls_session *_sess, int rc, char *buf, size_t len) 1217{ 1218 tlsst_session *sess = (tlsst_session *) _sess; 1219 Debug(LDAP_DEBUG_TRACE, "tlsst_session_errmsg(%p)\n", sess, 0, 0); 1220 1221 if (sess->last_error) { 1222 strlcpy(buf, sess->last_error, len); 1223 return buf; 1224 } 1225 1226 return NULL; 1227} 1228 1229static int 1230tlsst_session_my_dn(tls_session *_sess, struct berval *der_dn) 1231{ 1232 tlsst_session *sess = (tlsst_session *) _sess; 1233 Debug(LDAP_DEBUG_TRACE, "tlsst_session_my_dn(%p)\n", sess, 0, 0); 1234 1235 // make sure the pointer we return in der_dn->bv_val remains valid for as long as the session does 1236 if (!sess->subject_cached) { 1237 sess->subject_cached = TRUE; 1238 sess->subject_result = LDAP_INVALID_CREDENTIALS; 1239 1240 // SecureTransport has no way to retrieve the server certificate <rdar://problem/10930619> so fudge it by using identity_certs 1241 if (sess->ctx->identity_certs != NULL && CFArrayGetCount(sess->ctx->identity_certs) > 0) { 1242 SecIdentityRef identity = (SecIdentityRef) CFArrayGetValueAtIndex(sess->ctx->identity_certs, 0); 1243 SecCertificateRef cert = NULL; 1244 (void) SecIdentityCopyCertificate(identity, &cert); 1245 if (cert != NULL) { 1246 CFDataRef subject = SecCertificateCopyNormalizedSubjectContent(cert, NULL); 1247 if (subject != NULL) { 1248 // mutable because der_dn->bv_val is not const 1249 sess->subject_data = CFDataCreateMutableCopy(NULL, 0, subject); 1250 sess->subject_result = LDAP_SUCCESS; 1251 1252 CFRelease_and_null(subject); 1253 } 1254 1255 CFRelease_and_null(cert); 1256 } 1257 } 1258 } 1259 1260 if (sess->subject_data != NULL) { 1261 der_dn->bv_len = CFDataGetLength(sess->subject_data); 1262 der_dn->bv_val = (char *) CFDataGetMutableBytePtr(sess->subject_data); 1263 } 1264 return sess->subject_result; 1265} 1266 1267static int 1268tlsst_session_peer_dn(tls_session *_sess, struct berval *der_dn) 1269{ 1270 tlsst_session *sess = (tlsst_session *) _sess; 1271 Debug(LDAP_DEBUG_TRACE, "tlsst_session_peer_dn(%p)\n", sess, 0, 0); 1272 1273 // make sure the pointer we return in der_dn->bv_val remains valid for as long as the session does 1274 if (!sess->issuer_cached) { 1275 sess->issuer_cached = TRUE; 1276 sess->issuer_result = LDAP_INVALID_CREDENTIALS; 1277 1278 SecCertificateRef cert = tlsst_copy_peer_cert(sess); 1279 if (cert != NULL) { 1280 CFDataRef issuer = SecCertificateCopyNormalizedIssuerContent(cert, NULL); 1281 if (issuer != NULL) { 1282 // mutable because der_dn->bv_val is not const 1283 sess->issuer_data = CFDataCreateMutableCopy(NULL, 0, issuer); 1284 sess->issuer_result = LDAP_SUCCESS; 1285 1286 CFRelease_and_null(issuer); 1287 } 1288 1289 CFRelease_and_null(cert); 1290 } 1291 } 1292 1293 if (sess->issuer_data != NULL) { 1294 der_dn->bv_len = CFDataGetLength(sess->issuer_data); 1295 der_dn->bv_val = (char *) CFDataGetMutableBytePtr(sess->issuer_data); 1296 } 1297 return sess->issuer_result; 1298} 1299 1300/* what kind of hostname were we given? */ 1301#define IS_DNS 0 1302#define IS_IP4 1 1303#define IS_IP6 2 1304 1305// copied from the other tls*_session_chkhost() implementations so don't blame me for the yuck 1306static int 1307tlsst_session_chkhost(LDAP *ld, tls_session *_sess, const char *name_in) 1308{ 1309 tlsst_session *sess = (tlsst_session *) _sess; 1310 int ret = LDAP_LOCAL_ERROR; 1311 const char *name; 1312 char *ptr; 1313 int ntype = IS_DNS, nlen; 1314#ifdef LDAP_PF_INET6 1315 struct in6_addr addr; 1316#else 1317 struct in_addr addr; 1318#endif 1319 1320 Debug(LDAP_DEBUG_TRACE, "tlsst_session_chkhost(%p)\n", sess, 0, 0); 1321 1322 if( ldap_int_hostname && 1323 ( !name_in || !strcasecmp( name_in, "localhost" ) ) ) 1324 { 1325 name = ldap_int_hostname; 1326 } else { 1327 name = name_in; 1328 } 1329 nlen = strlen(name); 1330 1331 SecCertificateRef cert = tlsst_copy_peer_cert(sess); 1332 if (cert == NULL) { 1333 Debug( LDAP_DEBUG_ANY, 1334 "TLS: unable to get peer certificate.\n", 1335 0, 0, 0 ); 1336 /* If this was a fatal condition, things would have 1337 * aborted long before now. 1338 */ 1339 return LDAP_SUCCESS; 1340 } 1341 1342 memset(&addr, 0, sizeof addr); 1343#ifdef LDAP_PF_INET6 1344 if (name[0] == '[' && strchr(name, ']')) { 1345 char *n2 = ldap_strdup(name+1); 1346 *strchr(n2, ']') = 0; 1347 if (inet_pton(AF_INET6, n2, &addr) > 0) 1348 ntype = IS_IP6; 1349 LDAP_FREE(n2); 1350 } else 1351#endif 1352 if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) { 1353 if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4; 1354 } 1355 1356 int dlen = 0; 1357 char *domain = NULL; 1358 if (ntype == IS_DNS) { 1359 domain = strchr(name, '.'); 1360 if (domain) 1361 dlen = nlen - (domain-name); 1362 } 1363 1364 CFDictionaryRef certContents = SecCertificateCopyValues(cert, NULL, NULL); 1365 if (certContents != NULL) { 1366 CFDictionaryRef altNamesValues = (CFDictionaryRef) CFDictionaryGetValue(certContents, kSecOIDSubjectAltName); 1367 if (altNamesValues != NULL && CFGetTypeID(altNamesValues) == CFDictionaryGetTypeID()) { 1368 CFArrayRef altNames = (CFArrayRef) CFDictionaryGetValue(altNamesValues, kSecPropertyKeyValue); 1369 if (altNames != NULL && CFGetTypeID(altNames) == CFArrayGetTypeID()) { 1370 CFIndex i; 1371 for (i = 0; i < CFArrayGetCount(altNames); ++i) { 1372 CFDictionaryRef altNameValues = (CFDictionaryRef) CFArrayGetValueAtIndex(altNames, i); 1373 if (CFGetTypeID(altNameValues) == CFDictionaryGetTypeID()) { 1374 CFStringRef altNameLabel = CFDictionaryGetValue(altNameValues, kSecPropertyKeyLabel); 1375 CFStringRef altNameValue = CFDictionaryGetValue(altNameValues, kSecPropertyKeyValue); 1376 if (altNameLabel != NULL && CFGetTypeID(altNameLabel) == CFStringGetTypeID() && 1377 altNameValue != NULL && CFGetTypeID(altNameValue) == CFStringGetTypeID()) { 1378 CFIndex altNameLen = CFStringGetLength(altNameValue); 1379 CFIndex altNameSize = CFStringGetMaximumSizeForEncoding(altNameLen, kCFStringEncodingUTF8) + 1; 1380 char *altNameBuf = alloca(altNameSize); 1381 if (!CFStringGetCString(altNameValue, altNameBuf, altNameSize, kCFStringEncodingUTF8)) 1382 continue; 1383 1384 if (CFEqual(altNameLabel, CFSTR("DNS Name"))) { 1385 if (ntype != IS_DNS) 1386 continue; 1387 1388 /* ignore empty */ 1389 if (altNameLen == 0) 1390 continue; 1391 1392 /* Is this an exact match? */ 1393 if (nlen == altNameLen && !strncasecmp(name, altNameBuf, nlen)) 1394 break; 1395 1396 /* Is this a wildcard match? */ 1397 if (domain && dlen == altNameLen - 1 && altNameBuf[0] == '*' && altNameBuf[1] == '.' && !strncasecmp(domain, &altNameBuf[1], dlen)) 1398 break; 1399 } else if (CFEqual(altNameLabel, CFSTR("IP Address"))) { 1400 if (ntype == IS_DNS) 1401 continue; 1402 1403#ifdef LDAP_PF_INET6 1404 if (ntype == IS_IP6) { 1405 struct in6_addr altAddr6; 1406 memset(&altAddr6, 0, sizeof altAddr6); 1407 if (inet_pton(AF_INET6, altNameBuf, &altAddr6) > 0 && !memcmp(&addr, &altAddr6, sizeof altAddr6)) 1408 break; 1409 } else 1410#endif 1411 if (ntype == IS_IP4) { 1412 struct in_addr altAddr4; 1413 memset(&altAddr4, 0, sizeof altAddr4); 1414 if (inet_aton(altNameBuf, &altAddr4) && !memcmp(&addr, &altAddr4, sizeof altAddr4)) 1415 break; 1416 } 1417 } 1418 } 1419 } 1420 } 1421 if (i < CFArrayGetCount(altNames)) 1422 ret = LDAP_SUCCESS; 1423 } 1424 } 1425 1426 if (ret != LDAP_SUCCESS) { 1427 CFStringRef commonName = NULL; 1428 1429 /* find the last CN */ 1430 CFDictionaryRef commonNamesValues = (CFDictionaryRef) CFDictionaryGetValue(certContents, kSecOIDCommonName); 1431 if (commonNamesValues != NULL && CFGetTypeID(commonNamesValues) == CFDictionaryGetTypeID()) { 1432 CFArrayRef commonNames = (CFArrayRef) CFDictionaryGetValue(commonNamesValues, kSecPropertyKeyValue); 1433 if (commonNames != NULL && CFGetTypeID(commonNames) == CFArrayGetTypeID() && CFArrayGetCount(commonNames) > 0) 1434 commonName = CFArrayGetValueAtIndex(commonNames, CFArrayGetCount(commonNames) - 1); 1435 } 1436 1437 CFIndex commonNameLen = 0; 1438 CFIndex commonNameSize = 0; 1439 char *commonNameBuf = NULL; 1440 if (commonName != NULL) { 1441 commonNameLen = CFStringGetLength(commonName); 1442 commonNameSize = CFStringGetMaximumSizeForEncoding(commonNameLen, kCFStringEncodingUTF8) + 1; 1443 commonNameBuf = LDAP_MALLOC(commonNameSize); 1444 1445 if (!CFStringGetCString(commonName, commonNameBuf, commonNameSize, kCFStringEncodingUTF8)) 1446 commonNameLen = 0; 1447 } 1448 1449 if (commonNameLen == 0) { 1450 Debug(LDAP_DEBUG_ANY, "TLS: unable to get common name from peer certificate\n", 0, 0, 0); 1451 ret = LDAP_CONNECT_ERROR; 1452 if (ld->ld_error) 1453 LDAP_FREE(ld->ld_error); 1454 ld->ld_error = LDAP_STRDUP("TLS: unable to get CN from peer certificate"); 1455 } else if (commonNameLen == nlen && strncasecmp(name, commonNameBuf, nlen) == 0) 1456 ret = LDAP_SUCCESS; 1457 else if (domain && dlen == commonNameLen - 1 && commonNameBuf[0] == '*' && commonNameBuf[1] == '.' && !strncasecmp(domain, &commonNameBuf[1], dlen)) 1458 ret = LDAP_SUCCESS; 1459 1460 if( ret == LDAP_LOCAL_ERROR ) { 1461 Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match " 1462 "common name in certificate (%.*s).\n", 1463 name, (int) commonNameLen, commonNameBuf ); 1464 ret = LDAP_CONNECT_ERROR; 1465 if ( ld->ld_error ) { 1466 LDAP_FREE( ld->ld_error ); 1467 } 1468 ld->ld_error = LDAP_STRDUP( 1469 _("TLS: hostname does not match CN in peer certificate")); 1470 } 1471 1472 if (commonNameBuf != NULL) 1473 LDAP_FREE(commonNameBuf); 1474 } 1475 1476 CFRelease_and_null(certContents); 1477 } 1478 1479 CFRelease_and_null(cert); 1480 return ret; 1481} 1482 1483static int 1484tlsst_session_strength(tls_session *_sess) 1485{ 1486 tlsst_session *sess = (tlsst_session *) _sess; 1487 Debug(LDAP_DEBUG_TRACE, "tlsst_session_strength(%p)\n", sess, 0, 0); 1488 1489 int result = 0; 1490 1491 SSLCipherSuite cipher = SSL_NO_SUCH_CIPHERSUITE; 1492 if (SSLGetNegotiatedCipher(sess->ssl, &cipher) != 0) 1493 cipher = SSL_NO_SUCH_CIPHERSUITE; 1494 if (cipher != SSL_NO_SUCH_CIPHERSUITE) { 1495 CFDictionaryRef properties = XSCipherCopyCipherProperties(cipher); 1496 if (properties != NULL) { 1497 CFNumberRef bits = CFDictionaryGetValue(properties, kXSCipherPropertyStrengthBits); 1498 if (bits != NULL) { 1499 if (!CFNumberGetValue(bits, kCFNumberIntType, &result)) 1500 result = 0; 1501 } 1502 1503 CFRelease_and_null(properties); 1504 } 1505 } 1506 1507 return result; 1508} 1509 1510static void 1511tlsst_thr_init(void) 1512{ 1513 Debug(LDAP_DEBUG_TRACE, "tlsst_thr_init()\n", 0, 0, 0); 1514} 1515 1516static int 1517tlsst_sb_setup(Sockbuf_IO_Desc *sbiod, void *arg) 1518{ 1519 tlsst_session *sess = (tlsst_session *) arg; 1520 Debug(LDAP_DEBUG_TRACE, "tlsst_sb_setup(%p)\n", sess, 0, 0); 1521 assert(sess->sbiod == NULL); 1522 sess->sbiod = sbiod; 1523 sbiod->sbiod_pvt = sess; 1524 return 0; 1525} 1526 1527static int 1528tlsst_sb_remove(Sockbuf_IO_Desc *sbiod) 1529{ 1530 tlsst_session *sess = (tlsst_session *) sbiod->sbiod_pvt; 1531 Debug(LDAP_DEBUG_TRACE, "tlsst_sb_remove(%p)\n", sess, 0, 0); 1532 sbiod->sbiod_pvt = NULL; 1533 sess->sbiod = NULL; 1534 tlsst_session_free(sess); 1535 return 0; 1536} 1537 1538static int 1539tlsst_sb_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg) 1540{ 1541 tlsst_session *sess = (tlsst_session *) sbiod->sbiod_pvt; 1542 Debug(LDAP_DEBUG_TRACE, "tlsst_sb_ctrl(%p, %d)\n", sess, opt, 0); 1543 1544 switch (opt) { 1545 case LBER_SB_OPT_GET_SSL: 1546 *(tlsst_session **) arg = sess; 1547 return 1; 1548 case LBER_SB_OPT_DATA_READY: 1549 { 1550 size_t bytes = 0; 1551 (void) SSLGetBufferedReadSize(sess->ssl, &bytes); 1552 if (bytes > 0) 1553 return 1; 1554 // else pass opt to next sbiod 1555 } 1556 break; 1557 } 1558 1559 return LBER_SBIOD_CTRL_NEXT(sbiod, opt, arg); 1560} 1561 1562/* read cleartext data */ 1563static ber_slen_t 1564tlsst_sb_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) 1565{ 1566 tlsst_session *sess = (tlsst_session *) sbiod->sbiod_pvt; 1567 Debug(LDAP_DEBUG_TRACE, "tlsst_sb_read(%p, %lu)\n", sess, len, 0); 1568 1569 if (len <= 0) 1570 return 0; 1571 1572 sess->want_read = FALSE; 1573 sess->want_write = FALSE; 1574 1575 size_t processed = 0; 1576 OSStatus ret = SSLRead(sess->ssl, buf, len, &processed); 1577 if (tlsst_session_upflags(sbiod->sbiod_sb, (tls_session *) sess, ret)) 1578 sock_errset(EWOULDBLOCK); 1579 if (processed > 0) 1580 return processed; 1581 if (ret == errSSLWouldBlock) { 1582 sock_errset(EWOULDBLOCK); 1583 return -1; 1584 } 1585 if (ret) 1586 tlsst_save_error(sess, ret, "SSLRead()", NULL); 1587 else 1588 tlsst_clear_error(sess); 1589 if (ret == errSSLClosedGraceful) 1590 return 0; 1591 return -1; 1592} 1593 1594/* write cleartext data */ 1595static ber_slen_t 1596tlsst_sb_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) 1597{ 1598 tlsst_session *sess = (tlsst_session *) sbiod->sbiod_pvt; 1599 Debug(LDAP_DEBUG_TRACE, "tlsst_sb_write(%p, %lu)\n", sess, len, 0); 1600 1601 if (len <= 0) 1602 return 0; 1603 1604 sess->want_read = FALSE; 1605 sess->want_write = FALSE; 1606 1607 size_t processed = 0; 1608 OSStatus ret = SSLWrite(sess->ssl, buf, len, &processed); 1609 if (tlsst_session_upflags(sbiod->sbiod_sb, (tls_session *) sess, ret)) 1610 sock_errset(EWOULDBLOCK); 1611 if (processed > 0) 1612 return processed; 1613 if (ret == errSSLWouldBlock) { 1614 sock_errset(EWOULDBLOCK); 1615 return -1; 1616 } 1617 if (ret) 1618 tlsst_save_error(sess, ret, "SSLWrite()", NULL); 1619 else 1620 tlsst_clear_error(sess); 1621 return -1; 1622} 1623 1624static int 1625tlsst_sb_close(Sockbuf_IO_Desc *sbiod) 1626{ 1627 tlsst_session *sess = (tlsst_session *) sbiod->sbiod_pvt; 1628 Debug(LDAP_DEBUG_TRACE, "tlsst_sb_close(%p)\n", sess, 0, 0); 1629 1630 OSStatus ret = SSLClose(sess->ssl); 1631 if (ret) 1632 tlsst_save_error(sess, ret, "SSLClose()", NULL); 1633 else 1634 tlsst_clear_error(sess); 1635 return ret == 0 ? 0 : -1; 1636} 1637 1638static Sockbuf_IO tlsst_sbio = { 1639 tlsst_sb_setup, /* sbi_setup */ 1640 tlsst_sb_remove, /* sbi_remove */ 1641 tlsst_sb_ctrl, /* sbi_ctrl */ 1642 tlsst_sb_read, /* sbi_read */ 1643 tlsst_sb_write, /* sbi_write */ 1644 tlsst_sb_close /* sbi_close */ 1645}; 1646 1647tls_impl ldap_int_tls_impl = { 1648 "SecureTransport", 1649 1650 tlsst_init, 1651 tlsst_destroy, 1652 1653 tlsst_ctx_new, 1654 tlsst_ctx_ref, 1655 tlsst_ctx_free, 1656 tlsst_ctx_init, 1657 1658 tlsst_session_new, 1659 tlsst_session_connect, 1660 tlsst_session_accept, 1661 tlsst_session_upflags, 1662 tlsst_session_errmsg, 1663 tlsst_session_my_dn, 1664 tlsst_session_peer_dn, 1665 tlsst_session_chkhost, 1666 tlsst_session_strength, 1667 1668 &tlsst_sbio, 1669 1670#ifdef LDAP_R_COMPILE 1671 tlsst_thr_init, 1672#else 1673 NULL, 1674#endif 1675 1676 0 1677}; 1678 1679#endif /* HAVE_SECURE_TRANSPORT */ 1680