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
22static 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
34static 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
44static 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
86static 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 */
134void 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 */
167CSSM_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
232static 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 */
249int 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
390int 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}
495