1 /*
2  * Copyright (c) 2017, 2019-2020 Paul Mattes.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the names of Paul Mattes, Don Russell, Jeff Sparkes, GTRC
13  *       nor their contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY PAUL MATTES, "AS IS" AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL PAUL MATTES BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
22  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /*
29  *	sio_secure_transport.c
30  *		Secure I/O via the MacOS Secure Transport facility.
31  */
32 
33 #include "globals.h"
34 
35 #include <Security/Security.h>
36 #include <Security/SecureTransport.h>
37 
38 #include <string.h>
39 
40 #include "tls_config.h"
41 
42 #include "lazya.h"
43 #include "sio.h"
44 #include "sioc.h"
45 #include "tls_passwd_gui.h"
46 #include "trace.h"
47 #include "utils.h"
48 #include "varbuf.h"
49 
50 #define ARRAY_SIZE(n)	(int)(sizeof(n) / sizeof(n[0]))
51 
52 /* Globals */
53 
54 /* Statics */
55 typedef struct {
56     socket_t sock;			/* socket */
57     const char *hostname;		/* server name */
58     bool negotiate_pending;		/* true if negotiation pending */
59     bool secure_unverified;		/* true if server cert not verified */
60     SSLContextRef context;		/* secure transport context */
61     char *session_info;			/* session information */
62     char *server_cert_info;		/* server cert information */
63 } stransport_sio_t;
64 
65 static tls_config_t *config;
66 static char *interactive_password;
67 
68 #define CIPHER(s)	{ s, #s }
69 typedef struct {
70     int value;
71     const char *name;
72 } cipher_name_t;
73 cipher_name_t cipher_names[] = {
74     CIPHER(SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA),
75     CIPHER(SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA),
76     CIPHER(SSL_DHE_DSS_WITH_DES_CBC_SHA),
77     CIPHER(SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA),
78     CIPHER(SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA),
79     CIPHER(SSL_DHE_RSA_WITH_DES_CBC_SHA),
80     CIPHER(SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA),
81     CIPHER(SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA),
82     CIPHER(SSL_DH_DSS_WITH_DES_CBC_SHA),
83     CIPHER(SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA),
84     CIPHER(SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA),
85     CIPHER(SSL_DH_RSA_WITH_DES_CBC_SHA),
86     CIPHER(SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA),
87     CIPHER(SSL_DH_anon_EXPORT_WITH_RC4_40_MD5),
88     CIPHER(SSL_DH_anon_WITH_3DES_EDE_CBC_SHA),
89     CIPHER(SSL_DH_anon_WITH_DES_CBC_SHA),
90     CIPHER(SSL_DH_anon_WITH_RC4_128_MD5),
91     CIPHER(SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA),
92     CIPHER(SSL_FORTEZZA_DMS_WITH_NULL_SHA),
93     CIPHER(SSL_NO_SUCH_CIPHERSUITE),
94     CIPHER(SSL_NULL_WITH_NULL_NULL),
95     CIPHER(SSL_RSA_EXPORT_WITH_DES40_CBC_SHA),
96     CIPHER(SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5),
97     CIPHER(SSL_RSA_EXPORT_WITH_RC4_40_MD5),
98     CIPHER(SSL_RSA_WITH_3DES_EDE_CBC_MD5),
99     CIPHER(SSL_RSA_WITH_3DES_EDE_CBC_SHA),
100     CIPHER(SSL_RSA_WITH_DES_CBC_MD5),
101     CIPHER(SSL_RSA_WITH_DES_CBC_SHA),
102     CIPHER(SSL_RSA_WITH_IDEA_CBC_MD5),
103     CIPHER(SSL_RSA_WITH_IDEA_CBC_SHA),
104     CIPHER(SSL_RSA_WITH_NULL_MD5),
105     CIPHER(SSL_RSA_WITH_NULL_SHA),
106     CIPHER(SSL_RSA_WITH_RC2_CBC_MD5),
107     CIPHER(SSL_RSA_WITH_RC4_128_MD5),
108     CIPHER(SSL_RSA_WITH_RC4_128_SHA),
109     CIPHER(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA),
110     CIPHER(TLS_DHE_DSS_WITH_AES_128_CBC_SHA),
111     CIPHER(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256),
112     CIPHER(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256),
113     CIPHER(TLS_DHE_DSS_WITH_AES_256_CBC_SHA),
114     CIPHER(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256),
115     CIPHER(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384),
116     CIPHER(TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA),
117     CIPHER(TLS_DHE_PSK_WITH_AES_128_CBC_SHA),
118     CIPHER(TLS_DHE_PSK_WITH_AES_128_CBC_SHA256),
119     CIPHER(TLS_DHE_PSK_WITH_AES_128_GCM_SHA256),
120     CIPHER(TLS_DHE_PSK_WITH_AES_256_CBC_SHA),
121     CIPHER(TLS_DHE_PSK_WITH_AES_256_CBC_SHA384),
122     CIPHER(TLS_DHE_PSK_WITH_AES_256_GCM_SHA384),
123     CIPHER(TLS_DHE_PSK_WITH_NULL_SHA),
124     CIPHER(TLS_DHE_PSK_WITH_NULL_SHA256),
125     CIPHER(TLS_DHE_PSK_WITH_NULL_SHA384),
126     CIPHER(TLS_DHE_PSK_WITH_RC4_128_SHA),
127     CIPHER(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA),
128     CIPHER(TLS_DHE_RSA_WITH_AES_128_CBC_SHA),
129     CIPHER(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256),
130     CIPHER(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256),
131     CIPHER(TLS_DHE_RSA_WITH_AES_256_CBC_SHA),
132     CIPHER(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256),
133     CIPHER(TLS_DHE_RSA_WITH_AES_256_GCM_SHA384),
134     CIPHER(TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA),
135     CIPHER(TLS_DH_DSS_WITH_AES_128_CBC_SHA),
136     CIPHER(TLS_DH_DSS_WITH_AES_128_CBC_SHA256),
137     CIPHER(TLS_DH_DSS_WITH_AES_128_GCM_SHA256),
138     CIPHER(TLS_DH_DSS_WITH_AES_256_CBC_SHA),
139     CIPHER(TLS_DH_DSS_WITH_AES_256_CBC_SHA256),
140     CIPHER(TLS_DH_DSS_WITH_AES_256_GCM_SHA384),
141     CIPHER(TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA),
142     CIPHER(TLS_DH_RSA_WITH_AES_128_CBC_SHA),
143     CIPHER(TLS_DH_RSA_WITH_AES_128_CBC_SHA256),
144     CIPHER(TLS_DH_RSA_WITH_AES_128_GCM_SHA256),
145     CIPHER(TLS_DH_RSA_WITH_AES_256_CBC_SHA),
146     CIPHER(TLS_DH_RSA_WITH_AES_256_CBC_SHA256),
147     CIPHER(TLS_DH_RSA_WITH_AES_256_GCM_SHA384),
148     CIPHER(TLS_DH_anon_WITH_3DES_EDE_CBC_SHA),
149     CIPHER(TLS_DH_anon_WITH_AES_128_CBC_SHA),
150     CIPHER(TLS_DH_anon_WITH_AES_128_CBC_SHA256),
151     CIPHER(TLS_DH_anon_WITH_AES_128_GCM_SHA256),
152     CIPHER(TLS_DH_anon_WITH_AES_256_CBC_SHA),
153     CIPHER(TLS_DH_anon_WITH_AES_256_CBC_SHA256),
154     CIPHER(TLS_DH_anon_WITH_AES_256_GCM_SHA384),
155     CIPHER(TLS_DH_anon_WITH_RC4_128_MD5),
156     CIPHER(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA),
157     CIPHER(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA),
158     CIPHER(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256),
159     CIPHER(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
160     CIPHER(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA),
161     CIPHER(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384),
162     CIPHER(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
163     CIPHER(TLS_ECDHE_ECDSA_WITH_NULL_SHA),
164     CIPHER(TLS_ECDHE_ECDSA_WITH_RC4_128_SHA),
165     CIPHER(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA),
166     CIPHER(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA),
167     CIPHER(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256),
168     CIPHER(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256),
169     CIPHER(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA),
170     CIPHER(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384),
171     CIPHER(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384),
172     CIPHER(TLS_ECDHE_RSA_WITH_NULL_SHA),
173     CIPHER(TLS_ECDHE_RSA_WITH_RC4_128_SHA),
174     CIPHER(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA),
175     CIPHER(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA),
176     CIPHER(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256),
177     CIPHER(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256),
178     CIPHER(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA),
179     CIPHER(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384),
180     CIPHER(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384),
181     CIPHER(TLS_ECDH_ECDSA_WITH_NULL_SHA),
182     CIPHER(TLS_ECDH_ECDSA_WITH_RC4_128_SHA),
183     CIPHER(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA),
184     CIPHER(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA),
185     CIPHER(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256),
186     CIPHER(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256),
187     CIPHER(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA),
188     CIPHER(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384),
189     CIPHER(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384),
190     CIPHER(TLS_ECDH_RSA_WITH_NULL_SHA),
191     CIPHER(TLS_ECDH_RSA_WITH_RC4_128_SHA),
192     CIPHER(TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA),
193     CIPHER(TLS_ECDH_anon_WITH_AES_128_CBC_SHA),
194     CIPHER(TLS_ECDH_anon_WITH_AES_256_CBC_SHA),
195     CIPHER(TLS_ECDH_anon_WITH_NULL_SHA),
196     CIPHER(TLS_ECDH_anon_WITH_RC4_128_SHA),
197     CIPHER(TLS_EMPTY_RENEGOTIATION_INFO_SCSV),
198     CIPHER(TLS_NULL_WITH_NULL_NULL),
199     CIPHER(TLS_PSK_WITH_3DES_EDE_CBC_SHA),
200     CIPHER(TLS_PSK_WITH_AES_128_CBC_SHA),
201     CIPHER(TLS_PSK_WITH_AES_128_CBC_SHA256),
202     CIPHER(TLS_PSK_WITH_AES_128_GCM_SHA256),
203     CIPHER(TLS_PSK_WITH_AES_256_CBC_SHA),
204     CIPHER(TLS_PSK_WITH_AES_256_CBC_SHA384),
205     CIPHER(TLS_PSK_WITH_AES_256_GCM_SHA384),
206     CIPHER(TLS_PSK_WITH_NULL_SHA),
207     CIPHER(TLS_PSK_WITH_NULL_SHA256),
208     CIPHER(TLS_PSK_WITH_NULL_SHA384),
209     CIPHER(TLS_PSK_WITH_RC4_128_SHA),
210     CIPHER(TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA),
211     CIPHER(TLS_RSA_PSK_WITH_AES_128_CBC_SHA),
212     CIPHER(TLS_RSA_PSK_WITH_AES_128_CBC_SHA256),
213     CIPHER(TLS_RSA_PSK_WITH_AES_128_GCM_SHA256),
214     CIPHER(TLS_RSA_PSK_WITH_AES_256_CBC_SHA),
215     CIPHER(TLS_RSA_PSK_WITH_AES_256_CBC_SHA384),
216     CIPHER(TLS_RSA_PSK_WITH_AES_256_GCM_SHA384),
217     CIPHER(TLS_RSA_PSK_WITH_NULL_SHA),
218     CIPHER(TLS_RSA_PSK_WITH_NULL_SHA256),
219     CIPHER(TLS_RSA_PSK_WITH_NULL_SHA384),
220     CIPHER(TLS_RSA_PSK_WITH_RC4_128_SHA),
221     CIPHER(TLS_RSA_WITH_3DES_EDE_CBC_SHA),
222     CIPHER(TLS_RSA_WITH_AES_128_CBC_SHA),
223     CIPHER(TLS_RSA_WITH_AES_128_CBC_SHA256),
224     CIPHER(TLS_RSA_WITH_AES_128_GCM_SHA256),
225     CIPHER(TLS_RSA_WITH_AES_256_CBC_SHA),
226     CIPHER(TLS_RSA_WITH_AES_256_CBC_SHA256),
227     CIPHER(TLS_RSA_WITH_AES_256_GCM_SHA384),
228     CIPHER(TLS_RSA_WITH_NULL_MD5),
229     CIPHER(TLS_RSA_WITH_NULL_SHA),
230     CIPHER(TLS_RSA_WITH_NULL_SHA256),
231     CIPHER(TLS_RSA_WITH_RC4_128_MD5),
232     CIPHER(TLS_RSA_WITH_RC4_128_SHA),
233     { 0, NULL }
234 };
235 
236 /* Record an error from a Secure Transport call. */
237 static void
set_oserror(OSStatus status,const char * fmt,...)238 set_oserror(OSStatus status, const char *fmt, ...)
239 {
240     va_list args;
241     char *t;
242     CFStringRef errmsg;
243 
244     va_start(args, fmt);
245     t = xs_vbuffer(fmt, args);
246     va_end(args);
247 
248     errmsg = SecCopyErrorMessageString(status, NULL);
249     if (errmsg != NULL) {
250 	sioc_set_error("%s: %s", t,
251 		CFStringGetCStringPtr(errmsg, kCFStringEncodingASCII));
252 	CFRelease(errmsg);
253     } else {
254 	sioc_set_error("%s: Error %d", t, (int)status);
255     }
256     Free(t);
257 }
258 
259 /* Read function called by Secure Transport. */
260 OSStatus
read_func(SSLConnectionRef connection,void * data,size_t * data_length)261 read_func(SSLConnectionRef connection, void *data, size_t *data_length)
262 {
263     stransport_sio_t *s = (stransport_sio_t *)connection;
264     int nr;
265     size_t n_read = 0;
266 
267     if (s->sock == INVALID_SOCKET) {
268 	*data_length = 0;
269 	return errSecIO;
270     }
271 
272     /*
273      * They want us to return all of the data, or errSSLWouldBlock.
274      */
275     while (n_read < *data_length) {
276 	nr = recv(s->sock, (char *)data + n_read, *data_length - n_read, 0);
277 	vtrace("TLS: read %d/%d bytes\n", nr, (int)(*data_length - n_read));
278 	if (nr < 0) {
279 	    if (errno == EWOULDBLOCK) {
280 		*data_length = n_read;
281 		return errSSLWouldBlock;
282 	    }
283 	    vtrace("TLS recv: %s\n", strerror(errno));
284 	    *data_length = n_read;
285 	    return errSecIO;
286 	} else if (nr == 0) {
287 	    *data_length = n_read;
288 	    return errSSLClosedGraceful;
289 	}
290 	n_read += nr;
291     }
292 
293     *data_length = n_read;
294     return errSecSuccess;
295 }
296 
297 /* Write function called by Secure Transport. */
298 OSStatus
write_func(SSLConnectionRef connection,const void * data,size_t * data_length)299 write_func(SSLConnectionRef connection, const void *data, size_t *data_length)
300 {
301     stransport_sio_t *s = (stransport_sio_t *)connection;
302     int nw;
303 
304     if (s->sock == INVALID_SOCKET) {
305 	*data_length = 0;
306 	return errSecIO;
307     }
308 
309     nw = send(s->sock, data, *data_length, 0);
310     vtrace("TLS: wrote %d/%d bytes\n", nw, (int)*data_length);
311     if (nw < 0) {
312 	vtrace("TLS send: %s\n", strerror(errno));
313 	*data_length = 0;
314 	return errSecIO;
315     } else {
316 	*data_length = nw;
317 	return errSecSuccess;
318     }
319 }
320 
321 /* Get the subject or issuer name details from a cert. */
322 static char *
name_details(CFArrayRef array)323 name_details(CFArrayRef array)
324 {
325     const void *keys[] = {
326 	kSecOIDCommonName,
327 	kSecOIDEmailAddress,
328 	kSecOIDOrganizationalUnitName,
329 	kSecOIDOrganizationName,
330 	kSecOIDLocalityName,
331 	kSecOIDStateProvinceName,
332 	kSecOIDCountryName
333     };
334     static const char *labels[] = { "CN", "E", "OU", "O", "L", "S", "C", "E" };
335     varbuf_t v;
336     char *comma = "";
337 
338     vb_init(&v);
339 
340     for (int i = 0; i < ARRAY_SIZE(keys);  i++) {
341 	CFIndex n;
342 
343 	for (n = 0 ; n < CFArrayGetCount(array); n++) {
344 	    CFDictionaryRef dict;
345 	    CFTypeRef dictkey;
346 	    CFStringRef str;
347 	    char buf[1024];
348 
349 	    dict = CFArrayGetValueAtIndex(array, n);
350 	    if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) {
351 		continue;
352 	    }
353 	    dictkey = CFDictionaryGetValue(dict, kSecPropertyKeyLabel);
354 	    if (!CFEqual(dictkey, keys[i])) {
355 		continue;
356 	    }
357 	    str = (CFStringRef)CFDictionaryGetValue(dict, kSecPropertyKeyValue);
358 	    if (CFStringGetCString(str, buf, sizeof(buf),
359 			kCFStringEncodingUTF8)) {
360 		vb_appendf(&v, "%s%s=%s", comma, labels[i], buf);
361 		comma = ", ";
362 	    }
363 	}
364     }
365     return vb_consume(&v);
366 }
367 
368 /* Get the alternate names from a cert. */
369 static char *
alt_names(CFArrayRef array)370 alt_names(CFArrayRef array)
371 {
372     const void *keys[] = {
373 	CFSTR("DNS Name")	/* XXX: There must be a constant for this */
374     };
375     varbuf_t v;
376     char *comma = "";
377     int i;
378 
379     vb_init(&v);
380 
381     for (i = 0; i < ARRAY_SIZE(keys);  i++) {
382 	CFIndex n;
383 
384 	for (n = 0 ; n < CFArrayGetCount(array); n++) {
385 	    CFDictionaryRef dict;
386 	    CFTypeRef dictkey;
387 	    CFStringRef str;
388 	    char buf[1024];
389 
390 	    dict = CFArrayGetValueAtIndex(array, n);
391 	    if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) {
392 		continue;
393 	    }
394 	    dictkey = CFDictionaryGetValue(dict, kSecPropertyKeyLabel);
395 	    if (!CFEqual(dictkey, keys[i])) {
396 		continue;
397 	    }
398 	    str = (CFStringRef)CFDictionaryGetValue(dict, kSecPropertyKeyValue);
399 	    if (CFStringGetCString(str, buf, sizeof(buf),
400 			kCFStringEncodingUTF8)) {
401 		vb_appendf(&v, "%s%s", comma, buf);
402 		comma = ", ";
403 	    }
404 	}
405     }
406     return vb_consume(&v);
407 }
408 
409 /* Get details from a cert. */
410 static char *
cert_details(const char * prefix,SecCertificateRef certificateRef)411 cert_details(const char *prefix, SecCertificateRef certificateRef)
412 {
413     CFErrorRef error;
414     const void *keys[] = {
415 	kSecOIDX509V1SubjectName,
416 	kSecOIDX509V1IssuerName,
417 	kSecOIDSubjectAltName
418     };
419     static const void *labels[] = {
420 	"Subject", "Issuer", "Subject alternate names"
421     };
422     static char *(*decoders[])(CFArrayRef) = {
423 	name_details,
424 	name_details,
425 	alt_names
426     };
427     CFArrayRef keySelection = CFArrayCreate(NULL, keys, ARRAY_SIZE(keys),
428 	    &kCFTypeArrayCallBacks);
429     CFDictionaryRef vals = SecCertificateCopyValues(certificateRef,
430 	    keySelection, &error);
431     varbuf_t v;
432     int i;
433 
434     vb_init(&v);
435 
436     /* So I can see the OIDs and figure out which one is the alt name. */
437     for (i = 0; i < ARRAY_SIZE(keys); i++) {
438 	CFDictionaryRef dict;
439 	CFArrayRef values;
440 	char *s;
441 
442 	dict = CFDictionaryGetValue(vals, keys[i]);
443 	if (dict == NULL) {
444 	    continue;
445 	}
446 	values = CFDictionaryGetValue(dict, kSecPropertyKeyValue);
447 	if (values == NULL) {
448 	    continue;
449 	}
450 	s = decoders[i](values);
451 	vb_appendf(&v, "%s%s: %s\n", prefix, labels[i], s);
452 	Free(s);
453     }
454 
455     CFRelease(vals);
456 
457     return vb_consume(&v);
458 }
459 
460 /* Display certificate information. */
461 static void
display_cert(varbuf_t * v,const char * prefix,SecCertificateRef cert)462 display_cert(varbuf_t *v, const char *prefix, SecCertificateRef cert)
463 {
464 #if defined(LONG_DESC_IS_USEFUL) /*[*/
465     CFStringRef desc = SecCertificateCopyLongDescription(NULL, cert, NULL);
466 
467     if (desc != NULL) {
468 	char text[1024];
469 
470 	memset(text, 0, sizeof(text));
471 	if (CFStringGetCString(desc, text, sizeof(text),
472 		    kCFStringEncodingUTF8)) {
473 	    vb_appendf(v, "%s cert: %s\n", prefix, text);
474 	}
475 	CFRelease(desc);
476     }
477 #endif /*]*/
478 
479     char *s = cert_details(prefix, cert);
480     vb_appends(v, s);
481     Free(s);
482 }
483 
484 /* Convert a cipher to its name. */
485 const char *
cipher_name(int n)486 cipher_name(int n)
487 {
488     int i;
489     struct {
490 	const char *orig;
491 	const char *subst;
492     } substs[] = {
493 	{ "_", " " },
494 	{ "WITH", "with" },
495 	{ "NULL", "null" },
496 	{ "FORTEZZA", "Fortezza" },
497 	{ NULL, NULL }
498     };
499 
500     for (i = 0; cipher_names[i].name != NULL; i++) {
501 	if (cipher_names[i].value == n) {
502 	    char *s = lazyaf("%s", cipher_names[i].name);
503 	    int j;
504 
505 	    for (j = 0; substs[j].orig != NULL; j++) {
506 		char *t;
507 
508 		while ((t = strstr(s, substs[j].orig)) != NULL) {
509 		    strncpy(t, substs[j].subst, strlen(substs[j].subst));
510 		}
511 	    }
512 	    return s;
513 	}
514     }
515 
516     return lazyaf("0x%x\n", n);
517 }
518 
519 /* Display connection info. */
520 static void
display_connection_info(varbuf_t * v,stransport_sio_t * s)521 display_connection_info(varbuf_t *v, stransport_sio_t *s)
522 {
523     OSStatus status;
524     SSLProtocol protocol;
525     SSLCipherSuite cipher_suite;
526 
527     status = SSLGetNegotiatedProtocolVersion(s->context, &protocol);
528     if (status == errSecSuccess) {
529 	vb_appendf(v, "Protocol version: ");
530 	switch (protocol) {
531 	case kSSLProtocol2:
532 	    vb_appendf(v, "SSL 2");
533 	    break;
534 	case kSSLProtocol3:
535 	    vb_appendf(v, "SSL 3");
536 	    break;
537 	case kTLSProtocol1:
538 	    vb_appendf(v, "TLS 1.0");
539 	    break;
540 	case kTLSProtocol11:
541 	    vb_appendf(v, "TLS 1.1");
542 	    break;
543 	case kTLSProtocol12:
544 	    vb_appendf(v, "TLS 1.2");
545 	    break;
546 	default:
547 	    vb_appendf(v, "0x%x", (unsigned)protocol);
548 	    break;
549 	}
550 	vb_appendf(v, "\n");
551     }
552 
553     status = SSLGetNegotiatedCipher(s->context, &cipher_suite);
554     if (status == errSecSuccess) {
555 	vb_appendf(v, "Cipher: %s\n", cipher_name(cipher_suite));
556     }
557 }
558 
559 /* Display server cert info. */
560 static void
display_server_cert(varbuf_t * v,stransport_sio_t * s)561 display_server_cert(varbuf_t *v, stransport_sio_t *s)
562 {
563     OSStatus status;
564     SecTrustRef trust = NULL;
565 
566     status = SSLCopyPeerTrust(s->context, &trust);
567     if (status == errSecSuccess && trust != NULL) {
568 	CFIndex count = SecTrustGetCertificateCount(trust);
569 	CFIndex i;
570 
571 	for (i = 0L ; i < count ; i++) {
572 	    char *prefix = "";
573 
574 	    if (i) {
575 		prefix = lazyaf("CA %d ", i);
576 	    }
577 	    display_cert(v, prefix, SecTrustGetCertificateAtIndex(trust, i));
578 	}
579 	CFRelease(trust);
580     }
581 }
582 
583 /* Create a CFDataRef from the contents of a file. */
584 static CFDataRef
dataref_from_file(const char * path)585 dataref_from_file(const char *path)
586 {
587     char *accum = NULL;
588     size_t n_accum = 0;
589     CFDataRef dataref;
590 
591     accum = sioc_string_from_file(path, &n_accum);
592     if (accum == NULL) {
593 	return NULL;
594     }
595 
596     dataref = CFDataCreate(NULL, (UInt8 *)accum, n_accum);
597     Free(accum);
598     return dataref;
599 }
600 
601 /* Copy the identity from a file. */
602 static OSStatus
identity_from_file(const char * path,const char * password,SecIdentityRef * identity_ret)603 identity_from_file(const char *path, const char *password,
604 	SecIdentityRef *identity_ret)
605 {
606     CFDataRef pkcs_data = dataref_from_file(path);
607 
608     if (pkcs_data == NULL) {
609 	return errSecItemNotFound;
610     } else {
611 	CFStringRef pass_string = (password != NULL)?
612 	    CFStringCreateWithCString(NULL, password, kCFStringEncodingUTF8) :
613 	    NULL;
614 	const void *keys[] = { kSecImportExportPassphrase };
615 	const void *values[] = { pass_string };
616 	CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values,
617 		(pass_string != NULL)? 1L: 0L, NULL, NULL);
618 	CFArrayRef items = NULL;
619 	OSStatus status = SecPKCS12Import(pkcs_data, options, &items);
620 
621 	if (status == errSecSuccess && items != NULL &&
622 		CFArrayGetCount(items)) {
623 	    CFDictionaryRef identity_and_trust = CFArrayGetValueAtIndex(items,
624 		    0L);
625 	    const void *identity = CFDictionaryGetValue(identity_and_trust,
626 		    kSecImportItemIdentity);
627 
628 	    /* We only need the identity. */
629 	    CFRetain(identity);
630 	    *identity_ret = (SecIdentityRef)identity;
631 	}
632 
633 	if (items != NULL) {
634 	    CFRelease(items);
635 	}
636 	CFRelease(options);
637 	CFRelease(pkcs_data);
638 	if (pass_string != NULL) {
639 	    CFRelease(pass_string);
640 	}
641 	return status;
642     }
643 }
644 
645 /*
646  * Get an identity from a certificate in the keychain, based on the common
647  * name.
648  */
649 static OSStatus
identity_from_keychain(char * name,SecIdentityRef * identity_ret)650 identity_from_keychain(char *name, SecIdentityRef *identity_ret)
651 {
652 #   define KEY_ENTRIES 4
653     OSStatus status;
654     CFTypeRef keys[KEY_ENTRIES];
655     CFTypeRef values[KEY_ENTRIES];
656     CFDictionaryRef query_dict;
657     CFArrayRef ids;
658 
659     /* Assume we will return nothing. */
660     *identity_ret = NULL;
661 
662     /*
663      * Set up search criteria.
664      * The Apple docs imply that you can search for a match against the
665      * common name, e.g. kSecMatchSubjectWholeString. It doesn't appear to
666      * work; you get back all certificates.  So the result needs to be searched
667      * manually for a common name match.
668      */
669     keys[0] = kSecClass;
670     values[0] = kSecClassIdentity; 	/* want identity (cert and key) */
671     keys[1] = kSecReturnRef;
672     values[1] = kCFBooleanTrue;    	/* want a reference */
673     keys[2] = kSecMatchLimit;
674     values[2] = kSecMatchLimitAll;	/* all of them */
675     keys[3] = kSecMatchPolicy;
676     values[3] = SecPolicyCreateSSL(false, NULL); /* just SSL certs */
677     query_dict = CFDictionaryCreate(NULL, (const void **)keys,
678 	    (const void **)values, KEY_ENTRIES,
679 	    &kCFCopyStringDictionaryKeyCallBacks,
680 	    &kCFTypeDictionaryValueCallBacks);
681     CFRelease(values[3]); /* the policy */
682 										    /* Search for a common name match. */
683     status = SecItemCopyMatching(query_dict, (CFTypeRef *)&ids);
684     CFRelease(query_dict);
685 
686     if (status == errSecSuccess) {
687 	CFIndex count = CFArrayGetCount(ids);
688 	CFIndex i;
689 	bool matched = false;
690 
691 	/* TODO: Could do a case-independent match, or a substring match. */
692 	vtrace("identity_from_keychain: Got %d match%s\n", (int)count,
693 		((int)count == 1)? "": "es");
694 	for (i = 0; i < count && !matched; i++) {
695 	    SecIdentityRef identity =
696 		(SecIdentityRef)CFArrayGetValueAtIndex(ids, i);
697 	    SecCertificateRef cert = NULL;
698 
699 	    if (SecIdentityCopyCertificate(identity, &cert) == errSecSuccess) {
700 		CFStringRef cf_common_name;
701 		char common_name[1024];
702 
703 		if (SecCertificateCopyCommonName(cert, &cf_common_name) ==
704 			errSecSuccess) {
705 		    if (CFStringGetCString(cf_common_name, common_name,
706 				sizeof(common_name), kCFStringEncodingUTF8)
707 			    && !strcmp(name, common_name)) {
708 			CFRetain(identity);
709 			*identity_ret = identity;
710 			matched = true;
711 		    }
712 		    CFRelease(cf_common_name);
713 		}
714 		CFRelease(cert);
715 	    }
716 	}
717 	CFRelease(ids);
718 	return matched? errSecSuccess: errSecItemNotFound;
719     }
720 
721     return status;
722 }
723 
724 /* Set up the client certificate. */
725 static sio_init_ret_t
set_client_cert(stransport_sio_t * s)726 set_client_cert(stransport_sio_t *s)
727 {
728     OSStatus status;
729     SecIdentityRef identity = NULL;
730     char *cert_name;
731 
732     if (config->cert_file != NULL) {
733 	char *password = NULL;
734 	bool need_free = false;
735 
736 	if (interactive_password != NULL) {
737 	    password = interactive_password;
738 	} else if (config->key_passwd != NULL) {
739 	    password = sioc_parse_password_spec(config->key_passwd);
740 	    if (password == NULL) {
741 		return SI_FAILURE;
742 	    }
743 	    need_free = true;
744 	}
745 
746 	cert_name = config->cert_file;
747 	status = identity_from_file(cert_name, password,
748 		&identity);
749 	if (need_free) {
750 	    Free(password);
751 	}
752     } else if (config->client_cert != NULL) {
753 	cert_name = config->client_cert;
754 	status = identity_from_keychain(cert_name, &identity);
755     } else {
756 	/* No client cert. */
757 	return SI_SUCCESS;
758     }
759 
760     if (status == errSecSuccess && identity != NULL) {
761 	SecCertificateRef cert = NULL;
762 	CFTypeRef certs_array[1];
763 	CFArrayRef certs;
764 
765 	/* Found it. */
766 	status = SecIdentityCopyCertificate(identity, &cert);
767 	if (status == errSecSuccess) {
768 	    varbuf_t v;
769 
770 	    vb_init(&v);
771 	    display_cert(&v, "Client", cert);
772 	    vtrace("%s", vb_buf(&v));
773 	    vb_free(&v);
774 	    CFRelease(cert);
775 	}
776 
777 	/* Set it. */
778 	certs_array[0] = identity;
779 	certs = CFArrayCreate(NULL, (const void **)certs_array, 1L,
780 		&kCFTypeArrayCallBacks);
781 	status = SSLSetCertificate(s->context, certs);
782 	if (certs != NULL) {
783 	    CFRelease(certs);
784 	}
785 
786 	if (status != errSecSuccess) {
787 	    set_oserror(status, "SSLSetCertificate");
788 	    return SI_FAILURE;
789 	}
790 	CFRelease(identity);
791 	return SI_SUCCESS;
792     }
793 
794     /* Failure. */
795     switch (status) {
796     case errSecAuthFailed:
797     case errSecPkcs12VerifyFailure:
798 	sioc_set_error("Incorrect password for certificate \"%s\"", cert_name);
799 	return SI_WRONG_PASSWORD;
800     case errSecDecode:
801     case errSecUnknownFormat:
802 	sioc_set_error("Can't parse certificate certificate \"%s\"", cert_name);
803 	return SI_FAILURE;
804     case errSecPassphraseRequired:
805 	sioc_set_error("Certificate \"%s\" requires a password", cert_name);
806 	return SI_NEED_PASSWORD;
807     case errSecItemNotFound:
808 	sioc_set_error("Can't find certificate \"%s\"", cert_name);
809 	return SI_FAILURE;
810     default:
811 	set_oserror(status, "Can't load certificate \"%s\"", cert_name);
812 	return SI_FAILURE;
813     }
814 }
815 
816 /* Free a TLS context. */
817 static void
sio_free(stransport_sio_t * s)818 sio_free(stransport_sio_t *s)
819 {
820     s->sock = INVALID_SOCKET;
821     SSLClose(s->context);
822     CFRelease(s->context);
823     s->context = NULL;
824     if (s->session_info != NULL) {
825 	Free(s->session_info);
826 	s->session_info = NULL;
827     }
828     if (s->server_cert_info != NULL) {
829 	Free(s->server_cert_info);
830 	s->server_cert_info = NULL;
831     }
832     Free(s);
833 }
834 
835 /* Returns true if secure I/O is supported. */
836 bool
sio_supported(void)837 sio_supported(void)
838 {
839     return true;
840 }
841 
842 /*
843  * Create a new connection.
844  */
845 sio_init_ret_t
sio_init(tls_config_t * c,const char * password,sio_t * sio_ret)846 sio_init(tls_config_t *c, const char *password, sio_t *sio_ret)
847 {
848     stransport_sio_t *s;
849     OSStatus status;
850     sio_init_ret_t ret = SI_SUCCESS;
851 
852     sioc_error_reset();
853 
854     *sio_ret = NULL;
855 
856     config = c;
857     s = (stransport_sio_t *)Malloc(sizeof(stransport_sio_t));
858     memset(s, 0, sizeof(*s));
859 
860     s->sock = INVALID_SOCKET;
861     s->context = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide,
862 	    kSSLStreamType);
863     if (password != NULL) {
864 	Replace(interactive_password, NewString(password));
865     }
866     status = SSLSetIOFuncs(s->context, read_func, write_func);
867     if (status != errSecSuccess) {
868 	set_oserror(status, "SSLSetIOFuncs");
869 	goto fail;
870     }
871 
872     status = SSLSetConnection(s->context, s);
873     if (status != errSecSuccess) {
874 	set_oserror(status, "SSLSetConnection");
875 	goto fail;
876     }
877 
878     if (!config->verify_host_cert) {
879 	status = SSLSetSessionOption(s->context,
880 		kSSLSessionOptionBreakOnServerAuth, true);
881 	if (status != errSecSuccess) {
882 	    set_oserror(status, "SSLSetSessionOption");
883 	    goto fail;
884 	}
885     }
886 
887     /* Set the client certificate, which could require a password. */
888     ret = set_client_cert(s);
889     if (ret == SI_SUCCESS) {
890 	*sio_ret = (sio_t)s;
891 	return ret;
892     }
893 
894 fail:
895     sio_free(s);
896     *sio_ret = NULL;
897     return ret;
898 }
899 
900 /*
901  * Negotiate a TLS connection.
902  * Returns true for success, false for failure.
903  * If it returns false, the socket should be disconnected.
904  *
905  * Returns 'data' true if there is already protocol data pending.
906  */
907 sio_negotiate_ret_t
sio_negotiate(sio_t sio,socket_t sock,const char * hostname,bool * data)908 sio_negotiate(sio_t sio, socket_t sock, const char *hostname, bool *data)
909 {
910     stransport_sio_t *s;
911     const char *accept_hostname = hostname;
912     OSStatus status;
913     varbuf_t v;
914     size_t sl;
915 
916     sioc_error_reset();
917 
918     *data = false;
919     if (sio == NULL) {
920 	sioc_set_error("NULL sio");
921 	return SIG_FAILURE;
922     }
923     s = (stransport_sio_t *)sio;
924     if (s->negotiate_pending) {
925 	if (s->sock == INVALID_SOCKET) {
926 	    sioc_set_error("Invalid sio");
927 	    return SIG_FAILURE;
928 	}
929     } else {
930 	if (s->sock != INVALID_SOCKET) {
931 	    sioc_set_error("Invalid sio");
932 	    return SIG_FAILURE;
933 	}
934 
935 	s->sock = sock;
936 	s->hostname = hostname;
937 
938 	if (config->accept_hostname != NULL) {
939 	    if (!strncasecmp(accept_hostname, "DNS:", 4)) {
940 		accept_hostname = config->accept_hostname + 4;
941 		sioc_set_error("Empty acceptHostname");
942 		goto fail;
943 	    } else if (!strncasecmp(config->accept_hostname, "IP:", 3)) {
944 		sioc_set_error("Cannot use 'IP:' acceptHostname");
945 		goto fail;
946 	    } else if (!strcasecmp(config->accept_hostname, "any")) {
947 		sioc_set_error("Cannot use 'any' acceptHostname");
948 		goto fail;
949 	    } else {
950 		accept_hostname = config->accept_hostname;
951 	    }
952 	}
953 
954 	status = SSLSetPeerDomainName(s->context, accept_hostname,
955 		strlen(accept_hostname));
956 	if (status != errSecSuccess) {
957 	    set_oserror(status, "SSLSetPeerDomainName");
958 	    goto fail;
959 	}
960     }
961 
962     /* Perform handshake. */
963     status = SSLHandshake(s->context);
964     if (status == errSSLWouldBlock) {
965 	s->negotiate_pending = true;
966 	return SIG_WANTMORE;
967     }
968     if (status != errSecSuccess && status != errSSLServerAuthCompleted) {
969 	set_oserror(status, "SSLHandshake");
970 	goto fail;
971     }
972     if (status == errSSLServerAuthCompleted) {
973 	/* Do it again, to complete the handshake. */
974 	status = SSLHandshake(s->context);
975 	if (status == errSSLWouldBlock) {
976 	    s->negotiate_pending = true;
977 	    return SIG_WANTMORE;
978 	}
979 	if (status != errSecSuccess) {
980 	    set_oserror(status, "SSLHandshake");
981 	    goto fail;
982 	}
983     }
984 
985     /* Display connection info. */
986     vb_init(&v);
987     display_connection_info(&v, s);
988     s->session_info = vb_consume(&v);
989     sl = strlen(s->session_info);
990     if (sl > 0 && s->session_info[sl - 1] == '\n') {
991 	s->session_info[sl - 1] = '\0';
992     }
993 
994     /* Display server cert info. */
995     vb_init(&v);
996     display_server_cert(&v, s);
997     s->server_cert_info = vb_consume(&v);
998     sl = strlen(s->server_cert_info);
999     if (sl > 0 && s->server_cert_info[sl - 1] == '\n') {
1000 	s->server_cert_info[sl - 1] = '\0';
1001     }
1002 
1003     /* Success. */
1004     s->secure_unverified = !config->verify_host_cert;
1005     return SIG_SUCCESS;
1006 
1007 fail:
1008     return SIG_FAILURE;
1009 }
1010 
1011 /*
1012  * Read encrypted data from a socket.
1013  * Returns the data length, SIO_EOF for EOF, SIO_FATAL_ERROR for a fatal error,
1014  * SIO_EWOULDBLOCK for incomplete input.
1015  */
1016 int
sio_read(sio_t sio,char * buf,size_t buflen)1017 sio_read(sio_t sio, char *buf, size_t buflen)
1018 {
1019     stransport_sio_t *s;
1020     OSStatus status;
1021     size_t n_read = 0;
1022 
1023     sioc_error_reset();
1024 
1025     if (sio == NULL) {
1026 	sioc_set_error("NULL sio");
1027 	return SIO_FATAL_ERROR;
1028     }
1029     s = (stransport_sio_t *)sio;
1030     if (s->sock == INVALID_SOCKET) {
1031 	sioc_set_error("Invalid sio");
1032 	return SIO_FATAL_ERROR;
1033     }
1034 
1035     status = SSLRead(s->context, buf, buflen, &n_read);
1036     if (status == errSSLClosedGraceful || status == errSSLClosedNoNotify) {
1037 	vtrace("TLS: EOF\n");
1038 	return 0;
1039     }
1040     if (status == errSSLWouldBlock) {
1041 	vtrace("TLS: EWOULDBLOCK\n");
1042 	return SIO_EWOULDBLOCK;
1043     }
1044     if (status != errSecSuccess) {
1045 	set_oserror(status, "SSLRead %d", status);
1046 	return SIO_FATAL_ERROR;
1047     }
1048 
1049     return (int)n_read;
1050 }
1051 
1052 /*
1053  * Write encrypted data on the socket.
1054  * Returns the data length or SIO_FATAL_ERROR.
1055  */
1056 int
sio_write(sio_t sio,const char * buf,size_t buflen)1057 sio_write(sio_t sio, const char *buf, size_t buflen)
1058 {
1059     stransport_sio_t *s;
1060     OSStatus status;
1061     size_t n_written = 0;
1062 
1063     sioc_error_reset();
1064 
1065     if (sio == NULL) {
1066 	sioc_set_error("NULL sio");
1067 	return SIO_FATAL_ERROR;
1068     }
1069     s = (stransport_sio_t *)sio;
1070     if (s->sock == INVALID_SOCKET) {
1071 	sioc_set_error("Invalid sio");
1072 	return SIO_FATAL_ERROR;
1073     }
1074 
1075     status = SSLWrite(s->context, buf, buflen, &n_written);
1076     if (status != errSecSuccess) {
1077 	set_oserror(status, "SSLWrite");
1078 	return SIO_FATAL_ERROR;
1079     }
1080 
1081     return (int)buflen;
1082 }
1083 
1084 /* Closes the TLS connection. */
1085 void
sio_close(sio_t sio)1086 sio_close(sio_t sio)
1087 {
1088     stransport_sio_t *s;
1089 
1090     if (sio == NULL) {
1091 	return;
1092     }
1093     s = (stransport_sio_t *)sio;
1094     if (s->sock == INVALID_SOCKET) {
1095 	return;
1096     }
1097 
1098     sio_free(s);
1099 }
1100 
1101 /*
1102  * Returns true if the current connection is unverified.
1103  */
1104 bool
sio_secure_unverified(sio_t sio)1105 sio_secure_unverified(sio_t sio)
1106 {
1107     stransport_sio_t *s = (stransport_sio_t *)sio;
1108     return s? s->secure_unverified: false;
1109 }
1110 
1111 /*
1112  * Returns a bitmap of the supported options.
1113  */
1114 unsigned
sio_options_supported(void)1115 sio_options_supported(void)
1116 {
1117     return TLS_OPT_CERT_FILE | TLS_OPT_CLIENT_CERT | TLS_OPT_KEY_PASSWD;
1118 }
1119 
1120 const char *
sio_session_info(sio_t sio)1121 sio_session_info(sio_t sio)
1122 {
1123     stransport_sio_t *s = (stransport_sio_t *)sio;
1124     return (s != NULL)? s->session_info: NULL;
1125 }
1126 
1127 const char *
sio_server_cert_info(sio_t sio)1128 sio_server_cert_info(sio_t sio)
1129 {
1130     stransport_sio_t *s = (stransport_sio_t *)sio;
1131     return (s != NULL)? s->server_cert_info: NULL;
1132 }
1133 
1134 const char *
sio_provider(void)1135 sio_provider(void)
1136 {
1137     return "Apple Secure Transport";
1138 }
1139