]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/Tool/verify_cert.c
Security-58286.70.7.tar.gz
[apple/security.git] / OSX / sec / Security / Tool / 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 }