verify_cert.c
1 /* 2 * Copyright (c) 2003-2018 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 #define CFRELEASE(cf) if (cf) { CFRelease(cf); } 27 28 #include <Security/SecCertificate.h> 29 #include <Security/SecCertificatePriv.h> 30 #include <Security/SecTrust.h> 31 #include <Security/SecPolicy.h> 32 #include <Security/SecPolicyPriv.h> 33 #include <utilities/fileIo.h> 34 35 #include <sys/stat.h> 36 #include <stdio.h> 37 #include <time.h> 38 39 #include "SecurityCommands.h" 40 41 CFStringRef policyToConstant(const char *policy); 42 int verify_cert(int argc, char * const *argv); 43 44 static int addCertFile(const char *fileName, CFMutableArrayRef *array) { 45 SecCertificateRef certRef = NULL; 46 CFDataRef dataRef = NULL; 47 unsigned char *buf = NULL; 48 size_t numBytes; 49 int rtn = 0; 50 51 if (readFileSizet(fileName, &buf, &numBytes)) { 52 rtn = -1; 53 goto errOut; 54 } 55 56 dataRef = CFDataCreate(NULL, buf, numBytes); 57 certRef = SecCertificateCreateWithData(NULL, dataRef); 58 if (!certRef) { 59 certRef = SecCertificateCreateWithPEM(NULL, dataRef); 60 if (!certRef) { 61 rtn = -1; 62 goto errOut; 63 } 64 } 65 66 if (*array == NULL) { 67 *array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 68 } 69 70 CFArrayAppendValue(*array, certRef); 71 72 errOut: 73 /* Cleanup */ 74 free(buf); 75 CFRELEASE(dataRef); 76 CFRELEASE(certRef); 77 return rtn; 78 } 79 80 CFStringRef policyToConstant(const char *policy) { 81 if (policy == NULL) { 82 return NULL; 83 } 84 else if (!strcmp(policy, "basic")) { 85 return kSecPolicyAppleX509Basic; 86 } 87 else if (!strcmp(policy, "ssl")) { 88 return kSecPolicyAppleSSL; 89 } 90 else if (!strcmp(policy, "smime")) { 91 return kSecPolicyAppleSMIME; 92 } 93 else if (!strcmp(policy, "eap")) { 94 return kSecPolicyAppleEAP; 95 } 96 else if (!strcmp(policy, "IPSec")) { 97 return kSecPolicyAppleIPsec; 98 } 99 else if (!strcmp(policy, "appleID")) { 100 return kSecPolicyAppleIDValidation; 101 } 102 else if (!strcmp(policy, "codeSign")) { 103 return kSecPolicyAppleCodeSigning; 104 } 105 else if (!strcmp(policy, "timestamping")) { 106 return kSecPolicyAppleTimeStamping; 107 } 108 else if (!strcmp(policy, "revocation")) { 109 return kSecPolicyAppleRevocation; 110 } 111 else if (!strcmp(policy, "passbook")) { 112 /* Passbook not implemented */ 113 return NULL; 114 } 115 else { 116 return NULL; 117 } 118 } 119 120 static CFOptionFlags revCheckOptionStringToFlags( 121 const char *revCheckOption) 122 { 123 CFOptionFlags result = 0; 124 if(revCheckOption == NULL) { 125 return result; 126 } 127 else if(!strcmp(revCheckOption, "ocsp")) { 128 result |= kSecRevocationOCSPMethod; 129 } 130 else if(!strcmp(revCheckOption, "crl")) { 131 result |= kSecRevocationCRLMethod; 132 } 133 else if(!strcmp(revCheckOption, "require")) { 134 result |= kSecRevocationRequirePositiveResponse; 135 } 136 else if(!strcmp(revCheckOption, "offline")) { 137 result |= kSecRevocationNetworkAccessDisabled; 138 } 139 else if(!strcmp(revCheckOption, "online")) { 140 result |= kSecRevocationOnlineCheck; 141 } 142 return result; 143 } 144 145 int verify_cert(int argc, char * const *argv) { 146 extern char *optarg; 147 extern int optind; 148 int arg; 149 150 CFMutableArrayRef certs = NULL; 151 CFMutableArrayRef roots = NULL; 152 CFMutableArrayRef policies = NULL; 153 154 CFMutableDictionaryRef dict = NULL; 155 CFStringRef name = NULL; 156 CFBooleanRef client = kCFBooleanFalse; 157 CFOptionFlags revOptions = 0; 158 159 OSStatus ortn; 160 int ourRtn = 0; 161 bool quiet = false; 162 163 struct tm time; 164 CFGregorianDate gregorianDate; 165 CFDateRef dateRef = NULL; 166 167 CFStringRef policy = NULL; 168 SecPolicyRef policyRef = NULL; 169 SecPolicyRef revPolicyRef = NULL; 170 Boolean fetch = true; 171 SecTrustRef trustRef = NULL; 172 SecTrustResultType resultType; 173 174 if (argc < 2) { 175 return SHOW_USAGE_MESSAGE; 176 } 177 178 optind = 1; 179 180 while ((arg = getopt(argc, argv, "Cc:r:p:d:n:LqR:")) != -1) { 181 switch (arg) { 182 case 'c': 183 /* Can be specified multiple times */ 184 if (addCertFile(optarg, &certs)) { 185 fprintf(stderr, "Cert file error\n"); 186 ourRtn = 1; 187 goto errOut; 188 } 189 break; 190 case 'r': 191 /* Can be specified multiple times */ 192 if (addCertFile(optarg, &roots)) { 193 fprintf(stderr, "Root file error\n"); 194 ourRtn = 1; 195 goto errOut; 196 } 197 break; 198 case 'p': 199 policy = policyToConstant(optarg); 200 if (policy == NULL) { 201 fprintf(stderr, "Policy processing error\n"); 202 ourRtn = 2; 203 goto errOut; 204 } 205 break; 206 case 'L': 207 /* Force no network fetch of certs */ 208 fetch = false; 209 break; 210 case 'n': 211 if (name == NULL) { 212 name = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8); 213 } 214 break; 215 case 'q': 216 quiet = true; 217 break; 218 case 'C': 219 /* Set to client */ 220 client = kCFBooleanTrue; 221 break; 222 case 'd': 223 memset(&time, 0, sizeof(struct tm)); 224 if (strptime(optarg, "%Y-%m-%d-%H:%M:%S", &time) == NULL) { 225 if (strptime(optarg, "%Y-%m-%d", &time) == NULL) { 226 fprintf(stderr, "Date processing error\n"); 227 ourRtn = 2; 228 goto errOut; 229 } 230 } 231 gregorianDate.second = time.tm_sec; 232 gregorianDate.minute = time.tm_min; 233 gregorianDate.hour = time.tm_hour; 234 gregorianDate.day = time.tm_mday; 235 gregorianDate.month = time.tm_mon + 1; 236 gregorianDate.year = time.tm_year + 1900; 237 238 if (dateRef == NULL) { 239 dateRef = CFDateCreate(NULL, CFGregorianDateGetAbsoluteTime(gregorianDate, NULL)); 240 } 241 break; 242 case 'R': 243 revOptions |= revCheckOptionStringToFlags(optarg); 244 break; 245 default: 246 fprintf(stderr, "Usage error\n"); 247 ourRtn = 2; 248 goto errOut; 249 } 250 } 251 252 if (optind != argc) { 253 ourRtn = 2; 254 goto errOut; 255 } 256 257 if (policy == NULL) { 258 policy = kSecPolicyAppleX509Basic; 259 } 260 261 if (certs == NULL) { 262 if (roots == NULL) { 263 fprintf(stderr, "No certificates specified.\n"); 264 ourRtn = 2; 265 goto errOut; 266 } 267 if (CFArrayGetCount(roots) != 1) { 268 fprintf(stderr, "Multiple roots and no certificates not allowed.\n"); 269 ourRtn = 2; 270 goto errOut; 271 } 272 273 /* No certs and one root: verify the root */ 274 certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 275 CFArrayAppendValue(certs, CFArrayGetValueAtIndex(roots, 0)); 276 } 277 278 /* Per-policy options */ 279 if (!CFStringCompare(policy, kSecPolicyAppleSSL, 0) || !CFStringCompare(policy, kSecPolicyAppleIPsec, 0)) { 280 dict = CFDictionaryCreateMutable(NULL, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 281 282 if (name == NULL) { 283 fprintf(stderr, "Name not specified for IPsec or SSL policy. '-n' is a required option for these policies."); 284 ourRtn = 2; 285 goto errOut; 286 } 287 CFDictionaryAddValue(dict, kSecPolicyName, name); 288 CFDictionaryAddValue(dict, kSecPolicyClient, client); 289 } 290 else if (!CFStringCompare(policy, kSecPolicyAppleEAP, 0)) { 291 dict = CFDictionaryCreateMutable(NULL, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 292 293 CFDictionaryAddValue(dict, kSecPolicyClient, client); 294 } 295 else if (!CFStringCompare(policy, kSecPolicyAppleSMIME, 0)) { 296 dict = CFDictionaryCreateMutable(NULL, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 297 298 if (name == NULL) { 299 fprintf(stderr, "Name not specified for SMIME policy. '-n' is a required option for this policy."); 300 ourRtn = 2; 301 goto errOut; 302 } 303 CFDictionaryAddValue(dict, kSecPolicyName, name); 304 } 305 306 policyRef = SecPolicyCreateWithProperties(policy, dict); 307 308 /* create policies array */ 309 policies = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 310 CFArrayAppendValue(policies, policyRef); 311 /* add optional SecPolicyRef for revocation, if specified */ 312 if(revOptions != 0) { 313 revPolicyRef = SecPolicyCreateRevocation(revOptions); 314 CFArrayAppendValue(policies, revPolicyRef); 315 } 316 317 /* create trust reference from certs and policies */ 318 ortn = SecTrustCreateWithCertificates(certs, policies, &trustRef); 319 if (ortn) { 320 fprintf(stderr, "SecTrustCreateWithCertificates\n"); 321 ourRtn = 1; 322 goto errOut; 323 } 324 325 /* Roots (anchors) are optional */ 326 if (roots != NULL) { 327 ortn = SecTrustSetAnchorCertificates(trustRef, roots); 328 if (ortn) { 329 fprintf(stderr, "SecTrustSetAnchorCertificates\n"); 330 ourRtn = 1; 331 goto errOut; 332 } 333 } 334 if (fetch == false) { 335 ortn = SecTrustSetNetworkFetchAllowed(trustRef, fetch); 336 if (ortn) { 337 fprintf(stderr, "SecTrustSetNetworkFetchAllowed\n"); 338 ourRtn = 1; 339 goto errOut; 340 } 341 } 342 343 /* Set verification time for trust object */ 344 if (dateRef != NULL) { 345 ortn = SecTrustSetVerifyDate(trustRef, dateRef); 346 if (ortn) { 347 fprintf(stderr, "SecTrustSetVerifyDate\n"); 348 ourRtn = 1; 349 goto errOut; 350 } 351 } 352 353 /* Evaluate certs */ 354 ortn = SecTrustEvaluate(trustRef, &resultType); 355 if (ortn) { 356 /* Should never fail - error doesn't mean the cert verified badly */ 357 fprintf(stderr, "SecTrustEvaluate\n"); 358 ourRtn = 1; 359 goto errOut; 360 } 361 switch (resultType) { 362 case kSecTrustResultUnspecified: 363 /* Cert chain valid, no special UserTrust assignments */ 364 case kSecTrustResultProceed: 365 /* Cert chain valid AND user explicitly trusts this */ 366 break; 367 case kSecTrustResultDeny: 368 /* User-configured denial */ 369 if (!quiet) { 370 fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultDeny\n"); 371 } 372 ourRtn = 1; 373 break; 374 case kSecTrustResultInvalid: 375 /* SecTrustEvaluate not called yet */ 376 if (!quiet) { 377 fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultInvalid\n"); 378 } 379 ourRtn = 1; 380 break; 381 case kSecTrustResultRecoverableTrustFailure: 382 /* Failure, can be user-overridden */ 383 if (!quiet) { 384 fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultRecoverableTrustFailure\n"); 385 } 386 ourRtn = 1; 387 break; 388 case kSecTrustResultFatalTrustFailure: 389 /* Complete failure */ 390 if (!quiet) { 391 fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultFatalTrustFailure\n"); 392 } 393 ourRtn = 1; 394 break; 395 case kSecTrustResultOtherError: 396 /* Failure unrelated to trust evaluation */ 397 if (!quiet) { 398 fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultOtherError\n"); 399 } 400 ourRtn = 1; 401 break; 402 default: 403 /* Error is not a defined SecTrustResultType */ 404 if (!quiet) { 405 fprintf(stderr, "Cert Verify Result: %u\n", resultType); 406 } 407 ourRtn = 1; 408 break; 409 } 410 411 if ((ourRtn == 0) && !quiet) { 412 printf("...certificate verification successful.\n"); 413 } 414 errOut: 415 /* Cleanup */ 416 CFRELEASE(certs); 417 CFRELEASE(roots); 418 CFRELEASE(dateRef); 419 CFRELEASE(dict); 420 CFRELEASE(policies); 421 CFRELEASE(revPolicyRef); 422 CFRELEASE(policyRef); 423 CFRELEASE(trustRef); 424 CFRELEASE(name); 425 return ourRtn; 426 }