verify_cert.c
1 /* 2 * Copyright (c) 2006,2010,2012,2014-2019 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 * 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 <sys/stat.h> 35 #include <time.h> 36 #include "trusted_cert_ssl.h" 37 #include "trusted_cert_utils.h" 38 #include "verify_cert.h" 39 #include <utilities/SecCFRelease.h> 40 #include "security_tool.h" 41 42 /* 43 * Read file as a cert, add to a CFArray, creating the array if necessary 44 */ 45 static int addCertFile( 46 const char *fileName, 47 CFMutableArrayRef *array) 48 { 49 SecCertificateRef certRef; 50 51 if(readCertFile(fileName, &certRef)) { 52 return -1; 53 } 54 if(*array == NULL) { 55 *array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 56 } 57 CFArrayAppendValue(*array, certRef); 58 CFRelease(certRef); 59 return 0; 60 } 61 62 int 63 verify_cert(int argc, char * const *argv) 64 { 65 extern char *optarg; 66 extern int optind; 67 OSStatus ortn; 68 int arg; 69 CFMutableArrayRef certs = NULL; 70 CFMutableArrayRef roots = NULL; 71 CFMutableArrayRef keychains = NULL; 72 CFMutableArrayRef policies = NULL; 73 CFMutableDictionaryRef properties = NULL; 74 const CSSM_OID *policy = &CSSMOID_APPLE_X509_BASIC; 75 SecKeychainRef kcRef = NULL; 76 int ourRtn = 0; 77 bool quiet = false; 78 bool client = false; 79 bool useTLS = false; 80 bool printPem = false; 81 bool printText = false; 82 bool printDetails = false; 83 int verbose = 0; 84 SecPolicyRef policyRef = NULL; 85 SecPolicyRef revPolicyRef = NULL; 86 SecTrustRef trustRef = NULL; 87 SecPolicySearchRef searchRef = NULL; 88 CFErrorRef errorRef = NULL; 89 const char *emailAddrs = NULL; 90 const char *sslHost = NULL; 91 const char *name = NULL; 92 const char *url = NULL; 93 CSSM_APPLE_TP_ACTION_FLAGS actionFlags = 0; 94 bool forceActionFlags = false; 95 CSSM_APPLE_TP_ACTION_DATA actionData; 96 CFDataRef cfActionData = NULL; 97 SecTrustResultType resultType; 98 OSStatus ocrtn; 99 struct tm time; 100 CFGregorianDate gregorianDate; 101 CFDateRef dateRef = NULL; 102 CFOptionFlags revOptions = 0; 103 104 if(argc < 2) { 105 return SHOW_USAGE_MESSAGE; 106 } 107 /* permit network cert fetch unless explicitly turned off with '-L' */ 108 actionFlags |= CSSM_TP_ACTION_FETCH_CERT_FROM_NET; 109 optind = 1; 110 while ((arg = getopt(argc, argv, "Cc:r:p:k:e:s:d:LlNnPqR:tv")) != -1) { 111 switch (arg) { 112 case 'C': 113 client = true; 114 break; 115 case 'c': 116 /* this can be specified multiple times */ 117 if(addCertFile(optarg, &certs)) { 118 ourRtn = 1; 119 goto errOut; 120 } 121 break; 122 case 'r': 123 /* this can be specified multiple times */ 124 if(addCertFile(optarg, &roots)) { 125 ourRtn = 1; 126 goto errOut; 127 } 128 break; 129 case 'p': 130 policy = policyStringToOid(optarg, &useTLS); 131 if(policy == NULL) { 132 ourRtn = 2; 133 goto errOut; 134 } 135 break; 136 case 'k': 137 ortn = SecKeychainOpen(optarg, &kcRef); 138 if(ortn) { 139 cssmPerror("SecKeychainOpen", ortn); 140 ourRtn = 1; 141 goto errOut; 142 } 143 /* this can be specified multiple times */ 144 if(keychains == NULL) { 145 keychains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 146 } 147 CFArrayAppendValue(keychains, kcRef); 148 CFRelease(kcRef); 149 break; 150 case 'L': 151 actionFlags &= ~CSSM_TP_ACTION_FETCH_CERT_FROM_NET; 152 forceActionFlags = true; 153 break; 154 case 'l': 155 actionFlags |= CSSM_TP_ACTION_LEAF_IS_CA; 156 break; 157 case 'n': { 158 /* Legacy macOS used 'n' as the "no keychain search list" flag. 159 iOS interprets it as the name option, with one argument. 160 */ 161 char *o = argv[optind]; 162 if (o && o[0] != '-') { 163 name = o; 164 ++optind; 165 break; 166 } 167 } /* intentional fall-through to "no keychains" case, if no arg */ 168 case 'N': 169 /* No keychains, signalled by empty keychain array */ 170 if(keychains != NULL) { 171 fprintf(stderr, "-k and -%c are mutually exclusive\n", arg); 172 ourRtn = 2; 173 goto errOut; 174 } 175 keychains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 176 break; 177 case 'e': 178 emailAddrs = optarg; 179 break; 180 case 's': 181 sslHost = optarg; 182 break; 183 case 'q': 184 quiet = true; 185 break; 186 case 'd': 187 memset(&time, 0, sizeof(struct tm)); 188 if (strptime(optarg, "%Y-%m-%d-%H:%M:%S", &time) == NULL) { 189 if (strptime(optarg, "%Y-%m-%d", &time) == NULL) { 190 fprintf(stderr, "Date processing error\n"); 191 ourRtn = 2; 192 goto errOut; 193 } 194 } 195 gregorianDate.second = time.tm_sec; 196 gregorianDate.minute = time.tm_min; 197 gregorianDate.hour = time.tm_hour; 198 gregorianDate.day = time.tm_mday; 199 gregorianDate.month = time.tm_mon + 1; 200 gregorianDate.year = time.tm_year + 1900; 201 202 if (dateRef == NULL) { 203 dateRef = CFDateCreate(NULL, CFGregorianDateGetAbsoluteTime(gregorianDate, NULL)); 204 } 205 break; 206 case 'R': 207 revOptions |= revCheckOptionStringToFlags(optarg); 208 break; 209 case 'P': 210 printPem = true; 211 break; 212 case 't': 213 printText = true; 214 break; 215 case 'v': 216 printDetails = true; 217 verbose++; 218 break; 219 default: 220 ourRtn = 2; 221 goto errOut; 222 } 223 } 224 if(optind != argc) { 225 if (argc > optind) { 226 url = argv[argc-1]; 227 } 228 if (url && *url != '\0') { 229 useTLS = true; 230 ourRtn = evaluate_ssl(url, verbose, &trustRef); 231 goto post_evaluate; 232 } else { 233 ourRtn = 2; 234 } 235 goto errOut; 236 } 237 238 if(certs == NULL) { 239 if(roots == NULL) { 240 fprintf(stderr, "***No certs specified.\n"); 241 ourRtn = 2; 242 goto errOut; 243 } 244 if(CFArrayGetCount(roots) != 1) { 245 fprintf(stderr, "***Multiple roots and no certs not allowed.\n"); 246 ourRtn = 2; 247 goto errOut; 248 } 249 250 /* no certs and one root: verify the root */ 251 certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 252 CFArrayAppendValue(certs, CFArrayGetValueAtIndex(roots, 0)); 253 actionFlags |= CSSM_TP_ACTION_LEAF_IS_CA; 254 } 255 256 /* cook up a SecPolicyRef */ 257 properties = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 258 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 259 if (!properties) { 260 cssmPerror("CFDictionaryCreateMutable", errSecMemoryError); 261 ourRtn = 1; 262 goto errOut; 263 } else { 264 /* if a policy name was specified to match, set it in the dictionary */ 265 const char *nameStr = name; 266 if (!nameStr) { nameStr = (sslHost) ? sslHost : ((emailAddrs) ? emailAddrs : NULL); } 267 CFStringRef nameRef = (nameStr) ? CFStringCreateWithBytes(NULL, 268 (const UInt8 *)nameStr, (CFIndex)strlen(nameStr), kCFStringEncodingUTF8, false) : NULL; 269 if (nameRef) { 270 CFDictionarySetValue(properties, kSecPolicyName, nameRef); 271 CFRELEASE(nameRef); 272 } 273 CFStringRef policyID = NULL; 274 if (compareOids(policy, &CSSMOID_APPLE_TP_SSL)) { 275 policyID = kSecPolicyAppleSSL; 276 } else if (compareOids(policy, &CSSMOID_APPLE_TP_EAP)) { 277 policyID = kSecPolicyAppleEAP; 278 } else if (compareOids(policy, &CSSMOID_APPLE_TP_APPLEID_SHARING)) { 279 policyID = kSecPolicyAppleIDValidation; 280 } else if (compareOids(policy, &CSSMOID_APPLE_TP_SMIME)) { 281 policyID = kSecPolicyAppleSMIME; 282 } 283 if (policyID) { 284 policyRef = SecPolicyCreateWithProperties(policyID, properties); 285 } 286 } 287 if (!policyRef) { 288 /* all other policies not handled above */ 289 ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3, 290 policy, 291 NULL, // policy opts 292 &searchRef); 293 if(ortn) { 294 cssmPerror("SecPolicySearchCreate", ortn); 295 ourRtn = 1; 296 goto errOut; 297 } 298 ortn = SecPolicySearchCopyNext(searchRef, &policyRef); 299 if(ortn) { 300 cssmPerror("SecPolicySearchCopyNext", ortn); 301 ourRtn = 1; 302 goto errOut; 303 } 304 } 305 306 /* create policies array */ 307 policies = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 308 CFArrayAppendValue(policies, policyRef); 309 /* add optional SecPolicyRef for revocation, if specified */ 310 if(revOptions != 0) { 311 revPolicyRef = SecPolicyCreateRevocation(revOptions); 312 CFArrayAppendValue(policies, revPolicyRef); 313 } 314 315 /* create trust reference from certs and policies */ 316 ortn = SecTrustCreateWithCertificates(certs, policies, &trustRef); 317 if(ortn) { 318 cssmPerror("SecTrustCreateWithCertificates", ortn); 319 ourRtn = 1; 320 goto errOut; 321 } 322 323 /* roots (anchors) are optional */ 324 if(roots != NULL) { 325 ortn = SecTrustSetAnchorCertificates(trustRef, roots); 326 if(ortn) { 327 cssmPerror("SecTrustSetAnchorCertificates", ortn); 328 ourRtn = 1; 329 goto errOut; 330 } 331 } 332 if(actionFlags || forceActionFlags) { 333 memset(&actionData, 0, sizeof(actionData)); 334 actionData.Version = CSSM_APPLE_TP_ACTION_VERSION; 335 actionData.ActionFlags = actionFlags; 336 cfActionData = CFDataCreate(NULL, (UInt8 *)&actionData, sizeof(actionData)); 337 ortn = SecTrustSetParameters(trustRef, CSSM_TP_ACTION_DEFAULT, cfActionData); 338 if(ortn) { 339 cssmPerror("SecTrustSetParameters", ortn); 340 ourRtn = 1; 341 goto errOut; 342 } 343 } 344 if(keychains) { 345 ortn = SecTrustSetKeychains(trustRef, keychains); 346 if(ortn) { 347 cssmPerror("SecTrustSetKeychains", ortn); 348 ourRtn = 1; 349 goto errOut; 350 } 351 } 352 if(dateRef != NULL) { 353 ortn = SecTrustSetVerifyDate(trustRef, dateRef); 354 if(ortn) { 355 cssmPerror("SecTrustSetVerifyDate", ortn); 356 ourRtn = 1; 357 goto errOut; 358 } 359 } 360 361 /* GO */ 362 (void)SecTrustEvaluateWithError(trustRef, &errorRef); 363 post_evaluate: 364 ortn = SecTrustGetTrustResult(trustRef, &resultType); 365 if(ortn) { 366 /* should never fail - error on this doesn't mean the cert verified badly */ 367 cssmPerror("SecTrustEvaluate", ortn); 368 ourRtn = 1; 369 goto errOut; 370 } 371 switch(resultType) { 372 case kSecTrustResultUnspecified: 373 /* cert chain valid, no special UserTrust assignments */ 374 case kSecTrustResultProceed: 375 /* cert chain valid AND user explicitly trusts this */ 376 break; 377 case kSecTrustResultDeny: 378 if(!quiet) { 379 fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultDeny\n"); 380 } 381 ourRtn = 1; 382 break; 383 default: 384 ourRtn = 1; 385 if(!quiet) { 386 /* See what the TP had to say about this */ 387 ortn = SecTrustGetCssmResultCode(trustRef, &ocrtn); 388 if(ortn) { 389 cssmPerror("SecTrustGetCssmResultCode", ortn); 390 } 391 else { 392 cssmPerror("Cert Verify Result", ocrtn); 393 } 394 } 395 break; 396 } 397 if((ourRtn == 0) & !quiet) { 398 printf("...certificate verification successful.\n"); 399 } 400 if (printPem || printText || verbose) { 401 fprintf(stdout, "---\nCertificate chain\n"); 402 printCertChain(trustRef, printPem, printText); 403 } 404 if (verbose) { 405 printErrorDetails(trustRef); 406 } 407 if (useTLS) { 408 printExtendedResults(trustRef); 409 } 410 if (printDetails) { 411 CFArrayRef properties = SecTrustCopyProperties(trustRef); 412 if (verbose > 1) { 413 fprintf(stderr, "---\nCertificate chain properties\n"); 414 CFShow(properties); // output goes to stderr 415 } 416 if (properties) { 417 CFRelease(properties); 418 } 419 CFDictionaryRef result = SecTrustCopyResult(trustRef); 420 if (result) { 421 fprintf(stderr, "---\nTrust evaluation results\n"); 422 CFShow(result); // output goes to stderr 423 CFRelease(result); 424 } 425 if (errorRef) { 426 fprintf(stdout, "---\nTrust evaluation errors\n"); 427 CFShow(errorRef); 428 } 429 } 430 431 errOut: 432 /* cleanup */ 433 CFRELEASE(certs); 434 CFRELEASE(roots); 435 CFRELEASE(keychains); 436 CFRELEASE(properties); 437 CFRELEASE(policies); 438 CFRELEASE(revPolicyRef); 439 CFRELEASE(dateRef); 440 CFRELEASE(policyRef); 441 CFRELEASE(trustRef); 442 CFRELEASE(searchRef); 443 CFRELEASE(errorRef); 444 CFRELEASE(cfActionData); 445 return ourRtn; 446 }