]> git.saurik.com Git - apple/security.git/blob - SecurityTool/verify_cert.c
Security-58286.270.3.0.1.tar.gz
[apple/security.git] / SecurityTool / verify_cert.c
1 /*
2 * Copyright (c) 2006,2010,2012,2014-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 #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_utils.h"
37 #include "verify_cert.h"
38 #include <utilities/SecCFRelease.h>
39 #include "security_tool.h"
40
41 /*
42 * Read file as a cert, add to a CFArray, creating the array if necessary
43 */
44 static int addCertFile(
45 const char *fileName,
46 CFMutableArrayRef *array)
47 {
48 SecCertificateRef certRef;
49
50 if(readCertFile(fileName, &certRef)) {
51 return -1;
52 }
53 if(*array == NULL) {
54 *array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
55 }
56 CFArrayAppendValue(*array, certRef);
57 CFRelease(certRef);
58 return 0;
59 }
60
61 int
62 verify_cert(int argc, char * const *argv)
63 {
64 extern char *optarg;
65 extern int optind;
66 OSStatus ortn;
67 int arg;
68 CFMutableArrayRef certs = NULL;
69 CFMutableArrayRef roots = NULL;
70 CFMutableArrayRef keychains = NULL;
71 CFMutableArrayRef policies = NULL;
72 const CSSM_OID *policy = &CSSMOID_APPLE_X509_BASIC;
73 SecKeychainRef kcRef = NULL;
74 int ourRtn = 0;
75 bool quiet = false;
76 bool client = false;
77 SecPolicyRef policyRef = NULL;
78 SecPolicyRef revPolicyRef = NULL;
79 SecTrustRef trustRef = NULL;
80 SecPolicySearchRef searchRef = NULL;
81 const char *emailAddrs = NULL;
82 const char *sslHost = NULL;
83 const char *name = NULL;
84 CSSM_APPLE_TP_SSL_OPTIONS sslOpts;
85 CSSM_APPLE_TP_SMIME_OPTIONS smimeOpts;
86 CSSM_APPLE_TP_ACTION_FLAGS actionFlags = 0;
87 bool forceActionFlags = false;
88 CSSM_APPLE_TP_ACTION_DATA actionData;
89 CSSM_DATA optionData;
90 CFDataRef cfActionData = NULL;
91 SecTrustResultType resultType;
92 OSStatus ocrtn;
93 struct tm time;
94 CFGregorianDate gregorianDate;
95 CFDateRef dateRef = NULL;
96 CFOptionFlags revOptions = 0;
97
98 if(argc < 2) {
99 return SHOW_USAGE_MESSAGE;
100 }
101 /* permit network cert fetch unless explicitly turned off with '-L' */
102 actionFlags |= CSSM_TP_ACTION_FETCH_CERT_FROM_NET;
103 optind = 1;
104 while ((arg = getopt(argc, argv, "Cc:r:p:k:e:s:d:LlNnqR:")) != -1) {
105 switch (arg) {
106 case 'C':
107 client = true;
108 break;
109 case 'c':
110 /* this can be specified multiple times */
111 if(addCertFile(optarg, &certs)) {
112 ourRtn = 1;
113 goto errOut;
114 }
115 break;
116 case 'r':
117 /* this can be specified multiple times */
118 if(addCertFile(optarg, &roots)) {
119 ourRtn = 1;
120 goto errOut;
121 }
122 break;
123 case 'p':
124 policy = policyStringToOid(optarg);
125 if(policy == NULL) {
126 ourRtn = 2;
127 goto errOut;
128 }
129 break;
130 case 'k':
131 ortn = SecKeychainOpen(optarg, &kcRef);
132 if(ortn) {
133 cssmPerror("SecKeychainOpen", ortn);
134 ourRtn = 1;
135 goto errOut;
136 }
137 /* this can be specified multiple times */
138 if(keychains == NULL) {
139 keychains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
140 }
141 CFArrayAppendValue(keychains, kcRef);
142 CFRelease(kcRef);
143 break;
144 case 'L':
145 actionFlags &= ~CSSM_TP_ACTION_FETCH_CERT_FROM_NET;
146 forceActionFlags = true;
147 break;
148 case 'l':
149 actionFlags |= CSSM_TP_ACTION_LEAF_IS_CA;
150 break;
151 case 'n': {
152 /* Legacy macOS used 'n' as the "no keychain search list" flag.
153 iOS interprets it as the name option, with one argument.
154 */
155 char *o = argv[optind];
156 if (o && o[0] != '-') {
157 name = optarg;
158 ++optind;
159 break;
160 }
161 } /* intentional fall-through to "no keychains" case, if no arg */
162 case 'N':
163 /* No keychains, signalled by empty keychain array */
164 if(keychains != NULL) {
165 fprintf(stderr, "-k and -%c are mutually exclusive\n", arg);
166 ourRtn = 2;
167 goto errOut;
168 }
169 keychains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
170 break;
171 case 'e':
172 emailAddrs = optarg;
173 break;
174 case 's':
175 sslHost = optarg;
176 break;
177 case 'q':
178 quiet = true;
179 break;
180 case 'd':
181 memset(&time, 0, sizeof(struct tm));
182 if (strptime(optarg, "%Y-%m-%d-%H:%M:%S", &time) == NULL) {
183 if (strptime(optarg, "%Y-%m-%d", &time) == NULL) {
184 fprintf(stderr, "Date processing error\n");
185 ourRtn = 2;
186 goto errOut;
187 }
188 }
189 gregorianDate.second = time.tm_sec;
190 gregorianDate.minute = time.tm_min;
191 gregorianDate.hour = time.tm_hour;
192 gregorianDate.day = time.tm_mday;
193 gregorianDate.month = time.tm_mon + 1;
194 gregorianDate.year = time.tm_year + 1900;
195
196 if (dateRef == NULL) {
197 dateRef = CFDateCreate(NULL, CFGregorianDateGetAbsoluteTime(gregorianDate, NULL));
198 }
199 break;
200 case 'R':
201 revOptions |= revCheckOptionStringToFlags(optarg);
202 break;
203 default:
204 ourRtn = 2;
205 goto errOut;
206 }
207 }
208 if(optind != argc) {
209 ourRtn = 2;
210 goto errOut;
211 }
212
213 if(certs == NULL) {
214 if(roots == NULL) {
215 fprintf(stderr, "***No certs specified.\n");
216 ourRtn = 2;
217 goto errOut;
218 }
219 if(CFArrayGetCount(roots) != 1) {
220 fprintf(stderr, "***Multiple roots and no certs not allowed.\n");
221 ourRtn = 2;
222 goto errOut;
223 }
224
225 /* no certs and one root: verify the root */
226 certs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
227 CFArrayAppendValue(certs, CFArrayGetValueAtIndex(roots, 0));
228 actionFlags |= CSSM_TP_ACTION_LEAF_IS_CA;
229 }
230
231 /* cook up a SecPolicyRef */
232 ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
233 policy,
234 NULL, // policy opts
235 &searchRef);
236 if(ortn) {
237 cssmPerror("SecPolicySearchCreate", ortn);
238 ourRtn = 1;
239 goto errOut;
240 }
241 ortn = SecPolicySearchCopyNext(searchRef, &policyRef);
242 if(ortn) {
243 cssmPerror("SecPolicySearchCopyNext", ortn);
244 ourRtn = 1;
245 goto errOut;
246 }
247
248 /* per-policy options */
249 if(compareOids(policy, &CSSMOID_APPLE_TP_SSL) || compareOids(policy, &CSSMOID_APPLE_TP_APPLEID_SHARING)) {
250 const char *nameStr = (name) ? name : ((sslHost) ? sslHost : NULL);
251 if(nameStr) {
252 memset(&sslOpts, 0, sizeof(sslOpts));
253 sslOpts.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
254 sslOpts.ServerName = nameStr;
255 sslOpts.ServerNameLen = (uint32) strlen(nameStr);
256 sslOpts.Flags = (client) ? CSSM_APPLE_TP_SSL_CLIENT : 0;
257 optionData.Data = (uint8 *)&sslOpts;
258 optionData.Length = sizeof(sslOpts);
259 ortn = SecPolicySetValue(policyRef, &optionData);
260 if(ortn) {
261 cssmPerror("SecPolicySetValue", ortn);
262 ourRtn = 1;
263 goto errOut;
264 }
265 }
266 }
267 if(compareOids(policy, &CSSMOID_APPLE_TP_SMIME)) {
268 const char *nameStr = (name) ? name : ((emailAddrs) ? emailAddrs : NULL);
269 if(nameStr) {
270 memset(&smimeOpts, 0, sizeof(smimeOpts));
271 smimeOpts.Version = CSSM_APPLE_TP_SMIME_OPTS_VERSION;
272 smimeOpts.SenderEmail = nameStr;
273 smimeOpts.SenderEmailLen = (uint32) strlen(nameStr);
274 optionData.Data = (uint8 *)&smimeOpts;
275 optionData.Length = sizeof(smimeOpts);
276 ortn = SecPolicySetValue(policyRef, &optionData);
277 if(ortn) {
278 cssmPerror("SecPolicySetValue", ortn);
279 ourRtn = 1;
280 goto errOut;
281 }
282 }
283 }
284
285 /* create policies array */
286 policies = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
287 CFArrayAppendValue(policies, policyRef);
288 /* add optional SecPolicyRef for revocation, if specified */
289 if(revOptions != 0) {
290 revPolicyRef = SecPolicyCreateRevocation(revOptions);
291 CFArrayAppendValue(policies, revPolicyRef);
292 }
293
294 /* create trust reference from certs and policies */
295 ortn = SecTrustCreateWithCertificates(certs, policies, &trustRef);
296 if(ortn) {
297 cssmPerror("SecTrustCreateWithCertificates", ortn);
298 ourRtn = 1;
299 goto errOut;
300 }
301
302 /* roots (anchors) are optional */
303 if(roots != NULL) {
304 ortn = SecTrustSetAnchorCertificates(trustRef, roots);
305 if(ortn) {
306 cssmPerror("SecTrustSetAnchorCertificates", ortn);
307 ourRtn = 1;
308 goto errOut;
309 }
310 }
311 if(actionFlags || forceActionFlags) {
312 memset(&actionData, 0, sizeof(actionData));
313 actionData.Version = CSSM_APPLE_TP_ACTION_VERSION;
314 actionData.ActionFlags = actionFlags;
315 cfActionData = CFDataCreate(NULL, (UInt8 *)&actionData, sizeof(actionData));
316 ortn = SecTrustSetParameters(trustRef, CSSM_TP_ACTION_DEFAULT, cfActionData);
317 if(ortn) {
318 cssmPerror("SecTrustSetParameters", ortn);
319 ourRtn = 1;
320 goto errOut;
321 }
322 }
323 if(keychains) {
324 ortn = SecTrustSetKeychains(trustRef, keychains);
325 if(ortn) {
326 cssmPerror("SecTrustSetKeychains", ortn);
327 ourRtn = 1;
328 goto errOut;
329 }
330 }
331 if(dateRef != NULL) {
332 ortn = SecTrustSetVerifyDate(trustRef, dateRef);
333 if(ortn) {
334 cssmPerror("SecTrustSetVerifyDate", ortn);
335 ourRtn = 1;
336 goto errOut;
337 }
338 }
339
340 /* GO */
341 ortn = SecTrustEvaluate(trustRef, &resultType);
342 if(ortn) {
343 /* should never fail - error on this doesn't mean the cert verified badly */
344 cssmPerror("SecTrustEvaluate", ortn);
345 ourRtn = 1;
346 goto errOut;
347 }
348 switch(resultType) {
349 case kSecTrustResultUnspecified:
350 /* cert chain valid, no special UserTrust assignments */
351 case kSecTrustResultProceed:
352 /* cert chain valid AND user explicitly trusts this */
353 break;
354 case kSecTrustResultDeny:
355 if(!quiet) {
356 fprintf(stderr, "SecTrustEvaluate result: kSecTrustResultDeny\n");
357 }
358 ourRtn = 1;
359 break;
360 default:
361 ourRtn = 1;
362 if(!quiet) {
363 /* See what the TP had to say about this */
364 ortn = SecTrustGetCssmResultCode(trustRef, &ocrtn);
365 if(ortn) {
366 cssmPerror("SecTrustGetCssmResultCode", ortn);
367 }
368 else {
369 cssmPerror("Cert Verify Result", ocrtn);
370 }
371 }
372 break;
373 }
374
375 if((ourRtn == 0) & !quiet) {
376 printf("...certificate verification successful.\n");
377 }
378 errOut:
379 CFReleaseNull(dateRef);
380 /* cleanup */
381 CFRELEASE(certs);
382 CFRELEASE(roots);
383 CFRELEASE(keychains);
384 CFRELEASE(policies);
385 CFRELEASE(revPolicyRef);
386 CFRELEASE(policyRef);
387 CFRELEASE(trustRef);
388 CFRELEASE(searchRef);
389 CFRELEASE(cfActionData);
390 return ourRtn;
391 }