1/*
2 * Copyright (c) 2002-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * tlsutil.c
26 * - utility functions for dealing with Secure Transport API's
27 */
28
29/*
30 * Modification History
31 *
32 * August 26, 2002	Dieter Siegmund (dieter@apple)
33 * - created
34 */
35
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <time.h>
40#include <ctype.h>
41
42#include <Security/SecureTransport.h>
43#if !TARGET_OS_EMBEDDED
44#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
45#endif
46#include <CoreFoundation/CFArray.h>
47#include <CoreFoundation/CFBase.h>
48#include <CoreFoundation/CFData.h>
49#include <Security/SecureTransportPriv.h>
50#include <TargetConditionals.h>
51#if TARGET_OS_EMBEDDED
52#include <Security/SecCertificatePriv.h>
53#include <Security/SecPolicyPriv.h>
54#else /* TARGET_OS_EMBEDDED */
55#include <Security/oidsalg.h>
56#include <Security/SecKeychain.h>
57#include <Security/SecPolicySearch.h>
58#endif /* TARGET_OS_EMBEDDED */
59#include <Security/SecTrustPriv.h>
60#include <SystemConfiguration/SCValidation.h>
61#include <Security/SecPolicy.h>
62#include "EAPUtil.h"
63#include "EAPClientProperties.h"
64#include "EAPCertificateUtil.h"
65#include "EAPTLSUtil.h"
66#include "EAPSIMAKAPersistentState.h"
67#include "EAPSecurity.h"
68#include "printdata.h"
69#include "myCFUtil.h"
70#include "nbo.h"
71#include "EAPLog.h"
72
73#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
74#if __IPHONE_OS_VERSION_MIN_REQUIRED < 70000
75#define NEED_TO_DISABLE_ONE_BYTE_OPTION		0
76#endif /* __IPHONE_OS_VERSION_MIN_REQUIRED < 70000 */
77#endif /* __IPHONE_OS_VERSION_MIN_REQUIRED */
78
79#ifndef NEED_TO_DISABLE_ONE_BYTE_OPTION
80#define NEED_TO_DISABLE_ONE_BYTE_OPTION		1
81#endif /* NEED_TO_DISABLE_ONE_BYTE_OPTION */
82
83/* set a 12-hour session cache timeout */
84#define kEAPTLSSessionCacheTimeoutSeconds	(12 * 60 * 60)
85
86uint32_t
87EAPTLSLengthIncludedPacketGetMessageLength(EAPTLSLengthIncludedPacketRef pkt)
88{
89    return (net_uint32_get(pkt->tls_message_length));
90}
91
92void
93EAPTLSLengthIncludedPacketSetMessageLength(EAPTLSLengthIncludedPacketRef pkt,
94                                           uint32_t length)
95{
96    return (net_uint32_set(pkt->tls_message_length, length));
97}
98
99#ifdef NOTYET
100/*
101 * Lists of SSLCipherSuites used in setCipherRestrictions. Note that the
102 * SecureTransport library does not implement all of these; we only specify
103 * the ones it claims to support.
104 */
105static SSLCipherSuite suites40[] = {
106    SSL_RSA_EXPORT_WITH_RC4_40_MD5,
107    SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5,
108    SSL_RSA_EXPORT_WITH_DES40_CBC_SHA,
109    SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA,
110    SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA,
111    SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,
112    SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
113    SSL_DH_anon_EXPORT_WITH_RC4_40_MD5,
114    SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA,
115    SSL_NO_SUCH_CIPHERSUITE
116};
117static SSLCipherSuite suitesDES[] = {
118    SSL_RSA_WITH_DES_CBC_SHA,
119    SSL_DH_DSS_WITH_DES_CBC_SHA,
120    SSL_DH_RSA_WITH_DES_CBC_SHA,
121    SSL_DHE_DSS_WITH_DES_CBC_SHA,
122    SSL_DHE_RSA_WITH_DES_CBC_SHA,
123    SSL_DH_anon_WITH_DES_CBC_SHA,
124    SSL_RSA_WITH_DES_CBC_MD5,
125    SSL_NO_SUCH_CIPHERSUITE
126};
127static SSLCipherSuite suitesDES40[] = {
128    SSL_RSA_EXPORT_WITH_DES40_CBC_SHA,
129    SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA,
130    SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA,
131    SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,
132    SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
133    SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA,
134    SSL_NO_SUCH_CIPHERSUITE
135};
136static SSLCipherSuite suites3DES[] = {
137    SSL_RSA_WITH_3DES_EDE_CBC_SHA,
138    SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA,
139    SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA,
140    SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
141    SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
142    SSL_DH_anon_WITH_3DES_EDE_CBC_SHA,
143    SSL_RSA_WITH_3DES_EDE_CBC_MD5,
144    SSL_NO_SUCH_CIPHERSUITE
145};
146static SSLCipherSuite suitesRC4[] = {
147    SSL_RSA_WITH_RC4_128_MD5,
148    SSL_RSA_WITH_RC4_128_SHA,
149    SSL_DH_anon_WITH_RC4_128_MD5,
150    SSL_NO_SUCH_CIPHERSUITE
151};
152static SSLCipherSuite suitesRC4_40[] = {
153    SSL_RSA_EXPORT_WITH_RC4_40_MD5,
154    SSL_DH_anon_EXPORT_WITH_RC4_40_MD5,
155    SSL_NO_SUCH_CIPHERSUITE
156};
157static SSLCipherSuite suitesRC2[] = {
158    SSL_RSA_WITH_RC2_CBC_MD5,
159    SSL_NO_SUCH_CIPHERSUITE
160};
161
162const char *
163EAPSSLCipherSuiteString(SSLCipherSuite cs)
164{
165    switch(cs) {
166    case SSL_NULL_WITH_NULL_NULL:
167	return "SSL_NULL_WITH_NULL_NULL";
168    case SSL_RSA_WITH_NULL_MD5:
169	return "SSL_RSA_WITH_NULL_MD5";
170    case SSL_RSA_WITH_NULL_SHA:
171	return "SSL_RSA_WITH_NULL_SHA";
172    case SSL_RSA_EXPORT_WITH_RC4_40_MD5:
173	return "SSL_RSA_EXPORT_WITH_RC4_40_MD5";
174    case SSL_RSA_WITH_RC4_128_MD5:
175	return "SSL_RSA_WITH_RC4_128_MD5";
176    case SSL_RSA_WITH_RC4_128_SHA:
177	return "SSL_RSA_WITH_RC4_128_SHA";
178    case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5:
179	return "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5";
180    case SSL_RSA_WITH_IDEA_CBC_SHA:
181	return "SSL_RSA_WITH_IDEA_CBC_SHA";
182    case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA:
183	return "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA";
184    case SSL_RSA_WITH_DES_CBC_SHA:
185	return "SSL_RSA_WITH_DES_CBC_SHA";
186    case SSL_RSA_WITH_3DES_EDE_CBC_SHA:
187	return "SSL_RSA_WITH_3DES_EDE_CBC_SHA";
188    case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA:
189	return "SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA";
190    case SSL_DH_DSS_WITH_DES_CBC_SHA:
191	return "SSL_DH_DSS_WITH_DES_CBC_SHA";
192    case SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA:
193	return "SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA";
194    case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA:
195	return "SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA";
196    case SSL_DH_RSA_WITH_DES_CBC_SHA:
197	return "SSL_DH_RSA_WITH_DES_CBC_SHA";
198    case SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA:
199	return "SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA";
200    case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA:
201	return "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA";
202    case SSL_DHE_DSS_WITH_DES_CBC_SHA:
203	return "SSL_DHE_DSS_WITH_DES_CBC_SHA";
204    case SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
205	return "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA";
206    case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA:
207	return "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA";
208    case SSL_DHE_RSA_WITH_DES_CBC_SHA:
209	return "SSL_DHE_RSA_WITH_DES_CBC_SHA";
210    case SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
211	return "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA";
212    case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5:
213	return "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5";
214    case SSL_DH_anon_WITH_RC4_128_MD5:
215	return "SSL_DH_anon_WITH_RC4_128_MD5";
216    case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA:
217	return "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA";
218    case SSL_DH_anon_WITH_DES_CBC_SHA:
219	return "SSL_DH_anon_WITH_DES_CBC_SHA";
220    case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA:
221	return "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA";
222    case SSL_FORTEZZA_DMS_WITH_NULL_SHA:
223	return "SSL_FORTEZZA_DMS_WITH_NULL_SHA";
224    case SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA:
225	return "SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA";
226    case SSL_RSA_WITH_RC2_CBC_MD5:
227	return "SSL_RSA_WITH_RC2_CBC_MD5";
228    case SSL_RSA_WITH_IDEA_CBC_MD5:
229	return "SSL_RSA_WITH_IDEA_CBC_MD5";
230    case SSL_RSA_WITH_DES_CBC_MD5:
231	return "SSL_RSA_WITH_DES_CBC_MD5";
232    case SSL_RSA_WITH_3DES_EDE_CBC_MD5:
233	return "SSL_RSA_WITH_3DES_EDE_CBC_MD5";
234    case SSL_NO_SUCH_CIPHERSUITE:
235	return "SSL_NO_SUCH_CIPHERSUITE";
236    default:
237	return "<unknown>";
238    }
239}
240
241/*
242 * Given a SSLProtocolVersion - typically from SSLGetProtocolVersion -
243 * return a string representation.
244 */
245const char *
246EAPSSLProtocolVersionString(SSLProtocol prot)
247{
248    switch(prot) {
249    case kSSLProtocolUnknown:
250	return "kSSLProtocolUnknown";
251    case kSSLProtocol2:
252	return "kSSLProtocol2";
253    case kSSLProtocol3:
254	return "kSSLProtocol3";
255    case kSSLProtocol3Only:
256	return "kSSLProtocol3Only";
257    case kTLSProtocol1:
258	return "kTLSProtocol1";
259    case kTLSProtocol1Only:
260	return "kTLSProtocol1Only";
261    default:
262	return "<unknown>";
263    }
264}
265
266/*
267 * Given an SSLContextRef and an array of SSLCipherSuites, terminated by
268 * SSL_NO_SUCH_CIPHERSUITE, select those SSLCipherSuites which the library
269 * supports and do a SSLSetEnabledCiphers() specifying those.
270 */
271static OSStatus
272setEnabledCiphers(SSLContextRef ctx,
273		  const SSLCipherSuite * ciphers)
274{
275    UInt32 numSupported;
276    OSStatus ortn = noErr;
277    SSLCipherSuite *supported = NULL;
278    SSLCipherSuite *enabled = NULL;
279    unsigned enabledDex = 0;	// index into enabled
280    unsigned supportedDex = 0;	// index into supported
281    unsigned inDex = 0;			// index into ciphers
282
283    /* first get all the supported ciphers */
284    ortn = SSLGetNumberSupportedCiphers(ctx, &numSupported);
285    if (ortn) {
286	goto done;
287    }
288    supported = (SSLCipherSuite *)malloc(numSupported * sizeof(SSLCipherSuite));
289    ortn = SSLGetSupportedCiphers(ctx, supported, &numSupported);
290    if (ortn) {
291	goto done;
292    }
293
294    /*
295     * Malloc an array we'll use for SSLGetEnabledCiphers - this will  be
296     * bigger than the number of suites we actually specify
297     */
298    enabled = (SSLCipherSuite *)malloc(numSupported * sizeof(SSLCipherSuite));
299
300    /*
301     * For each valid suite in ciphers, see if it's in the list of
302     * supported ciphers. If it is, add it to the list of ciphers to be
303     * enabled.
304     */
305    for(inDex=0; ciphers[inDex] != SSL_NO_SUCH_CIPHERSUITE; inDex++) {
306	for(supportedDex=0; supportedDex<numSupported; supportedDex++) {
307	    if(ciphers[inDex] == supported[supportedDex]) {
308		enabled[enabledDex++] = ciphers[inDex];
309		break;
310	    }
311	}
312    }
313
314    /* send it on down. */
315    ortn = SSLSetEnabledCiphers(ctx, enabled, enabledDex);
316 done:
317    if (enabled != NULL) {
318	free(enabled);
319    }
320    if (supported != NULL) {
321	free(supported);
322    }
323    return ortn;
324}
325
326/*
327 * Specify a restricted set of cipherspecs.
328 */
329OSStatus
330EAPSSLContextSetCipherRestrictions(SSLContextRef ctx, char cipherRestrict)
331{
332    OSStatus ortn = noErr;
333
334    switch(cipherRestrict) {
335    case 'e':
336	ortn = setEnabledCiphers(ctx, suites40);
337	break;
338    case 'd':
339	ortn = setEnabledCiphers(ctx, suitesDES);
340	break;
341    case 'D':
342	ortn = setEnabledCiphers(ctx, suitesDES40);
343	break;
344    case '3':
345	ortn = setEnabledCiphers(ctx, suites3DES);
346	break;
347    case '4':
348	ortn = setEnabledCiphers(ctx, suitesRC4);
349	break;
350    case '$':
351	ortn = setEnabledCiphers(ctx, suitesRC4_40);
352	break;
353    case '2':
354	ortn = setEnabledCiphers(ctx, suitesRC2);
355	break;
356    default:
357	break;
358    }
359    return ortn;
360}
361
362#endif /* NOTYET */
363
364const char *
365EAPSSLErrorString(OSStatus err)
366{
367    return (EAPSecurityErrorString(err));
368}
369
370SSLContextRef
371EAPSSLContextCreate(SSLProtocol protocol, bool is_server,
372		    SSLReadFunc func_read, SSLWriteFunc func_write,
373		    void * handle, char * peername, OSStatus * ret_status)
374{
375    SSLContextRef       ctx;
376    OSStatus		status;
377
378    *ret_status = noErr;
379    ctx = SSLCreateContext(NULL, is_server ? kSSLServerSide : kSSLClientSide,
380			   kSSLStreamType);
381    status = SSLSetIOFuncs(ctx, func_read, func_write);
382    if (status) {
383	goto cleanup;
384    }
385    status = SSLSetProtocolVersionMin(ctx, protocol);
386    if (status) {
387	goto cleanup;
388    }
389    status = SSLSetProtocolVersionMax(ctx, protocol);
390    if (status) {
391	goto cleanup;
392    }
393    status = SSLSetConnection(ctx, handle);
394    if (status) {
395	goto cleanup;
396    }
397    if (peername != NULL) {
398	status = SSLSetPeerDomainName(ctx, peername, strlen(peername) + 1);
399	if (status) {
400	    goto cleanup;
401	}
402    }
403#if NEED_TO_DISABLE_ONE_BYTE_OPTION
404    SSLSetSessionOption(ctx, kSSLSessionOptionSendOneByteRecord, FALSE);
405#endif /* NEED_TO_DISABLE_ONE_BYTE_OPTION */
406    if (is_server == FALSE) {
407	status = SSLSetSessionOption(ctx,
408				     kSSLSessionOptionBreakOnServerAuth,
409				     TRUE);
410	if (status != noErr) {
411	    goto cleanup;
412	}
413    }
414    (void)SSLSetSessionCacheTimeout(ctx, kEAPTLSSessionCacheTimeoutSeconds);
415    return (ctx);
416
417 cleanup:
418    if (ctx != NULL) {
419	CFRelease(ctx);
420    }
421    *ret_status = status;
422    return (NULL);
423}
424
425SSLContextRef
426EAPTLSMemIOContextCreate(bool is_server, memoryIORef mem_io,
427			 char * peername, OSStatus * ret_status)
428{
429    return(EAPSSLContextCreate(kTLSProtocol1, is_server,
430			       EAPSSLMemoryIORead, EAPSSLMemoryIOWrite,
431			       mem_io, peername, ret_status));
432}
433
434OSStatus
435EAPSSLMemoryIORead(SSLConnectionRef connection, void * data_buf,
436		   size_t * data_length)
437{
438    size_t		bytes_left;
439    size_t		length = *data_length;
440    memoryIORef		mem_io = (memoryIORef)connection;
441    memoryBufferRef	mem_buf = mem_io->read;
442
443    if (mem_buf == NULL) {
444	if (mem_io->debug) {
445	    EAPLOG_FL(LOG_DEBUG, "Read not initialized");
446	}
447	*data_length = 0;
448	return (noErr);
449    }
450    bytes_left = mem_buf->length - mem_buf->offset;
451    if (mem_buf->data == NULL
452	|| mem_buf->length == 0 || bytes_left == 0) {
453	*data_length = 0;
454	if (mem_io->debug) {
455	    EAPLOG_FL(LOG_DEBUG, "Read would block");
456	}
457	return (errSSLWouldBlock);
458    }
459    if (length > bytes_left) {
460	length = bytes_left;
461    }
462    bcopy(mem_buf->data + mem_buf->offset, data_buf, length);
463    mem_buf->offset += length;
464    if (mem_buf->offset == mem_buf->length) {
465	free(mem_buf->data);
466	bzero(mem_buf, sizeof(*mem_buf));
467    }
468    *data_length = length;
469    if (mem_io->debug) {
470	CFMutableStringRef	str;
471
472	str = CFStringCreateMutable(NULL, 0);
473	print_data_cfstr(str, data_buf, length);
474	EAPLOG_FL(-LOG_DEBUG, "Read %d bytes:\n%@", (int)length,
475		  str);
476	CFRelease(str);
477    }
478    return (noErr);
479}
480
481OSStatus
482EAPSSLMemoryIOWrite(SSLConnectionRef connection, const void * data_buf,
483		    size_t * data_length)
484{
485    bool		additional = FALSE;
486    size_t		length = *data_length;
487    memoryIORef		mem_io = (memoryIORef)connection;
488    memoryBufferRef	mem_buf = mem_io->write;
489
490    if (mem_buf == NULL) {
491	if (mem_io->debug) {
492	    EAPLOG_FL(LOG_DEBUG, "Write not initialized");
493	}
494	*data_length = 0;
495	return (noErr);
496    }
497    if (mem_buf->data == NULL) {
498	mem_buf->data = malloc(length);
499	mem_buf->offset = 0;
500	mem_buf->length = length;
501	bcopy(data_buf, mem_buf->data, length);
502    }
503    else {
504	additional = TRUE;
505	mem_buf->data = realloc(mem_buf->data, length + mem_buf->length);
506	bcopy(data_buf, mem_buf->data + mem_buf->length, length);
507	mem_buf->length += length;
508    }
509    if (mem_io->debug) {
510	CFMutableStringRef	str;
511
512	str = CFStringCreateMutable(NULL, 0);
513	print_data_cfstr(str, data_buf, length);
514	EAPLOG_FL(-LOG_DEBUG, "Wrote %s%d bytes:\n%@",
515		  additional ? "additional " : "",
516		  (int)length, str);
517	CFRelease(str);
518    }
519    return (noErr);
520}
521
522void
523memoryBufferInit(memoryBufferRef buf)
524{
525    bzero(buf, sizeof(*buf));
526    return;
527}
528
529void
530memoryBufferClear(memoryBufferRef buf)
531{
532    if (buf == NULL) {
533	return;
534    }
535    if (buf->data != NULL) {
536	free(buf->data);
537    }
538    bzero(buf, sizeof(*buf));
539    return;
540}
541
542void
543memoryBufferAllocate(memoryBufferRef buf, size_t length)
544{
545    buf->data = malloc(length);
546    buf->length = length;
547    buf->offset = 0;
548    buf->complete = FALSE;
549    return;
550}
551
552bool
553memoryBufferIsComplete(memoryBufferRef buf)
554{
555    return (buf->complete);
556}
557
558bool
559memoryBufferAddData(memoryBufferRef buf, const void * data, size_t length)
560{
561    if ((buf->offset + length) > buf->length) {
562	return (FALSE);
563    }
564    bcopy(data, buf->data + buf->offset, length);
565    buf->offset += length;
566    if (buf->offset == buf->length) {
567	buf->offset = 0;
568	buf->complete = TRUE;
569    }
570    return (TRUE);
571}
572
573void
574memoryIOClearBuffers(memoryIORef mem_io)
575{
576    memoryBufferClear(mem_io->read);
577    memoryBufferClear(mem_io->write);
578    return;
579}
580
581void
582memoryIOInit(memoryIORef mem_io, memoryBufferRef read_buf,
583	     memoryBufferRef write_buf)
584{
585    bzero(mem_io, sizeof(*mem_io));
586    memoryBufferInit(read_buf);
587    memoryBufferInit(write_buf);
588    mem_io->read = read_buf;
589    mem_io->write = write_buf;
590    return;
591}
592
593void
594memoryIOSetDebug(memoryIORef mem_io, bool debug)
595{
596    mem_io->debug = debug;
597    return;
598}
599
600OSStatus
601EAPTLSComputeKeyData(SSLContextRef ssl_context,
602		     const void * label, int label_length,
603		     void * key, int key_length)
604{
605    char		master_secret[SSL_MASTER_SECRET_SIZE];
606    size_t		master_secret_length;
607    size_t		offset;
608    char		random[SSL_CLIENT_SRVR_RAND_SIZE * 2];
609    size_t		random_size = 0;
610    size_t		size;
611    OSStatus		status;
612
613    offset = 0;
614    size = sizeof(random);
615    status = SSLInternalClientRandom(ssl_context, random, &size);
616    if (status != noErr) {
617	EAPLOG_FL(LOG_NOTICE,
618		  "SSLInternalClientRandom failed, %s",
619		  EAPSSLErrorString(status));
620	return (status);
621    }
622    offset += size;
623    random_size += size;
624    if ((size + SSL_CLIENT_SRVR_RAND_SIZE) > sizeof(random)) {
625	EAPLOG_FL(LOG_NOTICE,
626		  "buffer overflow %ld >= %ld",
627		  size + SSL_CLIENT_SRVR_RAND_SIZE, sizeof(random));
628	return (errSSLBufferOverflow);
629    }
630    size = sizeof(random) - size;
631    status = SSLInternalServerRandom(ssl_context, random + offset, &size);
632    if (status != noErr) {
633	EAPLOG_FL(LOG_NOTICE,
634		  "SSLInternalServerRandom failed, %s",
635		  EAPSSLErrorString(status));
636	return (status);
637    }
638    random_size += size;
639    master_secret_length = sizeof(master_secret);
640    status = SSLInternalMasterSecret(ssl_context, master_secret,
641				     &master_secret_length);
642    if (status != noErr) {
643	EAPLOG_FL(LOG_NOTICE,
644		  "SSLInternalMasterSecret failed, %s",
645		  EAPSSLErrorString(status));
646	return (status);
647    }
648    status = SSLInternal_PRF(ssl_context,
649			     master_secret, master_secret_length,
650			     label, label_length,
651			     random, random_size,
652			     key, key_length);
653    if (status != noErr) {
654	EAPLOG_FL(LOG_NOTICE,
655		  "SSLInternal_PRF failed, %s", EAPSSLErrorString(status));
656	return (status);
657    }
658    return (status);
659}
660
661EAPPacket *
662EAPTLSPacketCreate2(EAPCode code, int type, u_char identifier, int mtu,
663		    memoryBufferRef buf, int * ret_fraglen,
664		    bool always_mark_first)
665{
666    bool		first_fragment = FALSE;
667    bool		more_fragments = FALSE;
668    EAPTLSPacket *	eaptls = NULL;
669    int 		pkt_size;
670    size_t		fraglen = 0;
671
672    if (buf != NULL && buf->data != NULL && buf->offset < buf->length) {
673	int	max_payload;
674
675	if (buf->offset == 0 && always_mark_first) {
676	    first_fragment = TRUE;
677	    pkt_size = sizeof(EAPTLSLengthIncludedPacket);
678	}
679	else {
680	    pkt_size = sizeof(EAPTLSPacket);
681	}
682	max_payload = mtu - pkt_size;
683	fraglen = buf->length - buf->offset;
684	if (fraglen > max_payload) {
685	    if (buf->offset == 0 && always_mark_first == FALSE) {
686		first_fragment = TRUE;
687		pkt_size = sizeof(EAPTLSLengthIncludedPacket);
688		max_payload = mtu - pkt_size;
689	    }
690	    more_fragments = TRUE;
691	    fraglen = max_payload;
692	}
693    }
694    else {
695	pkt_size = sizeof(EAPTLSPacket);
696    }
697
698    if (ret_fraglen != NULL) {
699	*ret_fraglen = fraglen;
700    }
701    pkt_size += fraglen;
702    eaptls = malloc(pkt_size);
703    if (eaptls == NULL) {
704	return (NULL);
705    }
706    eaptls->code = code;
707    eaptls->identifier = identifier;
708    EAPPacketSetLength((EAPPacketRef)eaptls, pkt_size);
709    eaptls->type = type;
710    eaptls->flags = 0;
711    if (fraglen != 0) {
712	void *		dest;
713
714	dest = eaptls->tls_data;
715	if (more_fragments) {
716	    eaptls->flags = kEAPTLSPacketFlagsMoreFragments;
717	}
718	if (first_fragment) {
719	    EAPTLSLengthIncludedPacket * first;
720
721	    /* ALIGN: void * cast OK,
722	     * we don't expect proper alignment */
723	    first = (EAPTLSLengthIncludedPacket *)(void *)eaptls;
724	    eaptls->flags |= kEAPTLSPacketFlagsLengthIncluded;
725            EAPTLSLengthIncludedPacketSetMessageLength(first,
726						       buf->length);
727	    dest = first->tls_data;
728	}
729	bcopy(buf->data + buf->offset, dest, fraglen);
730    }
731    return ((EAPPacket *)eaptls);
732}
733
734EAPPacket *
735EAPTLSPacketCreate(EAPCode code, int type, u_char identifier, int mtu,
736		   memoryBufferRef buf, int * ret_fraglen)
737{
738    return (EAPTLSPacketCreate2(code, type, identifier, mtu,
739				buf, ret_fraglen, TRUE));
740}
741
742OSStatus
743EAPSSLCopyPeerCertificates(SSLContextRef context, CFArrayRef * certs)
744{
745    CFMutableArrayRef	array = NULL;
746    int			count = 0;
747    int			i;
748    OSStatus		status;
749    SecTrustRef		trust = NULL;
750
751    status = SSLCopyPeerTrust(context, &trust);
752    if (status != noErr) {
753	EAPLOG_FL(LOG_NOTICE, "SSLCopyPeerTrust returned NULL");
754	goto done;
755    }
756    count = SecTrustGetCertificateCount(trust);
757    if (count == 0) {
758	/* this should not happen */
759	status = errSecItemNotFound;
760	EAPLOG_FL(LOG_NOTICE, "SecTrustGetCertificateCount returned 0");
761	goto done;
762    }
763    array = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks);
764    for (i = 0; i < count; i++) {
765	SecCertificateRef	s;
766
767	s = SecTrustGetCertificateAtIndex(trust, i);
768	CFArrayAppendValue(array, s);
769    }
770 done:
771    my_CFRelease(&trust);
772    *certs = array;
773    return (status);
774}
775
776enum {
777    kAvoidDenialOfServiceSize = 128 * 1024
778};
779
780CFStringRef
781EAPTLSPacketCopyDescription(EAPTLSPacketRef eaptls_pkt, bool * packet_is_valid)
782{
783    EAPTLSLengthIncludedPacketRef eaptls_pkt_l;
784    int			data_length;
785    void *		data_ptr = NULL;
786    u_int16_t		length = EAPPacketGetLength((EAPPacketRef)eaptls_pkt);
787    CFMutableStringRef	str;
788    u_int32_t		tls_message_length = 0;
789
790    *packet_is_valid = FALSE;
791    switch (eaptls_pkt->code) {
792    case kEAPCodeRequest:
793    case kEAPCodeResponse:
794	break;
795    default:
796	return (NULL);
797    }
798    str = CFStringCreateMutable(NULL, 0);
799    if (length < sizeof(*eaptls_pkt)) {
800	STRING_APPEND(str, "EAPTLSPacket header truncated %d < %d\n",
801		      length, (int)sizeof(*eaptls_pkt));
802	goto done;
803    }
804    STRING_APPEND(str, "%s %s: Identifier %d Length %d Flags 0x%x%s",
805		  EAPTypeStr(eaptls_pkt->type),
806		  eaptls_pkt->code == kEAPCodeRequest ? "Request" : "Response",
807		  eaptls_pkt->identifier, length, eaptls_pkt->flags,
808		  eaptls_pkt->flags != 0 ? " [" : "");
809
810    /* ALIGN: void * cast OK, we don't expect proper alignment */
811    eaptls_pkt_l = (EAPTLSLengthIncludedPacketRef)(void *)eaptls_pkt;
812    data_ptr = eaptls_pkt->tls_data;
813    tls_message_length = data_length = length - sizeof(EAPTLSPacket);
814
815    if ((eaptls_pkt->flags & kEAPTLSPacketFlagsStart) != 0) {
816	STRING_APPEND(str, " start");
817    }
818    if ((eaptls_pkt->flags & kEAPTLSPacketFlagsLengthIncluded) != 0) {
819	if (length < sizeof(EAPTLSLengthIncludedPacket)) {
820	    STRING_APPEND(str, "\nEAPTLSLengthIncludedPacket "
821			  "header truncated %d < %d\n",
822			  length, (int)sizeof(EAPTLSLengthIncludedPacket));
823	    goto done;
824	}
825	data_ptr = eaptls_pkt_l->tls_data;
826	data_length = length - sizeof(EAPTLSLengthIncludedPacket);
827	tls_message_length
828	    = EAPTLSLengthIncludedPacketGetMessageLength(eaptls_pkt_l);
829	STRING_APPEND(str, " length=%u", tls_message_length);
830
831    }
832    if ((eaptls_pkt->flags & kEAPTLSPacketFlagsMoreFragments) != 0) {
833	STRING_APPEND(str, " more");
834    }
835    STRING_APPEND(str, "%s Data Length %d\n",
836		  eaptls_pkt->flags != 0 ? " ]" : "", data_length);
837    if (tls_message_length > kAvoidDenialOfServiceSize) {
838	STRING_APPEND(str, "rejecting packet to avoid DOS attack %u > %d\n",
839		      tls_message_length, kAvoidDenialOfServiceSize);
840	goto done;
841    }
842    print_data_cfstr(str, data_ptr, data_length);
843    *packet_is_valid = TRUE;
844
845 done:
846    return (str);
847
848}
849
850OSStatus
851EAPSecPolicyCopy(SecPolicyRef * ret_policy)
852{
853#if TARGET_OS_EMBEDDED
854    *ret_policy = SecPolicyCreateEAP(FALSE, NULL);
855#else /* TARGET_OS_EMBEDDED */
856    *ret_policy = SecPolicyCreateWithProperties(kSecPolicyAppleEAP, NULL);
857#endif /* TARGET_OS_EMBEDDED */
858    if (*ret_policy != NULL) {
859	return (noErr);
860    }
861    return (-1);
862}
863
864static CFArrayRef
865copy_cert_list(CFDictionaryRef properties, CFStringRef prop_name)
866{
867    CFArrayRef		data_list;
868
869    if (properties == NULL) {
870	return (NULL);
871    }
872    data_list = CFDictionaryGetValue(properties, prop_name);
873    if (isA_CFArray(data_list) == NULL) {
874	return (NULL);
875    }
876    return (EAPCFDataArrayCreateSecCertificateArray(data_list));
877}
878
879static CFArrayRef
880copy_user_trust_proceed_certs(CFDictionaryRef properties)
881{
882    CFArrayRef		p;
883
884    p = CFDictionaryGetValue(properties,
885			     kEAPClientPropTLSUserTrustProceedCertificateChain);
886    if (p != NULL) {
887	p = EAPCFDataArrayCreateSecCertificateArray(p);
888    }
889    return (p);
890}
891
892static CFArrayRef
893get_trusted_server_names(CFDictionaryRef properties)
894{
895    int		count;
896    int		i;
897    CFArrayRef	list;
898
899    list = CFDictionaryGetValue(properties,
900				kEAPClientPropTLSTrustedServerNames);
901    if (list == NULL) {
902	list = CFDictionaryGetValue(properties,
903				    CFSTR("TLSTrustedServerCommonNames"));
904	if (list == NULL) {
905	    return (NULL);
906	}
907    }
908    if (isA_CFArray(list) == NULL) {
909	EAPLOG_FL(LOG_NOTICE,
910		  "TLSTrustedServerNames is not an array");
911	return (NULL);
912    }
913    count = CFArrayGetCount(list);
914    if (count == 0) {
915	EAPLOG_FL(LOG_NOTICE,
916		  "TLSTrustedServerNames is empty");
917	return (NULL);
918    }
919    for (i = 0; i < count; i++) {
920	CFStringRef	name = CFArrayGetValueAtIndex(list, i);
921
922	if (isA_CFString(name) == NULL) {
923	    EAPLOG_FL(LOG_NOTICE,
924		   "TLSTrustedServerNames contains a non-string value");
925	    return (NULL);
926	}
927    }
928    return (list);
929}
930
931#if TARGET_OS_EMBEDDED
932#include <CoreFoundation/CFPreferences.h>
933#include <notify.h>
934
935#define kEAPTLSTrustExceptionsID 		"com.apple.network.eapclient.tls.TrustExceptions"
936#define kEAPTLSTrustExceptionsApplicationID	CFSTR(kEAPTLSTrustExceptionsID)
937
938static int	token;
939static bool	token_valid;
940
941static void
942exceptions_change_check(void)
943{
944    int		check = 0;
945    uint32_t	status;
946
947    if (!token_valid) {
948	status = notify_register_check(kEAPTLSTrustExceptionsID, &token);
949	if (status != NOTIFY_STATUS_OK) {
950	    EAPLOG_FL(LOG_NOTICE,
951		      "notify_register_check returned %d",
952		   status);
953	    return;
954	}
955	token_valid = TRUE;
956    }
957    status = notify_check(token, &check);
958    if (status != NOTIFY_STATUS_OK) {
959	EAPLOG_FL(LOG_NOTICE,
960		  "notify_check returned %d",
961	       status);
962	return;
963    }
964    if (check != 0) {
965	CFPreferencesSynchronize(kEAPTLSTrustExceptionsApplicationID,
966				 kCFPreferencesCurrentUser,
967				 kCFPreferencesAnyHost);
968    }
969    return;
970}
971
972static void
973exceptions_change_notify(void)
974{
975    uint32_t	status;
976
977    status = notify_post(kEAPTLSTrustExceptionsID);
978    if (status != NOTIFY_STATUS_OK) {
979	EAPLOG_FL(LOG_NOTICE,
980		  "notify_post returned %d",
981		  status);
982    }
983    return;
984}
985
986static void
987EAPTLSTrustExceptionsSave(CFStringRef domain, CFStringRef identifier,
988			  CFStringRef hash_str, CFDataRef exceptions)
989{
990    CFDictionaryRef	domain_list;
991    CFDictionaryRef	exceptions_list = NULL;
992    bool		store_exceptions = TRUE;
993
994    exceptions_change_check();
995    domain_list = CFPreferencesCopyValue(domain,
996					 kEAPTLSTrustExceptionsApplicationID,
997					 kCFPreferencesCurrentUser,
998					 kCFPreferencesAnyHost);
999    if (domain_list != NULL && isA_CFDictionary(domain_list) == NULL) {
1000	CFRelease(domain_list);
1001	domain_list = NULL;
1002    }
1003    if (domain_list != NULL) {
1004	exceptions_list = CFDictionaryGetValue(domain_list, identifier);
1005	exceptions_list = isA_CFDictionary(exceptions_list);
1006	if (exceptions_list != NULL) {
1007	    CFDataRef	stored_exceptions;
1008
1009	    stored_exceptions = CFDictionaryGetValue(exceptions_list, hash_str);
1010	    if (isA_CFData(stored_exceptions) != NULL
1011		&& CFEqual(stored_exceptions, exceptions)) {
1012		/* stored exceptions are correct, don't store them again */
1013		store_exceptions = FALSE;
1014	    }
1015	}
1016    }
1017    if (store_exceptions) {
1018	if (exceptions_list == NULL) {
1019	    /* no exceptions for this identifier yet, create one */
1020	    exceptions_list
1021		= CFDictionaryCreate(NULL,
1022				     (const void * * )&hash_str,
1023				     (const void * *)&exceptions,
1024				     1,
1025				     &kCFTypeDictionaryKeyCallBacks,
1026				     &kCFTypeDictionaryValueCallBacks);
1027	}
1028	else {
1029	    /* update existing exceptions with this one */
1030	    CFMutableDictionaryRef	new_exceptions_list;
1031
1032	    new_exceptions_list
1033		= CFDictionaryCreateMutableCopy(NULL, 0,
1034						exceptions_list);
1035	    CFDictionarySetValue(new_exceptions_list, hash_str, exceptions);
1036	    /* don't CFRelease(exceptions_list), it's from domain_list */
1037	    exceptions_list = (CFDictionaryRef)new_exceptions_list;
1038
1039	}
1040	if (domain_list == NULL) {
1041	    domain_list
1042		= CFDictionaryCreate(NULL,
1043				     (const void * *)&identifier,
1044				     (const void * *)&exceptions_list,
1045				     1,
1046				     &kCFTypeDictionaryKeyCallBacks,
1047				     &kCFTypeDictionaryValueCallBacks);
1048	}
1049	else {
1050	    CFMutableDictionaryRef	new_domain_list;
1051
1052	    new_domain_list
1053		= CFDictionaryCreateMutableCopy(NULL, 0,
1054						domain_list);
1055	    CFDictionarySetValue(new_domain_list, identifier, exceptions_list);
1056	    CFRelease(domain_list);
1057	    domain_list = (CFDictionaryRef)new_domain_list;
1058
1059	}
1060	CFRelease(exceptions_list);
1061	CFPreferencesSetValue(domain, domain_list,
1062			      kEAPTLSTrustExceptionsApplicationID,
1063			      kCFPreferencesCurrentUser,
1064			      kCFPreferencesAnyHost);
1065	CFPreferencesSynchronize(kEAPTLSTrustExceptionsApplicationID,
1066				 kCFPreferencesCurrentUser,
1067				 kCFPreferencesAnyHost);
1068	exceptions_change_notify();
1069    }
1070    my_CFRelease(&domain_list);
1071    return;
1072}
1073
1074/*
1075 * Function: EAPTLSSecTrustSaveExceptions
1076 * Purpose:
1077 *   Given the evaluated SecTrustRef object, save an exceptions binding for the
1078 *   given domain, identifier, and server_hash_str, all of which must be
1079 *   specified.
1080 * Returns:
1081 *   FALSE if the trust object was not in a valid state,
1082 *   TRUE otherwise.
1083 */
1084bool
1085EAPTLSSecTrustSaveExceptionsBinding(SecTrustRef trust,
1086				    CFStringRef domain, CFStringRef identifier,
1087				    CFStringRef server_hash_str)
1088{
1089    CFDataRef 	exceptions;
1090
1091    exceptions = SecTrustCopyExceptions(trust);
1092    if (exceptions == NULL) {
1093	EAPLOG_FL(LOG_NOTICE, "failed to copy exceptions");
1094	return (FALSE);
1095    }
1096    EAPTLSTrustExceptionsSave(domain, identifier, server_hash_str,
1097			      exceptions);
1098    CFRelease(exceptions);
1099    return (TRUE);
1100}
1101
1102/*
1103 * Function: EAPTLSRemoveTrustExceptionsBindings
1104 * Purpose:
1105 *   Remove all of the trust exceptions bindings for the given
1106 *   trust domain and identifier.
1107 *
1108 * Example:
1109 * EAPTLSRemoveTrustExceptionsBindings(kEAPTLSTrustExceptionsDomainWirelessSSID,
1110 * 				       ssid);
1111 */
1112void
1113EAPTLSRemoveTrustExceptionsBindings(CFStringRef domain, CFStringRef identifier)
1114{
1115    CFDictionaryRef	domain_list;
1116    CFDictionaryRef	exceptions_list;
1117
1118    /*
1119     * Remove the saved EAP-SIM/EAP-AKA information as well.
1120     * XXX: this call should be moved into a common "EAP cleanup" function.
1121     */
1122    if (my_CFEqual(domain, kEAPTLSTrustExceptionsDomainWirelessSSID)) {
1123	EAPSIMAKAPersistentStateForgetSSID(identifier);
1124    }
1125
1126    exceptions_change_check();
1127    domain_list = CFPreferencesCopyValue(domain,
1128					 kEAPTLSTrustExceptionsApplicationID,
1129					 kCFPreferencesCurrentUser,
1130					 kCFPreferencesAnyHost);
1131    if (domain_list != NULL && isA_CFDictionary(domain_list) == NULL) {
1132	CFRelease(domain_list);
1133	domain_list = NULL;
1134    }
1135    if (domain_list == NULL) {
1136	return;
1137    }
1138    exceptions_list = CFDictionaryGetValue(domain_list, identifier);
1139    if (exceptions_list != NULL) {
1140	CFMutableDictionaryRef	new_domain_list;
1141
1142	new_domain_list
1143	    = CFDictionaryCreateMutableCopy(NULL, 0,
1144					    domain_list);
1145	CFDictionaryRemoveValue(new_domain_list, identifier);
1146	CFPreferencesSetValue(domain, new_domain_list,
1147			      kEAPTLSTrustExceptionsApplicationID,
1148			      kCFPreferencesCurrentUser,
1149			      kCFPreferencesAnyHost);
1150	CFPreferencesSynchronize(kEAPTLSTrustExceptionsApplicationID,
1151				 kCFPreferencesCurrentUser,
1152				 kCFPreferencesAnyHost);
1153	exceptions_change_notify();
1154	CFRelease(new_domain_list);
1155    }
1156    CFRelease(domain_list);
1157    return;
1158}
1159
1160static CFDataRef
1161EAPTLSTrustExceptionsCopy(CFStringRef domain, CFStringRef identifier,
1162			  CFStringRef hash_str)
1163{
1164    CFDataRef		exceptions = NULL;
1165    CFDictionaryRef	domain_list;
1166
1167    exceptions_change_check();
1168    domain_list = CFPreferencesCopyValue(domain,
1169					 kEAPTLSTrustExceptionsApplicationID,
1170					 kCFPreferencesCurrentUser,
1171					 kCFPreferencesAnyHost);
1172    if (isA_CFDictionary(domain_list) != NULL) {
1173	CFDictionaryRef		exceptions_list;
1174
1175	exceptions_list = CFDictionaryGetValue(domain_list, identifier);
1176	if (isA_CFDictionary(exceptions_list) != NULL) {
1177	    exceptions = isA_CFData(CFDictionaryGetValue(exceptions_list,
1178							 hash_str));
1179	    if (exceptions != NULL) {
1180		CFRetain(exceptions);
1181	    }
1182	}
1183    }
1184    my_CFRelease(&domain_list);
1185    return (exceptions);
1186}
1187
1188/*
1189 * Function: EAPTLSSecTrustApplyExceptionsBinding
1190 * Purpose:
1191 *   Finds a stored trust exceptions object for the given domain, identifier,
1192 *   and server_cert_hash.  If it exists, sets the exceptions on the given
1193 *   trust object.
1194 */
1195void
1196EAPTLSSecTrustApplyExceptionsBinding(SecTrustRef trust, CFStringRef domain,
1197				     CFStringRef identifier,
1198				     CFStringRef server_cert_hash)
1199{
1200    CFDataRef		exceptions;
1201
1202    exceptions = EAPTLSTrustExceptionsCopy(domain, identifier,
1203					   server_cert_hash);
1204    if (exceptions != NULL) {
1205	if (SecTrustSetExceptions(trust, exceptions) == FALSE) {
1206	    EAPLOG_FL(LOG_NOTICE, "SecTrustSetExceptions failed");
1207	}
1208    }
1209    my_CFRelease(&exceptions);
1210    return;
1211}
1212
1213static SecTrustRef
1214_EAPTLSCreateSecTrust(CFDictionaryRef properties,
1215		      CFArrayRef server_certs,
1216		      OSStatus * ret_status,
1217		      EAPClientStatus * ret_client_status,
1218		      bool * ret_allow_exceptions,
1219		      bool * ret_has_server_certs_or_names,
1220		      CFStringRef * ret_server_hash_str)
1221{
1222    bool		allow_exceptions;
1223    EAPClientStatus	client_status;
1224    int			count;
1225    CFStringRef		domain = NULL;
1226    CFStringRef		identifier = NULL;
1227    SecPolicyRef	policy = NULL;
1228    OSStatus		status = noErr;
1229    CFStringRef		server_hash_str = NULL;
1230    CFArrayRef		trusted_certs = NULL;
1231    CFArrayRef		trusted_server_names;
1232    SecTrustRef		trust = NULL;
1233
1234    client_status = kEAPClientStatusInternalError;
1235    if (server_certs == NULL) {
1236	goto done;
1237    }
1238    count = CFArrayGetCount(server_certs);
1239    if (count == 0) {
1240	goto done;
1241    }
1242    client_status = kEAPClientStatusSecurityError;
1243    trusted_server_names = get_trusted_server_names(properties);
1244    policy = SecPolicyCreateEAP(FALSE, trusted_server_names);
1245    if (policy == NULL) {
1246	goto done;
1247    }
1248    status = SecTrustCreateWithCertificates(server_certs, policy, &trust);
1249    if (status != noErr) {
1250	EAPLOG_FL(LOG_NOTICE,
1251		  "SecTrustCreateWithCertificates failed, %s (%d)",
1252		  EAPSecurityErrorString(status), (int)status);
1253	goto done;
1254    }
1255    trusted_certs = copy_cert_list(properties,
1256				   kEAPClientPropTLSTrustedCertificates);
1257    if (trusted_certs != NULL) {
1258	status = SecTrustSetAnchorCertificates(trust, trusted_certs);
1259	if (status != noErr) {
1260	    EAPLOG_FL(LOG_NOTICE,
1261		      " SecTrustSetAnchorCertificates failed, %s (%d)",
1262		      EAPSecurityErrorString(status), (int)status);
1263	    goto done;
1264	}
1265    }
1266
1267    /*
1268     * Don't allow exceptions by default if either trusted certs or trusted
1269     * server names is specified.  Trust exceptions must be explicitly enabled
1270     * in that case using the kEAPClientPropTLSAllowTrustExceptions property.
1271     */
1272    if (trusted_certs != NULL || trusted_server_names != NULL) {
1273	allow_exceptions = FALSE;
1274    }
1275    else {
1276	allow_exceptions = TRUE;
1277    }
1278    allow_exceptions
1279	= my_CFDictionaryGetBooleanValue(properties,
1280					 kEAPClientPropTLSAllowTrustExceptions,
1281					 allow_exceptions);
1282
1283    /* both the trust exception domain and identifier must be specified */
1284    domain
1285	= CFDictionaryGetValue(properties,
1286			       kEAPClientPropTLSTrustExceptionsDomain);
1287    identifier
1288	= CFDictionaryGetValue(properties,
1289			       kEAPClientPropTLSTrustExceptionsID);
1290    if (isA_CFString(domain) == NULL || isA_CFString(identifier) == NULL) {
1291	allow_exceptions = FALSE;
1292    }
1293    if (allow_exceptions) {
1294	SecCertificateRef	server;
1295
1296	server = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, 0);
1297	server_hash_str = EAPSecCertificateCopySHA1DigestString(server);
1298	EAPTLSSecTrustApplyExceptionsBinding(trust, domain, identifier,
1299					     server_hash_str);
1300    }
1301    client_status = kEAPClientStatusOK;
1302
1303 done:
1304    if (client_status == kEAPClientStatusOK) {
1305	if (ret_allow_exceptions != NULL) {
1306	    *ret_allow_exceptions = allow_exceptions;
1307	}
1308	if (ret_has_server_certs_or_names != NULL) {
1309	    *ret_has_server_certs_or_names
1310		= (trusted_certs != NULL || trusted_server_names != NULL);
1311	}
1312	if (ret_server_hash_str != NULL) {
1313	    *ret_server_hash_str = server_hash_str;
1314	}
1315	else {
1316	    my_CFRelease(&server_hash_str);
1317	}
1318    }
1319    else {
1320	my_CFRelease(&trust);
1321    }
1322    if (ret_status != NULL) {
1323	*ret_status = status;
1324    }
1325    if (ret_client_status != NULL) {
1326	*ret_client_status = client_status;
1327    }
1328    my_CFRelease(&policy);
1329    my_CFRelease(&trusted_certs);
1330    return (trust);
1331}
1332
1333SecTrustRef
1334EAPTLSCreateSecTrust(CFDictionaryRef properties, CFArrayRef server_certs,
1335		     CFStringRef domain, CFStringRef identifier)
1336{
1337    CFMutableDictionaryRef	dict;
1338    SecTrustRef			trust;
1339
1340    dict = CFDictionaryCreateMutableCopy(NULL, 0, properties);
1341    CFDictionarySetValue(dict, kEAPClientPropTLSTrustExceptionsDomain, domain);
1342    CFDictionarySetValue(dict, kEAPClientPropTLSTrustExceptionsID, identifier);
1343    trust = _EAPTLSCreateSecTrust(dict, server_certs,
1344				  NULL, NULL, NULL, NULL, NULL);
1345    my_CFRelease(&dict);
1346    return (trust);
1347}
1348
1349EAPClientStatus
1350EAPTLSVerifyServerCertificateChain(CFDictionaryRef properties,
1351				   CFArrayRef server_certs,
1352				   OSStatus * ret_status)
1353{
1354    bool		allow_exceptions = FALSE;
1355    bool		has_server_certs_or_names = FALSE;
1356    EAPClientStatus	client_status;
1357    OSStatus		status;
1358    CFStringRef		server_hash_str = NULL;
1359    SecTrustRef		trust = NULL;
1360    SecTrustResultType 	trust_result;
1361
1362
1363    trust = _EAPTLSCreateSecTrust(properties,
1364				  server_certs,
1365				  &status,
1366				  &client_status,
1367				  &allow_exceptions,
1368				  &has_server_certs_or_names,
1369				  &server_hash_str);
1370    if (trust == NULL) {
1371	goto done;
1372    }
1373    client_status = kEAPClientStatusSecurityError;
1374    status = SecTrustEvaluate(trust, &trust_result);
1375    if (status != noErr) {
1376	EAPLOG_FL(LOG_NOTICE,
1377		  "SecTrustEvaluate failed, %s (%d)",
1378		  EAPSecurityErrorString(status), (int)status);
1379	goto done;
1380    }
1381    switch (trust_result) {
1382    case kSecTrustResultProceed:
1383	client_status = kEAPClientStatusOK;
1384	break;
1385    case kSecTrustResultUnspecified:
1386	if (has_server_certs_or_names) {
1387	    /* trusted certs or server names specified, it's OK to proceed */
1388	    client_status = kEAPClientStatusOK;
1389	    break;
1390	}
1391	/* FALL THROUGH */
1392    case kSecTrustResultRecoverableTrustFailure:
1393	if (allow_exceptions) {
1394	    client_status = kEAPClientStatusUserInputRequired;
1395	    break;
1396	}
1397	/* FALL THROUGH */
1398    case kSecTrustResultDeny:
1399    default:
1400	status = errSSLXCertChainInvalid;
1401	break;
1402    }
1403
1404    /* if the trust is recoverable, check whether the user already said OK */
1405    if (client_status == kEAPClientStatusUserInputRequired) {
1406	CFArrayRef	proceed;
1407
1408	proceed = copy_user_trust_proceed_certs(properties);
1409	if (proceed != NULL
1410	    && CFEqual(proceed, server_certs)) {
1411	    bool	save_it;
1412
1413	    client_status = kEAPClientStatusOK;
1414	    save_it = my_CFDictionaryGetBooleanValue(properties,
1415						     kEAPClientPropTLSSaveTrustExceptions,
1416						     FALSE);
1417	    if (save_it && server_hash_str != NULL) {
1418		CFStringRef	domain;
1419		CFStringRef	identifier;
1420
1421		domain
1422		    = CFDictionaryGetValue(properties,
1423					   kEAPClientPropTLSTrustExceptionsDomain);
1424		identifier
1425		    = CFDictionaryGetValue(properties,
1426					   kEAPClientPropTLSTrustExceptionsID);
1427		EAPTLSSecTrustSaveExceptionsBinding(trust, domain, identifier,
1428						    server_hash_str);
1429	    }
1430	}
1431	my_CFRelease(&proceed);
1432    }
1433
1434 done:
1435    if (ret_status != NULL) {
1436	*ret_status = status;
1437    }
1438    my_CFRelease(&trust);
1439    my_CFRelease(&server_hash_str);
1440    return (client_status);
1441}
1442
1443#else /* TARGET_OS_EMBEDDED */
1444
1445static bool
1446cert_list_contains_cert(CFArrayRef list, SecCertificateRef cert)
1447{
1448    int		count;
1449    int		i;
1450
1451    count = CFArrayGetCount(list);
1452    for (i = 0; i < count; i++) {
1453	SecCertificateRef	this_cert;
1454
1455	this_cert = (SecCertificateRef)CFArrayGetValueAtIndex(list, i);
1456	if (CFEqual(cert, this_cert)) {
1457	    return (TRUE);
1458	}
1459    }
1460    return (FALSE);
1461}
1462
1463static CFArrayRef
1464EAPSecTrustCopyCertificateChain(SecTrustRef trust)
1465{
1466    CFMutableArrayRef	array = NULL;
1467    int			count = SecTrustGetCertificateCount(trust);
1468    int			i;
1469
1470    if (count == 0) {
1471	EAPLOG_FL(LOG_NOTICE, "SecTrustGetCertificateCount returned 0");
1472	goto done;
1473    }
1474    array = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks);
1475    for (i = 0; i < count; i++) {
1476	SecCertificateRef	s;
1477
1478	s = SecTrustGetCertificateAtIndex(trust, i);
1479	CFArrayAppendValue(array, s);
1480    }
1481 done:
1482    return (array);
1483}
1484
1485static bool
1486server_cert_chain_is_trusted(SecTrustRef trust, CFArrayRef trusted_certs)
1487{
1488    CFArrayRef 				cert_chain;
1489    int					count;
1490    int					i;
1491    bool				ret = FALSE;
1492
1493    cert_chain = EAPSecTrustCopyCertificateChain(trust);
1494    if (cert_chain != NULL) {
1495	count = CFArrayGetCount(cert_chain);
1496    }
1497    else {
1498	count = 0;
1499    }
1500    if (count == 0) {
1501	EAPLOG_FL(LOG_NOTICE, "failed to get evidence chain");
1502	goto done;
1503    }
1504    for (i = 0; i < count; i++) {
1505	SecCertificateRef	cert;
1506
1507	cert = (SecCertificateRef)CFArrayGetValueAtIndex(cert_chain, i);
1508	if (cert_list_contains_cert(trusted_certs, cert)) {
1509	    ret = TRUE;
1510	    break;
1511	}
1512    }
1513
1514 done:
1515    my_CFRelease(&cert_chain);
1516    return (ret);
1517}
1518
1519static bool
1520server_name_matches_server_names(CFStringRef name,
1521				 CFArrayRef trusted_server_names)
1522{
1523    int			count;
1524    int			i;
1525    bool		trusted = FALSE;
1526
1527    count = CFArrayGetCount(trusted_server_names);
1528    for (i = 0; i < count; i++) {
1529	CFStringRef	this_name = CFArrayGetValueAtIndex(trusted_server_names,
1530							   i);
1531	if (CFEqual(name, this_name)) {
1532	    trusted = TRUE;
1533	    break;
1534	}
1535	if (CFStringHasPrefix(this_name, CFSTR("*."))) {
1536	    bool		match = FALSE;
1537	    CFMutableStringRef	suffix;
1538
1539	    suffix = CFStringCreateMutableCopy(NULL, 0, this_name);
1540	    CFStringDelete(suffix, CFRangeMake(0, 1)); /* remove dot */
1541	    if (CFStringHasSuffix(name, suffix)) {
1542		match = TRUE;
1543	    }
1544	    CFRelease(suffix);
1545	    if (match) {
1546		trusted = TRUE;
1547		break;
1548	    }
1549	}
1550    }
1551    return (trusted);
1552}
1553
1554static bool
1555server_cert_matches_server_names(SecCertificateRef cert,
1556				 CFArrayRef trusted_server_names)
1557{
1558    CFDictionaryRef	attrs;
1559    bool		match = FALSE;
1560    CFStringRef		name;
1561
1562    attrs = EAPSecCertificateCopyAttributesDictionary(cert);
1563    if (attrs == NULL) {
1564	goto done;
1565    }
1566    name = CFDictionaryGetValue(attrs, kEAPSecCertificateAttributeCommonName);
1567    if (name == NULL) {
1568	goto done;
1569    }
1570    match = server_name_matches_server_names(name, trusted_server_names);
1571
1572 done:
1573    my_CFRelease(&attrs);
1574    return (match);
1575}
1576
1577/*
1578 * Function: verify_server_certs
1579 * Purpose:
1580 *   If the trusted_server_names list is specified, verify that the server
1581 *   cert name matches.  Similarly, if the trusted_certs list is specified,
1582 *   make sure that one of the certs in the cert chain in the SecTrust object
1583 *   is in the trusted_certs list.
1584 * Notes:
1585 *   The assumption here is that TrustSettings are already in place, and
1586 *   we perform additional checks to validate the cert chain on top of that.
1587 */
1588static bool
1589verify_server_certs(SecTrustRef trust,
1590		    CFArrayRef server_certs,
1591		    CFArrayRef trusted_certs,
1592		    CFArrayRef trusted_server_names)
1593{
1594    if (trusted_server_names != NULL) {
1595	SecCertificateRef	cert;
1596
1597	cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, 0);
1598	if (server_cert_matches_server_names(cert, trusted_server_names)
1599	    == FALSE) {
1600	    return (FALSE);
1601	}
1602    }
1603    if (trusted_certs != NULL
1604	&& server_cert_chain_is_trusted(trust, trusted_certs) == FALSE) {
1605	return (FALSE);
1606    }
1607    return (TRUE);
1608}
1609
1610EAPClientStatus
1611EAPTLSVerifyServerCertificateChain(CFDictionaryRef properties,
1612				   CFArrayRef server_certs,
1613				   OSStatus * ret_status)
1614{
1615    bool		allow_trust_decisions;
1616    EAPClientStatus	client_status;
1617    int			count;
1618    bool		is_recoverable;
1619    SecPolicyRef	policy = NULL;
1620    CFStringRef		profileID;
1621    OSStatus		status = noErr;
1622    SecTrustRef		trust = NULL;
1623    SecTrustResultType 	trust_result;
1624    CFArrayRef		trusted_certs = NULL;
1625    CFArrayRef		trusted_server_names;
1626
1627    client_status = kEAPClientStatusInternalError;
1628
1629    /* don't bother verifying server's identity */
1630    if (my_CFDictionaryGetBooleanValue(properties,
1631				       kEAPClientPropTLSVerifyServerCertificate,
1632				       TRUE) == FALSE) {
1633	client_status = kEAPClientStatusOK;
1634    }
1635    else {
1636	CFArrayRef	proceed;
1637
1638	proceed = copy_user_trust_proceed_certs(properties);
1639	if (proceed != NULL
1640	    && CFEqual(proceed, server_certs)) {
1641	    /* user said it was OK to go */
1642	    client_status = kEAPClientStatusOK;
1643	}
1644	my_CFRelease(&proceed);
1645    }
1646    if (client_status == kEAPClientStatusOK) {
1647	goto done;
1648    }
1649    if (server_certs == NULL) {
1650	goto done;
1651    }
1652    count = CFArrayGetCount(server_certs);
1653    if (count == 0) {
1654	goto done;
1655    }
1656    profileID = CFDictionaryGetValue(properties, kEAPClientPropProfileID);
1657    trusted_certs = copy_cert_list(properties,
1658				   kEAPClientPropTLSTrustedCertificates);
1659    trusted_server_names = get_trusted_server_names(properties);
1660
1661    /*
1662     * Don't allow trust decisions by the user by default if either trusted
1663     * certs or trusted server names is specified. Trust decisions must be
1664     * explicitly enabled in that case using the
1665     * kEAPClientPropTLSAllowTrustDecisions property.
1666     */
1667    if (trusted_certs != NULL || trusted_server_names != NULL) {
1668	allow_trust_decisions = FALSE;
1669    }
1670    else {
1671	allow_trust_decisions = TRUE;
1672    }
1673    allow_trust_decisions
1674	= my_CFDictionaryGetBooleanValue(properties,
1675					 kEAPClientPropTLSAllowTrustDecisions,
1676					 allow_trust_decisions);
1677    client_status = kEAPClientStatusSecurityError;
1678    status = EAPSecPolicyCopy(&policy);
1679    if (status != noErr) {
1680	goto done;
1681    }
1682    status = SecTrustCreateWithCertificates(server_certs, policy, &trust);
1683    if (status != noErr) {
1684	EAPLOG_FL(LOG_NOTICE,
1685		  "SecTrustCreateWithCertificates failed, %s (%d)",
1686		  EAPSecurityErrorString(status), (int)status);
1687	goto done;
1688    }
1689    if (profileID != NULL && trusted_certs != NULL) {
1690	status = SecTrustSetAnchorCertificates(trust, trusted_certs);
1691	if (status != noErr) {
1692	    EAPLOG_FL(LOG_NOTICE,
1693		      "SecTrustSetAnchorCertificates failed, %s (%d)",
1694		      EAPSecurityErrorString(status), (int)status);
1695	    goto done;
1696	}
1697    }
1698    status = SecTrustEvaluate(trust, &trust_result);
1699    switch (status) {
1700    case noErr:
1701	break;
1702    case errSecNoDefaultKeychain:
1703	status = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
1704	if (status != noErr) {
1705	    EAPLOG_FL(LOG_NOTICE,
1706		      "SecKeychainSetPreferenceDomain failed, %s (%d)",
1707		      EAPSecurityErrorString(status), (int)status);
1708	    goto done;
1709	}
1710	status = SecTrustEvaluate(trust, &trust_result);
1711	if (status == noErr) {
1712	    break;
1713	}
1714	/* FALL THROUGH */
1715    default:
1716	EAPLOG_FL(LOG_NOTICE,
1717		  "SecTrustEvaluate failed, %s (%d)",
1718		  EAPSecurityErrorString(status), (int)status);
1719	goto done;
1720	break;
1721    }
1722    is_recoverable = FALSE;
1723    switch (trust_result) {
1724    case kSecTrustResultProceed:
1725	if (verify_server_certs(trust, server_certs, trusted_certs,
1726				trusted_server_names)) {
1727	    /* the chain and/or name is valid */
1728	    client_status = kEAPClientStatusOK;
1729	    break;
1730	}
1731	is_recoverable = TRUE;
1732	break;
1733    case kSecTrustResultUnspecified:
1734	if (profileID != NULL
1735	    && (trusted_certs != NULL || trusted_server_names != NULL)) {
1736	    /* still need to check server names */
1737	    if (trusted_server_names != NULL) {
1738		if (verify_server_certs(NULL, server_certs, NULL,
1739					trusted_server_names)) {
1740		    client_status = kEAPClientStatusOK;
1741		    break;
1742		}
1743	    }
1744	    else {
1745		client_status = kEAPClientStatusOK;
1746		break;
1747	    }
1748	}
1749	is_recoverable = TRUE;
1750	break;
1751    case kSecTrustResultRecoverableTrustFailure:
1752	is_recoverable = TRUE;
1753	break;
1754    case kSecTrustResultDeny:
1755    default:
1756	status = errSSLXCertChainInvalid;
1757	break;
1758    }
1759    if (is_recoverable) {
1760	if (allow_trust_decisions == FALSE) {
1761	    client_status = kEAPClientStatusServerCertificateNotTrusted;
1762	}
1763	else {
1764	    client_status = kEAPClientStatusUserInputRequired;
1765	}
1766    }
1767
1768 done:
1769    if (ret_status != NULL) {
1770	*ret_status = status;
1771    }
1772    my_CFRelease(&policy);
1773    my_CFRelease(&trust);
1774    my_CFRelease(&trusted_certs);
1775    return (client_status);
1776}
1777
1778#endif /* TARGET_OS_EMBEDDED */
1779
1780OSStatus
1781EAPTLSCopyIdentityTrustChain(SecIdentityRef sec_identity,
1782			     CFDictionaryRef properties,
1783			     CFArrayRef * ret_array)
1784{
1785    if (sec_identity != NULL) {
1786	return (EAPSecIdentityCreateTrustChain(sec_identity, ret_array));
1787    }
1788    if (properties != NULL) {
1789	EAPSecIdentityHandleRef		id_handle;
1790
1791	id_handle = CFDictionaryGetValue(properties,
1792					 kEAPClientPropTLSIdentityHandle);
1793	if (id_handle != NULL) {
1794	    return (EAPSecIdentityHandleCreateSecIdentityTrustChain(id_handle,
1795								    ret_array));
1796	}
1797    }
1798    *ret_array = NULL;
1799    return (errSecParam);
1800}
1801
1802
1803#if defined(TEST_TRUST_EXCEPTIONS) || defined(TEST_EAPTLSVerifyServerCertificateChain)
1804
1805#include <sys/types.h>
1806#include <sys/uio.h>
1807#include <unistd.h>
1808#include <fcntl.h>
1809#include <sys/stat.h>
1810
1811static CFDataRef
1812file_create_data(const char * filename)
1813{
1814    CFMutableDataRef	data = NULL;
1815    size_t		len = 0;
1816    int			fd = -1;
1817    struct stat		sb;
1818
1819    if (stat(filename, &sb) < 0) {
1820	goto done;
1821    }
1822    len = sb.st_size;
1823    if (len == 0) {
1824	goto done;
1825    }
1826    data = CFDataCreateMutable(NULL, len);
1827    if (data == NULL) {
1828	goto done;
1829    }
1830    fd = open(filename, O_RDONLY);
1831    if (fd < 0) {
1832	goto done;
1833    }
1834    CFDataSetLength(data, len);
1835    if (read(fd, CFDataGetMutableBytePtr(data), len) != len) {
1836	goto done;
1837    }
1838 done:
1839    if (fd >= 0) {
1840	close(fd);
1841    }
1842    return (data);
1843}
1844
1845static SecCertificateRef
1846file_create_certificate(const char  * filename)
1847{
1848    CFDataRef		data;
1849    SecCertificateRef	cert;
1850
1851    data = file_create_data(filename);
1852    if (data == NULL) {
1853	return (NULL);
1854    }
1855    cert = SecCertificateCreateWithData(NULL, data);
1856    CFRelease(data);
1857    return (cert);
1858}
1859#endif /* defined(TEST_TRUST_EXCEPTIONS) || defined(TEST_EAPTLSVerifyServerCertificateChain) */
1860
1861#ifdef TEST_TRUST_EXCEPTIONS
1862#if TARGET_OS_EMBEDDED
1863#include <SystemConfiguration/SCPrivate.h>
1864
1865static void
1866usage(const char * progname)
1867{
1868    fprintf(stderr, "usage:\n%s get <domain> <identifier> <cert-file>\n",
1869	    progname);
1870    fprintf(stderr, "%s remove_all <domain> <identifier>\n", progname);
1871    exit(1);
1872    return;
1873}
1874enum {
1875    kCommandGet,
1876    kCommandRemoveAll
1877};
1878
1879static CFStringRef
1880file_create_certificate_hash(const char * filename)
1881{
1882    SecCertificateRef	cert;
1883    CFStringRef		str;
1884
1885    cert = file_create_certificate(filename);
1886    if (cert == NULL) {
1887	return (NULL);
1888    }
1889    str = EAPSecCertificateCopySHA1DigestString(cert);
1890    CFRelease(cert);
1891    return (str);
1892}
1893
1894static void
1895getTrustExceptions(char * domain, char * identifier, char * cert_file)
1896{
1897    CFStringRef		domain_cf;
1898    CFDataRef		exceptions;
1899    CFStringRef 	identifier_cf;
1900    CFStringRef		cert_hash;
1901
1902    domain_cf
1903	= CFStringCreateWithCStringNoCopy(NULL,
1904					  domain,
1905					  kCFStringEncodingUTF8,
1906					  kCFAllocatorNull);
1907    identifier_cf
1908	= CFStringCreateWithCStringNoCopy(NULL,
1909					  identifier,
1910					  kCFStringEncodingUTF8,
1911					  kCFAllocatorNull);
1912    cert_hash = file_create_certificate_hash(cert_file);
1913    if (cert_hash == NULL) {
1914	fprintf(stderr, "error reading certificate file '%s', %s\n",
1915		cert_file, strerror(errno));
1916	exit(1);
1917    }
1918    exceptions = EAPTLSTrustExceptionsCopy(domain_cf,
1919					   identifier_cf,
1920					   cert_hash);
1921    if (exceptions != NULL) {
1922	SCPrint(TRUE, stdout,
1923		CFSTR("Exceptions for %@/%@/%@ are defined:\n%@\n"),
1924		domain_cf, identifier_cf, cert_hash,
1925		exceptions);
1926    }
1927    else {
1928	SCPrint(TRUE, stdout,
1929		CFSTR("No exceptions for %@/%@/%@ are defined\n"),
1930		domain_cf, identifier_cf, cert_hash);
1931    }
1932    return;
1933}
1934
1935static void
1936removeAllTrustExceptions(char * domain, char * identifier)
1937{
1938    CFStringRef	domain_cf;
1939    CFStringRef identifier_cf;
1940
1941    domain_cf
1942	= CFStringCreateWithCStringNoCopy(NULL,
1943					  domain,
1944					  kCFStringEncodingUTF8,
1945					  kCFAllocatorNull);
1946    identifier_cf
1947	= CFStringCreateWithCStringNoCopy(NULL,
1948					  identifier,
1949					  kCFStringEncodingUTF8,
1950					  kCFAllocatorNull);
1951    EAPTLSRemoveTrustExceptionsBindings(domain_cf, identifier_cf);
1952    CFRelease(domain_cf);
1953    CFRelease(identifier_cf);
1954    return;
1955}
1956
1957int
1958main(int argc, char * argv[])
1959{
1960    int		command;
1961
1962    if (argc < 2) {
1963	usage(argv[0]);
1964    }
1965    if (strcmp(argv[1], "get") == 0) {
1966	command = kCommandGet;
1967    }
1968    else if (strcmp(argv[1], "remove_all") == 0) {
1969	command = kCommandRemoveAll;
1970    }
1971    else {
1972	usage(argv[0]);
1973    }
1974    switch (command) {
1975    case kCommandGet:
1976	if (argc < 5) {
1977	    usage(argv[0]);
1978	}
1979	getTrustExceptions(argv[2], argv[3], argv[4]);
1980	break;
1981    case kCommandRemoveAll:
1982	if (argc < 4) {
1983	    usage(argv[0]);
1984	}
1985	removeAllTrustExceptions(argv[2], argv[3]);
1986	break;
1987    }
1988    exit(0);
1989    return (0);
1990}
1991
1992#else /* TARGET_OS_EMBEDDED */
1993
1994#error "TrustExceptions are only available with TARGET_OS_EMBEDDED"
1995#endif /* TARGET_OS_EMBEDDED */
1996#endif /* TEST_TRUST_EXCEPTIONS */
1997
1998#ifdef TEST_SEC_TRUST
1999
2000#if TARGET_OS_EMBEDDED
2001#include <sys/types.h>
2002#include <sys/uio.h>
2003#include <unistd.h>
2004#include <fcntl.h>
2005#include <sys/stat.h>
2006#include <SystemConfiguration/SCPrivate.h>
2007
2008static CFDictionaryRef
2009read_dictionary(const char * filename)
2010{
2011    CFDictionaryRef dict;
2012
2013    dict = my_CFPropertyListCreateFromFile(filename);
2014    if (dict != NULL) {
2015	if (isA_CFDictionary(dict) == NULL) {
2016	    CFRelease(dict);
2017	    dict = NULL;
2018	}
2019    }
2020    return (dict);
2021}
2022
2023static CFArrayRef
2024read_array(const char * filename)
2025{
2026    CFArrayRef array;
2027
2028    array = my_CFPropertyListCreateFromFile(filename);
2029    if (array != NULL) {
2030	if (isA_CFArray(array) == NULL) {
2031	    CFRelease(array);
2032	    array = NULL;
2033	}
2034    }
2035    return (array);
2036}
2037
2038static void
2039usage(const char * progname)
2040{
2041    fprintf(stderr, "usage: %s <domain> <identifier> <config> <certs>\n",
2042	    progname);
2043    exit(1);
2044    return;
2045}
2046
2047int
2048main(int argc, char * argv[])
2049{
2050    CFArrayRef		certs;
2051    CFArrayRef		certs_data;
2052    CFDictionaryRef	config;
2053    const char *	domain;
2054    CFStringRef		domain_cf;
2055    const char *	identifier;
2056    CFStringRef 	identifier_cf;
2057    const char *	result_str;
2058    OSStatus		status;
2059    SecTrustRef		trust;
2060    SecTrustResultType 	trust_result;
2061
2062    if (argc < 5) {
2063	usage(argv[0]);
2064    }
2065    domain = argv[1];
2066    identifier = argv[2];
2067    config = read_dictionary(argv[3]);
2068    if (config == NULL) {
2069	fprintf(stderr, "failed to load '%s'\n",
2070		argv[3]);
2071	exit(1);
2072    }
2073    certs_data = read_array(argv[4]);
2074    if (certs_data == NULL) {
2075	fprintf(stderr, "failed to load '%s'\n",
2076		argv[4]);
2077	exit(1);
2078    }
2079    certs = EAPCFDataArrayCreateSecCertificateArray(certs_data);
2080    if (certs == NULL) {
2081	fprintf(stderr,
2082		"the file '%s' does not contain a certificate array data\n",
2083		argv[4]);
2084	exit(1);
2085    }
2086    domain_cf
2087	= CFStringCreateWithCStringNoCopy(NULL,
2088					  domain,
2089					  kCFStringEncodingUTF8,
2090					  kCFAllocatorNull);
2091    identifier_cf
2092	= CFStringCreateWithCStringNoCopy(NULL,
2093					  identifier,
2094					  kCFStringEncodingUTF8,
2095					  kCFAllocatorNull);
2096    trust = EAPTLSCreateSecTrust(config, certs, domain_cf, identifier_cf);
2097    if (trust == NULL) {
2098	fprintf(stderr, "EAPTLSCreateSecTrustFailed failed\n");
2099	exit(1);
2100    }
2101    status = SecTrustEvaluate(trust, &trust_result);
2102    if (status != noErr) {
2103	fprintf(stderr, "SecTrustEvaluate failed, %s (%d)",
2104		EAPSecurityErrorString(status), (int)status);
2105	exit(1);
2106    }
2107    switch (trust_result) {
2108    case kSecTrustResultProceed:
2109	result_str = "Proceed";
2110	break;
2111    case kSecTrustResultUnspecified:
2112	result_str = "Unspecified";
2113	break;
2114    case kSecTrustResultRecoverableTrustFailure:
2115	result_str = "RecoverableTrustFailure";
2116	break;
2117    case kSecTrustResultDeny:
2118	result_str = "Deny";
2119	break;
2120    default:
2121	result_str = "<unknown>";
2122	break;
2123    }
2124    printf("Trust result is %s\n", result_str);
2125    CFRelease(domain_cf);
2126    CFRelease(identifier_cf);
2127    CFRelease(certs_data);
2128    CFRelease(certs);
2129    CFRelease(trust);
2130    exit(0);
2131    return (0);
2132}
2133
2134#else /* TARGET_OS_EMBEDDED */
2135
2136#error "SecTrust test is only available with TARGET_OS_EMBEDDED"
2137#endif /* TARGET_OS_EMBEDDED */
2138#endif /* TEST_SEC_TRUST */
2139
2140#ifdef TEST_SERVER_NAMES
2141#if TARGET_OS_EMBEDDED
2142#error "Can't test server names with TARGET_OS_EMBEDDED"
2143#else /* TARGET_OS_EMBEDDED */
2144
2145#include <SystemConfiguration/SCPrivate.h>
2146
2147int
2148main()
2149{
2150    const void *	name_list[] = {
2151	CFSTR("siegdi.apple.com"),
2152	CFSTR("radius1.testing123.org"),
2153	CFSTR("apple.com"),
2154	CFSTR("radius1.foo.bar.nellie.joe.edu"),
2155	NULL
2156    };
2157    int			i;
2158    CFStringRef		match1 = CFSTR("*.apple.com");
2159    CFStringRef		match2 = CFSTR("*.testing123.org");
2160    CFStringRef		match3 = CFSTR("*.foo.bar.nellie.joe.edu");
2161    CFStringRef		match4 = CFSTR("apple.com");
2162    const void *	vlist[3] = { match1, match2, match4 };
2163    CFArrayRef		list[3] = { NULL, NULL, NULL };
2164
2165    list[0] = CFArrayCreate(NULL, (const void **)&match1,
2166			    1, &kCFTypeArrayCallBacks);
2167    list[1] = CFArrayCreate(NULL, vlist, 3, &kCFTypeArrayCallBacks);
2168    list[2] = CFArrayCreate(NULL, (const void **)&match3, 1,
2169			    &kCFTypeArrayCallBacks);
2170    SCPrint(TRUE, stdout, CFSTR("list[0] = %@\n"), list[0]);
2171    SCPrint(TRUE, stdout, CFSTR("list[1] = %@\n"), list[1]);
2172    SCPrint(TRUE, stdout, CFSTR("list[2] = %@\n"), list[2]);
2173    for (i = 0; name_list[i] != NULL; i++) {
2174	int	j;
2175	for (j = 0; j < 3; j++) {
2176	    if (server_name_matches_server_names(name_list[i],
2177						 list[j])) {
2178		SCPrint(TRUE, stdout, CFSTR("%@ matches list[%d]\n"),
2179			name_list[i], j);
2180	    }
2181	    else {
2182		SCPrint(TRUE, stdout, CFSTR("%@ does not match list[%d]\n"),
2183			name_list[i], j);
2184	    }
2185	}
2186    }
2187    exit(0);
2188    return (0);
2189}
2190
2191#endif /* TARGET_OS_EMBEDDED */
2192#endif /* TEST_SERVER_NAMES */
2193
2194#ifdef TEST_EAPTLSVerifyServerCertificateChain
2195
2196static CFDictionaryRef
2197read_dictionary(const char * filename)
2198{
2199    CFDictionaryRef dict;
2200
2201    dict = my_CFPropertyListCreateFromFile(filename);
2202    if (dict != NULL) {
2203	if (isA_CFDictionary(dict) == NULL) {
2204	    CFRelease(dict);
2205	    dict = NULL;
2206	}
2207    }
2208    return (dict);
2209}
2210
2211int
2212main(int argc, char * argv[])
2213{
2214    CFArrayRef		array;
2215    CFDictionaryRef	properties;
2216    OSStatus		sec_status;
2217    SecCertificateRef	server_cert;
2218    EAPClientStatus	status;
2219
2220    if (argc < 3) {
2221	fprintf(stderr, "usage: verify_server <cert-file> <properties>\n");
2222	exit(1);
2223    }
2224    server_cert = file_create_certificate(argv[1]);
2225    if (server_cert == NULL) {
2226	fprintf(stderr, "failed to load cert file\n");
2227	exit(2);
2228    }
2229    properties = read_dictionary(argv[2]);
2230    if (properties == NULL) {
2231	fprintf(stderr, "failed to load properties\n");
2232	exit(2);
2233    }
2234    array = CFArrayCreate(NULL, (const void **)&server_cert, 1,
2235			  &kCFTypeArrayCallBacks);
2236
2237    status = EAPTLSVerifyServerCertificateChain(properties,
2238						array,
2239						&sec_status);
2240    printf("status is %d, sec status is %d\n",
2241	   status, sec_status);
2242    exit(0);
2243}
2244#endif /* TEST_EAPTLSVerifyServerCertificateChain */
2245