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