]> git.saurik.com Git - apple/security.git/blob - SecurityTool/macOS/verify_cert.c
Security-59306.61.1.tar.gz
[apple/security.git] / SecurityTool / macOS / 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 }