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