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