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