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