]>
Commit | Line | Data |
---|---|---|
d8f41ccd A |
1 | /* |
2 | * Verify one CAC cert using standard system-wide cert and CRL keychains. | |
3 | */ | |
4 | #include <stdlib.h> | |
5 | #include <stdio.h> | |
6 | #include <security_cdsa_utils/cuFileIo.h> | |
7 | #include <Security/cuCdsaUtils.h> /* private */ | |
8 | //#include "clutils.h" | |
9 | #include <Security/cssmerrno.h> /* private cssmErrorString() */ | |
10 | #include <Security/cssm.h> | |
11 | #include <Security/oidsalg.h> | |
12 | #include <Security/SecTrust.h> | |
13 | ||
14 | /* | |
15 | * More-or-less hard-coded locations of system-wide keychains containing | |
16 | * intermediate certs (which are required for this operation) and | |
17 | * CRLs (which is optional; it's just a cache for performance reasons). | |
18 | */ | |
19 | #define X509_CERT_DB "/System/Library/Keychains/X509Certificates" | |
20 | #define X509_CRL_DB "/private/var/db/crls/crlcache.db" | |
21 | ||
22 | static void usage(char **argv) | |
23 | { | |
24 | printf("Usage: %s certFileName [options]\n", argv[0]); | |
25 | printf("Options:\n"); | |
26 | printf(" a allow unverified certs\n"); | |
27 | printf(" d disable CRL verification\n"); | |
28 | printf(" n no network fetch of CRLs\n"); | |
29 | exit(1); | |
30 | } | |
31 | ||
32 | /*** Display verify results ***/ | |
33 | ||
34 | static void statusBitTest( | |
35 | CSSM_TP_APPLE_CERT_STATUS certStatus, | |
36 | uint32 bit, | |
37 | const char *str) | |
38 | { | |
39 | if(certStatus & bit) { | |
40 | printf("%s ", str); | |
41 | } | |
42 | } | |
43 | ||
44 | static void printCertInfo( | |
45 | unsigned numCerts, // from CertGroup | |
46 | const CSSM_TP_APPLE_EVIDENCE_INFO *info) | |
47 | { | |
48 | CSSM_TP_APPLE_CERT_STATUS cs; | |
49 | ||
50 | for(unsigned i=0; i<numCerts; i++) { | |
51 | const CSSM_TP_APPLE_EVIDENCE_INFO *thisInfo = &info[i]; | |
52 | cs = thisInfo->StatusBits; | |
53 | printf(" cert %u:\n", i); | |
54 | printf(" StatusBits : 0x%x", (unsigned)cs); | |
55 | if(cs) { | |
56 | printf(" ( "); | |
57 | statusBitTest(cs, CSSM_CERT_STATUS_EXPIRED, "EXPIRED"); | |
58 | statusBitTest(cs, CSSM_CERT_STATUS_NOT_VALID_YET, | |
59 | "NOT_VALID_YET"); | |
60 | statusBitTest(cs, CSSM_CERT_STATUS_IS_IN_INPUT_CERTS, | |
61 | "IS_IN_INPUT_CERTS"); | |
62 | statusBitTest(cs, CSSM_CERT_STATUS_IS_IN_ANCHORS, | |
63 | "IS_IN_ANCHORS"); | |
64 | statusBitTest(cs, CSSM_CERT_STATUS_IS_ROOT, "IS_ROOT"); | |
65 | statusBitTest(cs, CSSM_CERT_STATUS_IS_FROM_NET, "IS_FROM_NET"); | |
66 | printf(")\n"); | |
67 | } | |
68 | else { | |
69 | printf("\n"); | |
70 | } | |
71 | printf(" NumStatusCodes : %u ", | |
72 | thisInfo->NumStatusCodes); | |
73 | for(unsigned j=0; j<thisInfo->NumStatusCodes; j++) { | |
74 | printf("%s ", | |
75 | cssmErrorString(thisInfo->StatusCodes[j]).c_str()); | |
76 | } | |
77 | printf("\n"); | |
78 | printf(" Index: %u\n", thisInfo->Index); | |
79 | } | |
80 | return; | |
81 | } | |
82 | ||
83 | /* we really only need CSSM_EVIDENCE_FORM_APPLE_CERT_INFO */ | |
84 | #define SHOW_ALL_VFY_RESULTS 0 | |
85 | ||
86 | static void dumpVfyResult( | |
87 | const CSSM_TP_VERIFY_CONTEXT_RESULT *vfyResult) | |
88 | { | |
89 | unsigned numEvidences = vfyResult->NumberOfEvidences; | |
90 | unsigned numCerts = 0; | |
91 | printf("Returned evidence:\n"); | |
92 | for(unsigned dex=0; dex<numEvidences; dex++) { | |
93 | CSSM_EVIDENCE_PTR ev = &vfyResult->Evidence[dex]; | |
94 | #if SHOW_ALL_VFY_RESULTS | |
95 | printf(" Evidence %u:\n", dex); | |
96 | #endif | |
97 | switch(ev->EvidenceForm) { | |
98 | case CSSM_EVIDENCE_FORM_APPLE_HEADER: | |
99 | { | |
100 | #if SHOW_ALL_VFY_RESULTS | |
101 | const CSSM_TP_APPLE_EVIDENCE_HEADER *hdr = | |
102 | (const CSSM_TP_APPLE_EVIDENCE_HEADER *)(ev->Evidence); | |
103 | printf(" Form = HEADER; Version = %u\n", hdr->Version); | |
104 | #endif | |
105 | break; | |
106 | } | |
107 | case CSSM_EVIDENCE_FORM_APPLE_CERTGROUP: | |
108 | { | |
109 | const CSSM_CERTGROUP *grp = (const CSSM_CERTGROUP *)ev->Evidence; | |
110 | numCerts = grp->NumCerts; | |
111 | #if SHOW_ALL_VFY_RESULTS | |
112 | /* parse the rest of this eventually */ | |
113 | /* Note we depend on this coming before the CERT_INFO */ | |
114 | printf(" Form = CERTGROUP; numCerts = %u\n", numCerts); | |
115 | #endif | |
116 | break; | |
117 | } | |
118 | case CSSM_EVIDENCE_FORM_APPLE_CERT_INFO: | |
119 | { | |
120 | const CSSM_TP_APPLE_EVIDENCE_INFO *info = | |
121 | (const CSSM_TP_APPLE_EVIDENCE_INFO *)ev->Evidence; | |
122 | printCertInfo(numCerts, info); | |
123 | break; | |
124 | } | |
125 | default: | |
126 | printf("***UNKNOWN Evidence form (%u)\n", | |
127 | (unsigned)ev->EvidenceForm); | |
128 | break; | |
129 | } | |
130 | } | |
131 | } | |
132 | ||
133 | /* free a CSSM_CERT_GROUP */ | |
134 | void tpFreeCertGroup( | |
135 | CSSM_CERTGROUP_PTR certGroup, | |
136 | CSSM_BOOL freeCertData, // free individual CertList.Data | |
137 | CSSM_BOOL freeStruct) // free the overall CSSM_CERTGROUP | |
138 | { | |
139 | unsigned dex; | |
140 | ||
141 | if(certGroup == NULL) { | |
142 | return; | |
143 | } | |
144 | ||
145 | if(freeCertData) { | |
146 | /* free the individual cert Data fields */ | |
147 | for(dex=0; dex<certGroup->NumCerts; dex++) { | |
148 | APP_FREE(certGroup->GroupList.CertList[dex].Data); | |
149 | } | |
150 | } | |
151 | ||
152 | /* and the array of CSSM_DATAs */ | |
153 | if(certGroup->GroupList.CertList) { | |
154 | APP_FREE(certGroup->GroupList.CertList); | |
155 | } | |
156 | ||
157 | if(freeStruct) { | |
158 | APP_FREE(certGroup); | |
159 | } | |
160 | } | |
161 | ||
162 | ||
163 | /* | |
164 | * Free the contents of a CSSM_TP_VERIFY_CONTEXT_RESULT returned from | |
165 | * CSSM_TP_CertGroupVerify(). | |
166 | */ | |
167 | CSSM_RETURN freeVfyResult( | |
168 | CSSM_TP_VERIFY_CONTEXT_RESULT *ctx) | |
169 | { | |
170 | int numCerts = -1; | |
171 | CSSM_RETURN crtn = CSSM_OK; | |
172 | ||
173 | for(unsigned i=0; i<ctx->NumberOfEvidences; i++) { | |
174 | CSSM_EVIDENCE_PTR evp = &ctx->Evidence[i]; | |
175 | switch(evp->EvidenceForm) { | |
176 | case CSSM_EVIDENCE_FORM_APPLE_HEADER: | |
177 | /* Evidence = (CSSM_TP_APPLE_EVIDENCE_HEADER *) */ | |
178 | APP_FREE(evp->Evidence); | |
179 | evp->Evidence = NULL; | |
180 | break; | |
181 | case CSSM_EVIDENCE_FORM_APPLE_CERTGROUP: | |
182 | { | |
183 | /* Evidence = CSSM_CERTGROUP_PTR */ | |
184 | CSSM_CERTGROUP_PTR cgp = (CSSM_CERTGROUP_PTR)evp->Evidence; | |
185 | numCerts = cgp->NumCerts; | |
186 | tpFreeCertGroup(cgp, CSSM_TRUE, CSSM_TRUE); | |
187 | evp->Evidence = NULL; | |
188 | break; | |
189 | } | |
190 | case CSSM_EVIDENCE_FORM_APPLE_CERT_INFO: | |
191 | { | |
192 | /* Evidence = array of CSSM_TP_APPLE_EVIDENCE_INFO */ | |
193 | if(numCerts < 0) { | |
194 | /* Haven't gotten a CSSM_CERTGROUP_PTR! */ | |
195 | printf("***Malformed VerifyContextResult (2)\n"); | |
196 | crtn = CSSMERR_TP_INTERNAL_ERROR; | |
197 | break; | |
198 | } | |
199 | CSSM_TP_APPLE_EVIDENCE_INFO *evInfo = | |
200 | (CSSM_TP_APPLE_EVIDENCE_INFO *)evp->Evidence; | |
201 | for(unsigned k=0; k<(unsigned)numCerts; k++) { | |
202 | /* Dispose of StatusCodes, UniqueRecord */ | |
203 | CSSM_TP_APPLE_EVIDENCE_INFO *thisEvInfo = | |
204 | &evInfo[k]; | |
205 | if(thisEvInfo->StatusCodes) { | |
206 | APP_FREE(thisEvInfo->StatusCodes); | |
207 | } | |
208 | if(thisEvInfo->UniqueRecord) { | |
209 | CSSM_RETURN crtn = | |
210 | CSSM_DL_FreeUniqueRecord(thisEvInfo->DlDbHandle, | |
211 | thisEvInfo->UniqueRecord); | |
212 | if(crtn) { | |
213 | cuPrintError("CSSM_DL_FreeUniqueRecord", crtn); | |
214 | break; | |
215 | } | |
216 | thisEvInfo->UniqueRecord = NULL; | |
217 | } | |
218 | } /* for each cert info */ | |
219 | APP_FREE(evp->Evidence); | |
220 | evp->Evidence = NULL; | |
221 | break; | |
222 | } /* CSSM_EVIDENCE_FORM_APPLE_CERT_INFO */ | |
223 | } /* switch(evp->EvidenceForm) */ | |
224 | } /* for each evidence */ | |
225 | if(ctx->Evidence) { | |
226 | APP_FREE(ctx->Evidence); | |
227 | ctx->Evidence = NULL; | |
228 | } | |
229 | return crtn; | |
230 | } | |
231 | ||
232 | static int testError(CSSM_BOOL quiet) | |
233 | { | |
234 | char resp; | |
235 | ||
236 | if(quiet) { | |
237 | printf("\n***Test aborting.\n"); | |
238 | exit(1); | |
239 | } | |
240 | fpurge(stdin); | |
241 | printf("a to abort, c to continue: "); | |
242 | resp = getchar(); | |
243 | return (resp == 'a'); | |
244 | } | |
245 | ||
246 | /* | |
247 | * The heart of CAC certification verification. | |
248 | */ | |
249 | int vfyCert( | |
250 | CSSM_TP_HANDLE tpHand, | |
251 | CSSM_CL_HANDLE clHand, | |
252 | CSSM_CSP_HANDLE cspHand, | |
253 | const void * certData, | |
254 | unsigned certLength, | |
255 | ||
256 | /* these three booleans will eventually come from system preferences */ | |
257 | bool enableCrlCheck, | |
258 | bool requireFullCrlVerify, | |
259 | bool enableFetchFromNet, | |
260 | CSSM_DL_DB_HANDLE_PTR certKeychain, | |
261 | CSSM_DL_DB_HANDLE_PTR crlKeychain) | |
262 | { | |
263 | /* main job is building a CSSM_TP_VERIFY_CONTEXT and its components */ | |
264 | CSSM_TP_VERIFY_CONTEXT vfyCtx; | |
265 | CSSM_TP_CALLERAUTH_CONTEXT authCtx; | |
266 | CSSM_TP_VERIFY_CONTEXT_RESULT vfyResult; | |
267 | ||
268 | memset(&vfyCtx, 0, sizeof(CSSM_TP_VERIFY_CONTEXT)); | |
269 | memset(&authCtx, 0, sizeof(CSSM_TP_CALLERAUTH_CONTEXT)); | |
270 | ||
271 | /* CSSM_TP_CALLERAUTH_CONTEXT components */ | |
272 | /* | |
273 | typedef struct cssm_tp_callerauth_context { | |
274 | CSSM_TP_POLICYINFO Policy; | |
275 | CSSM_TIMESTRING VerifyTime; | |
276 | CSSM_TP_STOP_ON VerificationAbortOn; | |
277 | CSSM_TP_VERIFICATION_RESULTS_CALLBACK CallbackWithVerifiedCert; | |
278 | uint32 NumberOfAnchorCerts; | |
279 | CSSM_DATA_PTR AnchorCerts; | |
280 | CSSM_DL_DB_LIST_PTR DBList; | |
281 | CSSM_ACCESS_CREDENTIALS_PTR CallerCredentials; | |
282 | } CSSM_TP_CALLERAUTH_CONTEXT, *CSSM_TP_CALLERAUTH_CONTEXT_PTR; | |
283 | */ | |
284 | /* two policies */ | |
285 | CSSM_FIELD policyIds[2]; | |
286 | CSSM_APPLE_TP_CRL_OPTIONS crlOpts; | |
287 | policyIds[0].FieldOid = CSSMOID_APPLE_X509_BASIC; | |
288 | policyIds[0].FieldValue.Data = NULL; | |
289 | policyIds[0].FieldValue.Length = 0; | |
290 | if(enableCrlCheck) { | |
291 | policyIds[1].FieldOid = CSSMOID_APPLE_TP_REVOCATION_CRL; | |
292 | policyIds[1].FieldValue.Data = (uint8 *)&crlOpts; | |
293 | policyIds[1].FieldValue.Length = sizeof(crlOpts); | |
294 | crlOpts.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION; | |
295 | crlOpts.CrlFlags = 0; | |
296 | if(requireFullCrlVerify) { | |
297 | crlOpts.CrlFlags |= CSSM_TP_ACTION_REQUIRE_CRL_PER_CERT; | |
298 | } | |
299 | if(enableFetchFromNet) { | |
300 | crlOpts.CrlFlags |= CSSM_TP_ACTION_FETCH_CRL_FROM_NET; | |
301 | } | |
302 | /* optional, may well not exist */ | |
303 | /* FIXME: as of 12/4/02 this field is ignored by the TP | |
304 | * and may well go away from the CSSM_APPLE_TP_CRL_OPTIONS | |
305 | * struct. */ | |
306 | crlOpts.crlStore = crlKeychain; | |
307 | ||
308 | authCtx.Policy.NumberOfPolicyIds = 2; | |
309 | } | |
310 | else { | |
311 | /* No CRL checking */ | |
312 | authCtx.Policy.NumberOfPolicyIds = 1; | |
313 | } | |
314 | authCtx.Policy.PolicyIds = policyIds; | |
315 | authCtx.Policy.PolicyControl = NULL; | |
316 | ||
317 | authCtx.VerifyTime = NULL; | |
318 | authCtx.VerificationAbortOn = CSSM_TP_STOP_ON_POLICY; | |
319 | authCtx.CallbackWithVerifiedCert = NULL; | |
320 | ||
321 | /* anchors */ | |
322 | const CSSM_DATA *anchors; | |
323 | uint32 anchorCount; | |
324 | OSStatus ortn; | |
325 | ortn = SecTrustGetCSSMAnchorCertificates(&anchors, &anchorCount); | |
326 | if(ortn) { | |
327 | printf("SecTrustGetCSSMAnchorCertificates returned %u\n", ortn); | |
328 | return -1; | |
329 | } | |
330 | authCtx.NumberOfAnchorCerts = anchorCount; | |
331 | authCtx.AnchorCerts = const_cast<CSSM_DATA_PTR>(anchors); | |
332 | ||
333 | /* DBList of intermediate certs and CRLs */ | |
334 | CSSM_DL_DB_HANDLE handles[2]; | |
335 | unsigned numDbs = 0; | |
336 | if(certKeychain != NULL) { | |
337 | handles[0] = *certKeychain; | |
338 | numDbs++; | |
339 | } | |
340 | if(crlKeychain != NULL) { | |
341 | handles[numDbs] = *crlKeychain; | |
342 | numDbs++; | |
343 | } | |
344 | CSSM_DL_DB_LIST dlDbList; | |
345 | dlDbList.NumHandles = numDbs; | |
346 | dlDbList.DLDBHandle = &handles[0]; | |
347 | ||
348 | authCtx.DBList = &dlDbList; | |
349 | authCtx.CallerCredentials = NULL; | |
350 | ||
351 | /* CSSM_TP_VERIFY_CONTEXT */ | |
352 | vfyCtx.ActionData.Data = NULL; | |
353 | vfyCtx.ActionData.Length = 0; | |
354 | vfyCtx.Action = CSSM_TP_ACTION_DEFAULT; | |
355 | vfyCtx.Cred = &authCtx; | |
356 | ||
357 | /* cook up cert group */ | |
358 | CSSM_CERTGROUP cssmCerts; | |
359 | cssmCerts.CertType = CSSM_CERT_X_509v3; | |
360 | cssmCerts.CertEncoding = CSSM_CERT_ENCODING_DER; | |
361 | cssmCerts.NumCerts = 1; | |
362 | CSSM_DATA cert; | |
363 | cert.Data = (uint8 *)certData; | |
364 | cert.Length = certLength; | |
365 | cssmCerts.GroupList.CertList = &cert; | |
366 | cssmCerts.CertGroupType = CSSM_CERTGROUP_DATA; | |
367 | ||
368 | CSSM_RETURN crtn = CSSM_TP_CertGroupVerify(tpHand, | |
369 | clHand, | |
370 | cspHand, | |
371 | &cssmCerts, | |
372 | &vfyCtx, | |
373 | &vfyResult); | |
374 | if(crtn) { | |
375 | cuPrintError("CSSM_TP_CertGroupVerify", crtn); | |
376 | } | |
377 | else { | |
378 | printf("...verify successful\n"); | |
379 | } | |
380 | dumpVfyResult(&vfyResult); | |
381 | freeVfyResult(&vfyResult); | |
382 | if(crtn) { | |
383 | return testError(0); | |
384 | } | |
385 | else { | |
386 | return 0; | |
387 | } | |
388 | } | |
389 | ||
390 | int main(int argc, char **argv) | |
391 | { | |
392 | int rtn; | |
393 | CSSM_RETURN crtn; | |
394 | CSSM_DL_HANDLE dlHand; | |
395 | CSSM_CSP_HANDLE cspHand; | |
396 | CSSM_CL_HANDLE clHand; | |
397 | CSSM_TP_HANDLE tpHand; | |
398 | CSSM_DL_DB_HANDLE certKeychain; | |
399 | CSSM_DL_DB_HANDLE crlKeychain; | |
400 | CSSM_DL_DB_HANDLE_PTR certKeychainPtr = NULL; | |
401 | CSSM_DL_DB_HANDLE_PTR crlKeychainPtr = NULL; | |
402 | unsigned char *certData; | |
403 | unsigned certLength; | |
404 | bool requireFullCrlVerify = true; | |
405 | bool enableFetchFromNet = true; | |
406 | bool enableCrlCheck = true; | |
407 | int arg; | |
408 | char *argp; | |
409 | ||
410 | if(argc < 2) { | |
411 | usage(argv); | |
412 | } | |
413 | for(arg=2; arg<argc; arg++) { | |
414 | argp = argv[arg]; | |
415 | switch(argp[0]) { | |
416 | case 'a': | |
417 | requireFullCrlVerify = false; | |
418 | break; | |
419 | case 'd': | |
420 | enableCrlCheck = false; | |
421 | break; | |
422 | case 'n': | |
423 | enableFetchFromNet = false; | |
424 | break; | |
425 | default: | |
426 | usage(argv); | |
427 | } | |
428 | } | |
429 | ||
430 | /* in the real world all these should be verified to be nonzero */ | |
431 | cspHand = cuCspStartup(CSSM_TRUE); | |
432 | clHand = cuClStartup(); | |
433 | tpHand = cuTpStartup(); | |
434 | dlHand = cuDlStartup(); | |
435 | ||
436 | /* get the cert */ | |
437 | rtn = readFile(argv[1], &certData, &certLength); | |
438 | if(rtn) { | |
439 | printf("***Error reading cert file %s\n", argv[1]); | |
440 | exit(1); | |
441 | } | |
442 | ||
443 | /* get the intermediate certs */ | |
444 | crtn = CSSM_DL_DbOpen(dlHand, | |
445 | X509_CERT_DB, | |
446 | NULL, // DbLocation | |
447 | CSSM_DB_ACCESS_READ, | |
448 | NULL, // CSSM_ACCESS_CREDENTIALS *AccessCred | |
449 | NULL, // void *OpenParameters | |
450 | &certKeychain.DBHandle); | |
451 | if(crtn) { | |
452 | cuPrintError("CSSM_DL_DbOpen", crtn); | |
453 | printf("***Error opening intermediate cert file %s. This op will" | |
454 | "probably fail.n", X509_CERT_DB); | |
455 | } | |
456 | else { | |
457 | certKeychain.DLHandle = dlHand; | |
458 | certKeychainPtr = &certKeychain; | |
459 | } | |
460 | ||
461 | /* and the CRL cache */ | |
462 | crtn = CSSM_DL_DbOpen(dlHand, | |
463 | X509_CRL_DB, | |
464 | NULL, // DbLocation | |
465 | CSSM_DB_ACCESS_READ, | |
466 | NULL, // CSSM_ACCESS_CREDENTIALS *AccessCred | |
467 | NULL, // void *OpenParameters | |
468 | &crlKeychain.DBHandle); | |
469 | if(crtn == CSSM_OK) { | |
470 | crlKeychain.DLHandle = dlHand; | |
471 | crlKeychainPtr = &crlKeychain; | |
472 | } | |
473 | ||
474 | /* go for it */ | |
475 | vfyCert(tpHand, clHand, cspHand, certData, certLength, | |
476 | enableCrlCheck, requireFullCrlVerify, enableFetchFromNet, | |
477 | certKeychainPtr, crlKeychainPtr); | |
478 | ||
479 | /* Cleanup - release handles, free certData, etc. */ | |
480 | free(certData); | |
481 | if(crlKeychainPtr != NULL) { | |
482 | CSSM_DL_DbClose(crlKeychain); | |
483 | } | |
484 | if(certKeychainPtr != NULL) { | |
485 | CSSM_DL_DbClose(certKeychain); | |
486 | } | |
487 | CSSM_ModuleDetach(dlHand); | |
488 | CSSM_ModuleDetach(clHand); | |
489 | CSSM_ModuleDetach(tpHand); | |
490 | CSSM_ModuleDetach(cspHand); | |
491 | ||
492 | return 0; | |
493 | ||
494 | } |