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