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