1/*
2 * crlUtils.cpp - CRL CL/TP/DL utilities.
3 */
4#include <Security/cssmtype.h>
5#include <Security/cssmapi.h>
6#include <utilLib/common.h>
7#include <clAppUtils/timeStr.h>
8#include <clAppUtils/crlUtils.h>
9#include <Security/oidsattr.h>
10#include <Security/oidscert.h>
11#include <Security/oidscrl.h>
12#include <stdlib.h>
13#include <stdio.h>
14#include <string.h>
15#include <security_cdsa_utilities/Schema.h>				/* private API */
16
17/* crlAddCrlToDb() removed due to build problem; obsoleted in favor of
18 * cuAddCrlToDb() in security_cdsa_utils
19 */
20#define CRL_ADD_TO_DB		0
21
22#if		CRL_ADD_TO_DB
23#include <Security/SecCertificatePriv.h>	/* private */
24											/* SecInferLabelFromX509Name() */
25
26/*
27 * Update an existing DLDB to be CRL-capable.
28 */
29static CSSM_RETURN crlAddCrlSchema(
30	CSSM_DL_DB_HANDLE	dlDbHand)
31{
32	return CSSM_DL_CreateRelation(dlDbHand,
33		CSSM_DL_DB_RECORD_X509_CRL,
34		"CSSM_DL_DB_RECORD_X509_CRL",
35		Security::KeychainCore::Schema::X509CrlSchemaAttributeCount,
36		Security::KeychainCore::Schema::X509CrlSchemaAttributeList,
37		Security::KeychainCore::Schema::X509CrlSchemaIndexCount,
38		Security::KeychainCore::Schema::X509CrlSchemaIndexList);
39}
40
41static void crlInferCrlLabel(
42	const CSSM_X509_NAME 	*x509Name,
43	CSSM_DATA				*label)	 // not mallocd; contents are
44									 //    from the x509Name
45{
46	/* use private API for common "infer label" logic */
47	const CSSM_DATA *printValue = SecInferLabelFromX509Name(x509Name);
48	if(printValue == NULL) {
49		/* punt! */
50		label->Data = (uint8 *)"X509 CRL";
51		label->Length = 8;
52	}
53	else {
54		*label = *printValue;
55	}
56}
57
58/*
59 * Search extensions for specified OID, assumed to have underlying
60 * value type of uint32; returns the value and true if found.
61 */
62static bool crlSearchNumericExtension(
63	const CSSM_X509_EXTENSIONS	*extens,
64	const CSSM_OID				*oid,
65	uint32						*val)
66{
67	for(uint32 dex=0; dex<extens->numberOfExtensions; dex++) {
68		const CSSM_X509_EXTENSION *exten = &extens->extensions[dex];
69		if(!appCompareCssmData(&exten->extnId, oid)) {
70			continue;
71		}
72		if(exten->format != CSSM_X509_DATAFORMAT_PARSED) {
73			printf("***Malformed extension\n");
74			continue;
75		}
76		*val = *((uint32 *)exten->value.parsedValue);
77		return true;
78	}
79	return false;
80}
81
82/*
83 * Add a CRL to an existing DL/DB.
84 */
85#define MAX_CRL_ATTRS			8
86
87CSSM_RETURN crlAddCrlToDb(
88	CSSM_DL_DB_HANDLE	dlDbHand,
89	CSSM_CL_HANDLE		clHand,
90	const CSSM_DATA		*crl)
91{
92	CSSM_DB_ATTRIBUTE_DATA			attrs[MAX_CRL_ATTRS];
93	CSSM_DB_RECORD_ATTRIBUTE_DATA	recordAttrs;
94	CSSM_DB_ATTRIBUTE_DATA_PTR		attr = &attrs[0];
95	CSSM_DATA						crlTypeData;
96	CSSM_DATA						crlEncData;
97	CSSM_DATA						printNameData;
98	CSSM_RETURN						crtn;
99	CSSM_DB_UNIQUE_RECORD_PTR		recordPtr;
100	CSSM_DATA_PTR					issuer;		// mallocd by CL
101	CSSM_DATA_PTR					crlValue;	// ditto
102	uint32							numFields;
103	CSSM_HANDLE						result;
104	CSSM_CRL_ENCODING 				crlEnc = CSSM_CRL_ENCODING_DER;
105	const CSSM_X509_SIGNED_CRL 		*signedCrl;
106	const CSSM_X509_TBS_CERTLIST 	*tbsCrl;
107	CSSM_CRL_TYPE 					crlType;
108	CSSM_DATA 						thisUpdateData = {0, NULL};
109	CSSM_DATA 						nextUpdateData = {0, NULL};
110	char							*thisUpdate, *nextUpdate;
111	unsigned						timeLen;
112	uint32							crlNumber;
113	uint32							deltaCrlNumber;
114	CSSM_DATA						crlNumberData;
115	CSSM_DATA						deltaCrlNumberData;
116	bool							crlNumberPresent = false;
117	bool							deltaCrlPresent = false;
118
119	/* get normalized issuer name as Issuer attr */
120	crtn = CSSM_CL_CrlGetFirstFieldValue(clHand,
121		crl,
122		&CSSMOID_X509V1IssuerName,
123		&result,
124		&numFields,
125		&issuer);
126	if(crtn) {
127		printError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn);
128		return crtn;
129	}
130	CSSM_CL_CrlAbortQuery(clHand, result);
131
132	/* get parsed CRL from the CL */
133	crtn = CSSM_CL_CrlGetFirstFieldValue(clHand,
134		crl,
135		&CSSMOID_X509V2CRLSignedCrlCStruct,
136		&result,
137		&numFields,
138		&crlValue);
139	if(crtn) {
140		printError("CSSM_CL_CrlGetFirstFieldValue(Issuer)", crtn);
141		return crtn;
142	}
143	CSSM_CL_CrlAbortQuery(clHand, result);
144	if(crlValue == NULL) {
145		printf("***CSSM_CL_CrlGetFirstFieldValue: value error (1)\n");
146		return CSSMERR_CL_INVALID_CRL_POINTER;
147	}
148	if((crlValue->Data == NULL) ||
149	   (crlValue->Length != sizeof(CSSM_X509_SIGNED_CRL))) {
150		printf("***CSSM_CL_CrlGetFirstFieldValue: value error (2)\n");
151		return CSSMERR_CL_INVALID_CRL_POINTER;
152	}
153	signedCrl = (const CSSM_X509_SIGNED_CRL *)crlValue->Data;
154	tbsCrl = &signedCrl->tbsCertList;
155
156	/* CrlType inferred from version */
157	if(tbsCrl->version.Length == 0) {
158		/* should never happen... */
159		crlType = CSSM_CRL_TYPE_X_509v1;
160	}
161	else {
162		uint8 vers = tbsCrl->version.Data[tbsCrl->version.Length - 1];
163		switch(vers) {
164			case 0:
165				crlType = CSSM_CRL_TYPE_X_509v1;
166				break;
167			case 1:
168				crlType = CSSM_CRL_TYPE_X_509v2;
169				break;
170			default:
171				printf("***Unknown version in CRL (%u)\n", vers);
172				crlType = CSSM_CRL_TYPE_X_509v1;
173				break;
174		}
175	}
176	crlTypeData.Data = (uint8 *)&crlType;
177	crlTypeData.Length = sizeof(CSSM_CRL_TYPE);
178	/* encoding more-or-less assumed here */
179	crlEncData.Data = (uint8 *)&crlEnc;
180	crlEncData.Length = sizeof(CSSM_CRL_ENCODING);
181
182	/* printName inferred from issuer */
183	crlInferCrlLabel(&tbsCrl->issuer, &printNameData);
184
185	/* cook up CSSM_TIMESTRING versions of this/next update */
186	thisUpdate = x509TimeToCssmTimestring(&tbsCrl->thisUpdate, &timeLen);
187	if(thisUpdate == NULL) {
188		printf("***Badly formatted thisUpdate\n");
189	}
190	else {
191		thisUpdateData.Data = (uint8 *)thisUpdate;
192		thisUpdateData.Length = timeLen;
193	}
194	nextUpdate = x509TimeToCssmTimestring(&tbsCrl->nextUpdate, &timeLen);
195	if(nextUpdate == NULL) {
196		printf("***Badly formatted nextUpdate\n");
197	}
198	else {
199		nextUpdateData.Data = (uint8 *)nextUpdate;
200		nextUpdateData.Length = timeLen;
201	}
202
203	/* optional CrlNumber and DeltaCrlNumber */
204	if(crlSearchNumericExtension(&tbsCrl->extensions,
205			&CSSMOID_CrlNumber,
206			&crlNumber)) {
207		crlNumberData.Data = (uint8 *)&crlNumber;
208		crlNumberData.Length = sizeof(uint32);
209		crlNumberPresent = true;
210	}
211	if(crlSearchNumericExtension(&tbsCrl->extensions,
212			&CSSMOID_DeltaCrlIndicator,
213			&deltaCrlNumber)) {
214		deltaCrlNumberData.Data = (uint8 *)&deltaCrlNumber;
215		deltaCrlNumberData.Length = sizeof(uint32);
216		deltaCrlPresent = true;
217	}
218	/* we spec six attributes, skipping alias and URI */
219	attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
220	attr->Info.Label.AttributeName = "CrlType";
221	attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
222	attr->NumberOfValues = 1;
223	attr->Value = &crlTypeData;
224	attr++;
225
226	attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
227	attr->Info.Label.AttributeName = "CrlEncoding";
228	attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
229	attr->NumberOfValues = 1;
230	attr->Value = &crlEncData;
231	attr++;
232
233	attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
234	attr->Info.Label.AttributeName = "PrintName";
235	attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
236	attr->NumberOfValues = 1;
237	attr->Value = &printNameData;
238	attr++;
239
240	attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
241	attr->Info.Label.AttributeName = "Issuer";
242	attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
243	attr->NumberOfValues = 1;
244	attr->Value = issuer;
245	attr++;
246
247	attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
248	attr->Info.Label.AttributeName = "ThisUpdate";
249	attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
250	attr->NumberOfValues = 1;
251	attr->Value = &thisUpdateData;
252	attr++;
253
254	attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
255	attr->Info.Label.AttributeName = "NextUpdate";
256	attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
257	attr->NumberOfValues = 1;
258	attr->Value = &nextUpdateData;
259	attr++;
260
261	/* now the optional attributes */
262	if(crlNumberPresent) {
263		attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
264		attr->Info.Label.AttributeName = "CrlNumber";
265		attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
266		attr->NumberOfValues = 1;
267		attr->Value = &crlNumberData;
268		attr++;
269	}
270	if(deltaCrlPresent) {
271		attr->Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
272		attr->Info.Label.AttributeName = "DeltaCrlNumber";
273		attr->Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_UINT32;
274		attr->NumberOfValues = 1;
275		attr->Value = &deltaCrlNumberData;
276		attr++;
277	}
278
279	recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_X509_CRL;
280	recordAttrs.SemanticInformation = 0;
281	recordAttrs.NumberOfAttributes = attr - attrs;
282	recordAttrs.AttributeData = attrs;
283
284	crtn = CSSM_DL_DataInsert(dlDbHand,
285		CSSM_DL_DB_RECORD_X509_CRL,
286		&recordAttrs,
287		crl,
288		&recordPtr);
289	if(crtn == CSSMERR_DL_INVALID_RECORDTYPE) {
290		/* gross hack of inserting this "new" schema that
291		 * Keychain didn't specify */
292		crtn = crlAddCrlSchema(dlDbHand);
293		if(crtn == CSSM_OK) {
294			/* Retry with a fully capable DLDB */
295			crtn = CSSM_DL_DataInsert(dlDbHand,
296				CSSM_DL_DB_RECORD_X509_CRL,
297				&recordAttrs,
298				crl,
299				&recordPtr);
300		}
301	}
302	if(crtn) {
303		printError("CSSM_DL_DataInsert", crtn);
304	}
305	else {
306		CSSM_DL_FreeUniqueRecord(dlDbHand, recordPtr);
307	}
308
309	/* free all the stuff we allocated to get here */
310  	CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1IssuerName, issuer);
311  	CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V2CRLSignedCrlCStruct,
312			crlValue);
313	free(thisUpdate);
314	free(nextUpdate);
315	return crtn;
316}
317
318#endif  /* CRL_ADD_TO_DB */
319