1/* 2 * Copyright (c) 2006 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 * verify_cert.c 24 */ 25 26#include <Security/SecTrust.h> 27#include <Security/SecKeychain.h> 28#include <Security/SecPolicy.h> 29#include <Security/SecPolicySearch.h> 30#include <Security/cssmapple.h> 31#include <Security/oidsalg.h> 32#include <stdlib.h> 33#include <unistd.h> 34#include "trusted_cert_utils.h" 35 36/* 37 * Read file as a cert, add to a CFArray, creating the array if necessary 38 */ 39static int addCertFile( 40 const char *fileName, 41 CFMutableArrayRef *array) 42{ 43 SecCertificateRef certRef; 44 45 if(readCertFile(fileName, &certRef)) { 46 return -1; 47 } 48 if(*array == NULL) { 49 *array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 50 } 51 CFArrayAppendValue(*array, certRef); 52 CFRelease(certRef); 53 return 0; 54} 55 56int 57verify_cert(int argc, char * const *argv) 58{ 59 extern char *optarg; 60 extern int optind; 61 OSStatus ortn; 62 int arg; 63 CFMutableArrayRef certs = NULL; 64 CFMutableArrayRef roots = NULL; 65 CFMutableArrayRef keychains = NULL; 66 const CSSM_OID *policy = &CSSMOID_APPLE_X509_BASIC; 67 SecKeychainRef kcRef = NULL; 68 int ourRtn = 0; 69 bool quiet = false; 70 SecPolicyRef policyRef = NULL; 71 SecTrustRef trustRef = NULL; 72 SecPolicySearchRef searchRef = NULL; 73 const char *emailAddrs = NULL; 74 const char *sslHost = NULL; 75 CSSM_APPLE_TP_SSL_OPTIONS sslOpts; 76 CSSM_APPLE_TP_SMIME_OPTIONS smimeOpts; 77 CSSM_APPLE_TP_ACTION_FLAGS actionFlags = 0; 78 bool forceActionFlags = false; 79 CSSM_APPLE_TP_ACTION_DATA actionData; 80 CSSM_DATA optionData; 81 CFDataRef cfActionData = NULL; 82 SecTrustResultType resultType; 83 OSStatus ocrtn; 84 85 if(argc < 2) { 86 return 2; /* @@@ Return 2 triggers usage message. */ 87 } 88 /* permit network cert fetch unless explicitly turned off with '-L' */ 89 actionFlags |= CSSM_TP_ACTION_FETCH_CERT_FROM_NET; 90 optind = 1; 91 while ((arg = getopt(argc, argv, "c:r:p:k:e:s:Llnq")) != -1) { 92 switch (arg) { 93 case 'c': 94 /* this can be specified multiple times */ 95 if(addCertFile(optarg, &certs)) { 96 ourRtn = 1; 97 goto errOut; 98 } 99 break; 100 case 'r': 101 /* this can be specified multiple times */ 102 if(addCertFile(optarg, &roots)) { 103 ourRtn = 1; 104 goto errOut; 105 } 106 break; 107 case 'p': 108 policy = policyStringToOid(optarg); 109 if(policy == NULL) { 110 ourRtn = 2; 111 goto errOut; 112 } 113 break; 114 case 'k': 115 ortn = SecKeychainOpen(optarg, &kcRef); 116 if(ortn) { 117 cssmPerror("SecKeychainOpen", ortn); 118 ourRtn = 1; 119 goto errOut; 120 } 121 /* this can be specified multiple times */ 122 if(keychains == NULL) { 123 keychains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 124 } 125 CFArrayAppendValue(keychains, kcRef); 126 CFRelease(kcRef); 127 break; 128 case 'L': 129 actionFlags &= ~CSSM_TP_ACTION_FETCH_CERT_FROM_NET; 130 forceActionFlags = true; 131 break; 132 case 'l': 133 actionFlags |= CSSM_TP_ACTION_LEAF_IS_CA; 134 break; 135 case 'n': 136 /* No keychains, signalled by empty keychain array */ 137 if(keychains != NULL) { 138 fprintf(stderr, "-k and -n are mutually exclusive\n"); 139 ourRtn = 2; 140 goto errOut; 141 } 142 keychains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 143 break; 144 case 'e': 145 emailAddrs = optarg; 146 break; 147 case 's': 148 sslHost = optarg; 149 break; 150 case 'q': 151 quiet = true; 152 break; 153 default: 154 ourRtn = 2; 155 goto errOut; 156 } 157 } 158 if(optind != argc) { 159 ourRtn = 2; 160 goto errOut; 161 } 162 163 if(certs == NULL) { 164 if(roots == NULL) { 165 fprintf(stderr, "***No certs specified.\n"); 166 ourRtn = 2; 167 goto errOut; 168 } 169 if(CFArrayGetCount(roots) != 1) { 170 fprintf(stderr, "***Multiple roots and no certs not allowed.\n"); 171 ourRtn = 2; 172 goto errOut; 173 } 174 175 /* no certs and one root: verify the root */ 176 certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 177 CFArrayAppendValue(certs, CFArrayGetValueAtIndex(roots, 0)); 178 actionFlags |= CSSM_TP_ACTION_LEAF_IS_CA; 179 } 180 181 /* cook up a SecPolicyRef */ 182 ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3, 183 policy, 184 NULL, // policy opts 185 &searchRef); 186 if(ortn) { 187 cssmPerror("SecPolicySearchCreate", ortn); 188 ourRtn = 1; 189 goto errOut; 190 } 191 ortn = SecPolicySearchCopyNext(searchRef, &policyRef); 192 if(ortn) { 193 cssmPerror("SecPolicySearchCopyNext", ortn); 194 ourRtn = 1; 195 goto errOut; 196 } 197 198 /* per-policy options */ 199 if(compareOids(policy, &CSSMOID_APPLE_TP_SSL) || compareOids(policy, &CSSMOID_APPLE_TP_APPLEID_SHARING)) { 200 if(sslHost != NULL) { 201 memset(&sslOpts, 0, sizeof(sslOpts)); 202 sslOpts.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION; 203 sslOpts.ServerName = sslHost; 204 sslOpts.ServerNameLen = strlen(sslHost); 205 optionData.Data = (uint8 *)&sslOpts; 206 optionData.Length = sizeof(sslOpts); 207 ortn = SecPolicySetValue(policyRef, &optionData); 208 if(ortn) { 209 cssmPerror("SecPolicySetValue", ortn); 210 ourRtn = 1; 211 goto errOut; 212 } 213 } 214 } 215 if(compareOids(policy, &CSSMOID_APPLE_TP_SMIME)) { 216 if(emailAddrs != NULL) { 217 memset(&smimeOpts, 0, sizeof(smimeOpts)); 218 smimeOpts.Version = CSSM_APPLE_TP_SMIME_OPTS_VERSION; 219 smimeOpts.SenderEmail = emailAddrs; 220 smimeOpts.SenderEmailLen = strlen(emailAddrs); 221 optionData.Data = (uint8 *)&smimeOpts; 222 optionData.Length = sizeof(smimeOpts); 223 ortn = SecPolicySetValue(policyRef, &optionData); 224 if(ortn) { 225 cssmPerror("SecPolicySetValue", ortn); 226 ourRtn = 1; 227 goto errOut; 228 } 229 } 230 } 231 232 /* Now create a SecTrustRef and set its options */ 233 ortn = SecTrustCreateWithCertificates(certs, policyRef, &trustRef); 234 if(ortn) { 235 cssmPerror("SecTrustCreateWithCertificates", ortn); 236 ourRtn = 1; 237 goto errOut; 238 } 239 240 /* roots (anchors) are optional */ 241 if(roots != NULL) { 242 ortn = SecTrustSetAnchorCertificates(trustRef, roots); 243 if(ortn) { 244 cssmPerror("SecTrustSetAnchorCertificates", ortn); 245 ourRtn = 1; 246 goto errOut; 247 } 248 } 249 if(actionFlags || forceActionFlags) { 250 memset(&actionData, 0, sizeof(actionData)); 251 actionData.Version = CSSM_APPLE_TP_ACTION_VERSION; 252 actionData.ActionFlags = actionFlags; 253 cfActionData = CFDataCreate(NULL, (UInt8 *)&actionData, sizeof(actionData)); 254 ortn = SecTrustSetParameters(trustRef, CSSM_TP_ACTION_DEFAULT, cfActionData); 255 if(ortn) { 256 cssmPerror("SecTrustSetParameters", ortn); 257 ourRtn = 1; 258 goto errOut; 259 } 260 } 261 if(keychains) { 262 ortn = SecTrustSetKeychains(trustRef, keychains); 263 if(ortn) { 264 cssmPerror("SecTrustSetKeychains", ortn); 265 ourRtn = 1; 266 goto errOut; 267 } 268 } 269 270 /* GO */ 271 ortn = SecTrustEvaluate(trustRef, &resultType); 272 if(ortn) { 273 /* should never fail - error on this doesn't mean the cert verified badly */ 274 cssmPerror("SecTrustEvaluate", ortn); 275 ourRtn = 1; 276 goto errOut; 277 } 278 switch(resultType) { 279 case kSecTrustResultUnspecified: 280 /* cert chain valid, no special UserTrust assignments */ 281 case kSecTrustResultProceed: 282 /* cert chain valid AND user explicitly trusts this */ 283 break; 284 case kSecTrustResultDeny: 285 if(!quiet) { 286 fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultDeny\n"); 287 } 288 ourRtn = 1; 289 break; 290 case kSecTrustResultConfirm: 291 /* 292 * Cert chain may well have verified OK, but user has flagged 293 * one of these certs as untrustable. 294 */ 295 if(!quiet) { 296 fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultConfirm\n"); 297 } 298 ourRtn = 1; 299 break; 300 default: 301 ourRtn = 1; 302 if(!quiet) { 303 /* See what the TP had to say about this */ 304 ortn = SecTrustGetCssmResultCode(trustRef, &ocrtn); 305 if(ortn) { 306 cssmPerror("SecTrustGetCssmResultCode", ortn); 307 } 308 else { 309 cssmPerror("Cert Verify Result", ocrtn); 310 } 311 } 312 break; 313 } 314 315 if((ourRtn == 0) & !quiet) { 316 printf("...certificate verification successful.\n"); 317 } 318errOut: 319 /* cleanup */ 320 CFRELEASE(certs); 321 CFRELEASE(roots); 322 CFRELEASE(keychains); 323 CFRELEASE(policyRef); 324 CFRELEASE(trustRef); 325 CFRELEASE(searchRef); 326 CFRELEASE(cfActionData); 327 return ourRtn; 328} 329