1/*
2 * Copyright (c) 2004 Apple Computer, 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#import "Controller.h"
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <unistd.h>
29#import <SecurityInterface/SFCertificatePanel.h>
30#import <SecurityInterface/SFCertificateTrustPanel.h>
31#import <Security/SecTrust.h>
32#import <Security/SecureTransport.h>
33#include <Security/oidsalg.h>
34#include <Security/SecPolicySearch.h>
35#include <Security/SecPolicy.h>
36#include <SystemConfiguration/SCValidation.h>
37#include <AssertMacros.h>
38
39#define kSSLClientPropTLSServerCertificateChain CFSTR("TLSServerCertificateChain") /* array[data] */
40#define kSSLClientPropTLSTrustClientStatus	CFSTR("TLSTrustClientStatus") /* CFNumberRef of kCFNumberSInt32Type (errSSLxxxx) */
41#define kSSLClientPropTLSServerHostName	CFSTR("TLSServerHostName") /* CFString */
42
43#define LOCSTRING(x) [[NSBundle mainBundle] localizedStringForKey: x value: x table: nil]
44
45/*
46 * Function: add_certs_to_keychain
47 * Purpose:
48 *   Adds an array of certificates to the default keychain.
49 */
50static void
51add_certs_to_keychain(CFArrayRef cert_list)
52{
53	CFIndex count;
54	int i;
55
56	count = CFArrayGetCount(cert_list);
57	for (i = 0; i < count; ++i)
58	{
59		SecCertificateRef cert;
60
61		cert = (SecCertificateRef)CFArrayGetValueAtIndex(cert_list, i);
62		SecCertificateAddToKeychain(cert, NULL);
63	}
64
65	return;
66}
67
68
69/*
70 * Function: SSLSecPolicyCopy
71 * Purpose:
72 *   Returns a copy of the SSL policy.
73 */
74static OSStatus
75SSLSecPolicyCopy(SecPolicyRef *ret_policy)
76{
77	SecPolicyRef policy;
78	SecPolicySearchRef policy_search;
79	OSStatus status;
80
81	*ret_policy = NULL;
82	status = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_SSL, NULL, &policy_search);
83	require_noerr(status, SecPolicySearchCreate);
84
85	status = SecPolicySearchCopyNext(policy_search, &policy);
86	require_noerr(status, SecPolicySearchCopyNext);
87
88	*ret_policy = policy;
89
90SecPolicySearchCopyNext:
91
92	CFRelease(policy_search);
93
94SecPolicySearchCreate:
95
96	return (status);
97}
98
99
100/*
101 * Function: CFDataCreateSecCertificate
102 * Purpose:
103 *   Creates a SecCertificateRef from a CFDataRef.
104 */
105static SecCertificateRef
106CFDataCreateSecCertificate(CFDataRef data_cf)
107{
108	SecCertificateRef cert;
109	CSSM_DATA data;
110	OSStatus status;
111
112	cert = NULL;
113	require(data_cf != NULL, bad_input);
114
115	data.Length = CFDataGetLength(data_cf);
116	data.Data = (uint8 *)CFDataGetBytePtr(data_cf);
117	status = SecCertificateCreateFromData(&data, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &cert);
118	check_noerr(status);
119
120bad_input:
121
122	return (cert);
123}
124
125
126/*
127 * Function: CFDataArrayCreateSecCertificateArray
128 * Purpose:
129 *   Convert a CFArray[CFData] to CFArray[SecCertificate].
130 */
131static CFArrayRef
132CFDataArrayCreateSecCertificateArray(CFArrayRef certs)
133{
134    CFMutableArrayRef array;
135    CFIndex count;
136    int i;
137
138	count = CFArrayGetCount(certs);
139    array = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks);
140	require(array != NULL, CFArrayCreateMutable);
141
142	for (i = 0; i < count; ++i)
143	{
144		SecCertificateRef cert;
145		CFDataRef data;
146
147		data = isA_CFData((CFDataRef)CFArrayGetValueAtIndex(certs, i));
148		require(data != NULL, isA_CFData);
149
150		cert = CFDataCreateSecCertificate(data);
151		require(cert != NULL, CFDataCreateSecCertificate);
152
153		CFArrayAppendValue(array, cert);
154		CFRelease(cert);
155	}
156
157	return (array);
158
159	/* error cases handled here */
160
161CFDataCreateSecCertificate:
162isA_CFData:
163
164    CFRelease(array);
165
166CFArrayCreateMutable:
167
168    return (NULL);
169}
170
171
172/*
173 * Function: show_cert_trust_panel
174 * Purpose:
175 *   Displays the modal cert panel.
176 */
177static int
178show_cert_trust_panel(CFArrayRef cert_list, SInt32 trust_status, CFStringRef host_name)
179{
180	int exit_code;
181	SFCertificateTrustPanel *panel;
182	SecPolicyRef policy;
183	int ret_val;
184	OSStatus status;
185	NSString *text;
186	SecTrustRef trust;
187	SecTrustResultType trust_result;
188
189	status = SSLSecPolicyCopy(&policy);
190	require_noerr_action(status, SSLSecPolicyCopy, exit_code = 2);
191
192	status = SecTrustCreateWithCertificates(cert_list, policy, &trust);
193	require_noerr_action(status, SecTrustCreateWithCertificates, exit_code = 2);
194
195	(void)SecTrustEvaluate(trust, &trust_result);
196	panel = [[SFCertificateTrustPanel alloc] init];
197
198	switch (trust_status)
199	{
200		case errSSLCertExpired:
201			text = @"MESSAGE_CERT_EXPIRED";
202			break;
203
204		case errSSLUnknownRootCert:
205		case errSSLNoRootCert:
206			text = @"MESSAGE_CERT_INVALID";
207			break;
208
209		case errSSLCertNotYetValid:
210			text = @"MESSAGE_CERT_NOT_YET_VALID";
211			break;
212
213		case errSSLBadCert:
214		case errSSLXCertChainInvalid:
215		case errSSLHostNameMismatch:
216		default:
217			text = @"MESSAGE_CERT_UNKNOWN_AUTHORITY";
218			break;
219	}
220	text = LOCSTRING(text);
221
222	/* insert host_name into the text string */
223	text = [NSString stringWithFormat: text, host_name];
224
225	if ([panel respondsToSelector:@selector(setPolicies:)])
226	{
227		[panel setPolicies:(id)policy];
228	}
229	if ([panel respondsToSelector:@selector(setDefaultButtonTitle:)])
230	{
231		[panel setDefaultButtonTitle:LOCSTRING(@"MESSAGE_CERT_CONTINUE")];
232	}
233	if ([panel respondsToSelector:@selector(setAlternateButtonTitle:)])
234	{
235		[panel setAlternateButtonTitle:LOCSTRING(@"MESSAGE_CERT_CANCEL")];
236	}
237	if ([panel respondsToSelector:@selector(setShowsHelp:)])
238	{
239		[panel setShowsHelp:YES];
240	}
241
242	[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
243
244	ret_val = (int)[panel runModalForTrust:trust message:text];
245
246	switch (ret_val)
247	{
248		case NSOKButton:
249			exit_code = 0;
250			add_certs_to_keychain(cert_list);
251			break;
252
253		case NSCancelButton:
254		default:
255			exit_code = 1;
256			break;
257	}
258
259	[panel release];
260
261SecTrustCreateWithCertificates:
262
263	CFRelease(policy);
264
265SSLSecPolicyCopy:
266
267	exit(exit_code);
268}
269
270
271extern CFDictionaryRef the_dict;
272
273
274@implementation Controller
275- (void)awakeFromNib
276{
277	CFArrayRef cert_data_list;
278	CFArrayRef cert_list;
279	CFStringRef host_name;
280	CFNumberRef trust_status_cf;
281	SInt32 trust_status;
282	int error;
283
284	cert_data_list = CFDictionaryGetValue(the_dict, kSSLClientPropTLSServerCertificateChain);
285	require_action(isA_CFArray(cert_data_list) != NULL, isA_CFArray, error = 2);
286
287	cert_list = CFDataArrayCreateSecCertificateArray(cert_data_list);
288	require_action(cert_list != NULL, CFDataArrayCreateSecCertificateArray, error = 2);
289
290	trust_status = errSSLUnknownRootCert;
291	trust_status_cf = CFDictionaryGetValue(the_dict, kSSLClientPropTLSTrustClientStatus);
292	if (isA_CFNumber(trust_status_cf) != NULL)
293	{
294		CFNumberGetValue(trust_status_cf, kCFNumberSInt32Type, &trust_status);
295	}
296
297	host_name = CFDictionaryGetValue(the_dict, kSSLClientPropTLSServerHostName);
298	if ( host_name == NULL )
299	{
300		/* this should not happen, but just in case */
301		host_name = CFSTR("unknown");
302	}
303
304	error = show_cert_trust_panel(cert_list, trust_status, host_name);
305	require_noerr(error, show_cert_trust_panel);
306
307	return;
308
309	/* error cases handled here */
310
311show_cert_trust_panel:
312CFDataArrayCreateSecCertificateArray:
313isA_CFArray:
314
315	exit(error);
316}
317@end
318