/ SecurityTool / sharedTool / verify_cert.c
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  }