1/*
2 * Copyright (c) 2003-2005 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please
7 * obtain a copy of the License at http://www.apple.com/publicsource and
8 * read it before using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
12 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
13 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
14 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
15 * Please see the License for the specific language governing rights and
16 * limitations under the License.
17 */
18
19/*
20 * CertParser.h - cert parser with autorelease of fetched fields
21 *
22 * Created 24 October 2003 by Doug Mitchell
23 */
24
25#include "CertParser.h"
26#import <AvailabilityMacros.h>
27
28#define CP_DEBUG	1
29#if		CP_DEBUG
30#define dprintf(args...)  printf(args)
31#else
32#define dprintf(args...)
33#endif
34
35#pragma mark --- CP_FetchedField ---
36
37class CP_FetchedField
38{
39public:
40	/* construct one fetched field (which will be stored in CertParser's
41	 * mFetchedFields) */
42	CP_FetchedField(
43		const CSSM_OID	&fieldOid,
44		CSSM_DATA_PTR   fieldData,
45		CSSM_CL_HANDLE  clHand);
46
47	/* Free the field via CL */
48	~CP_FetchedField();
49private:
50	CSSM_OID		mFieldOid;
51	CSSM_DATA_PTR   mFieldData;
52	CSSM_CL_HANDLE  mClHand;
53};
54
55CP_FetchedField::CP_FetchedField(
56	const CSSM_OID	&fieldOid,
57	CSSM_DATA_PTR   fieldData,
58	CSSM_CL_HANDLE  clHand)
59	: mFieldOid(fieldOid), mFieldData(fieldData), mClHand(clHand)
60{
61}
62
63/* Free the field via CL */
64CP_FetchedField::~CP_FetchedField()
65{
66	CSSM_CL_FreeFieldValue(mClHand, &mFieldOid, mFieldData);
67}
68
69#pragma mark --- CertParser implementation ---
70
71/* Construct with or without data - you can add the data later with
72 * initWithData() to parse without exceptions */
73CertParser::CertParser()
74{
75	initFields();
76}
77
78CertParser::CertParser(
79	CSSM_CL_HANDLE		clHand)
80{
81	initFields();
82	mClHand = clHand;
83}
84
85CertParser::CertParser(
86	CSSM_CL_HANDLE		clHand,
87	const CSSM_DATA 	&certData)
88{
89	initFields();
90	mClHand = clHand;
91	CSSM_RETURN crtn = initWithData(certData);
92	if(crtn) {
93		throw ((int)crtn);
94	}
95}
96
97CertParser::CertParser(
98	SecCertificateRef 	secCert)
99{
100	initFields();
101	OSStatus ortn = initWithSecCert(secCert);
102	if(ortn) {
103		throw ((int)ortn);
104	}
105}
106
107/* frees all the fields we fetched */
108CertParser::~CertParser()
109{
110	if(mClHand && mCacheHand) {
111		CSSM_RETURN crtn = CSSM_CL_CertAbortCache(mClHand, mCacheHand);
112		if(crtn) {
113			/* almost certainly a bug */
114			printf("Internal Error: CertParser error on free.");
115			cssmPerror("CSSM_CL_CertAbortCache", crtn);
116		}
117	}
118	vector<CP_FetchedField *>::iterator iter;
119	for(iter=mFetchedFields.begin(); iter!=mFetchedFields.end(); iter++) {
120		delete *iter;
121	}
122}
123
124/* common init for all constructors */
125void CertParser::initFields()
126{
127	mClHand = 0;
128	mCacheHand = 0;
129}
130
131/*** NO MORE EXCEPTIONS ***/
132
133/*
134 * No cert- or CDSA-related exceptions thrown by remainder.
135 * This is the core initializer: have the CL parse and cache the cert.
136 */
137CSSM_RETURN CertParser::initWithData(
138	const CSSM_DATA 	&certData)
139{
140	assert(mClHand != 0);
141	CSSM_RETURN crtn = CSSM_CL_CertCache(mClHand, &certData, &mCacheHand);
142	#if CP_DEBUG
143	if(crtn) {
144		cssmPerror("CSSM_CL_CertCache", crtn);
145	}
146	#endif
147	return crtn;
148}
149
150OSStatus CertParser::initWithSecCert(
151	SecCertificateRef 	secCert)
152{
153	OSStatus ortn;
154	CSSM_DATA certData;
155
156	assert(mClHand == 0);
157	ortn = SecCertificateGetCLHandle(secCert, &mClHand);
158	if(ortn) {
159		return ortn;
160	}
161	ortn = SecCertificateGetData(secCert, &certData);
162	if(ortn) {
163		return ortn;
164	}
165	return (OSStatus)initWithData(certData);
166}
167
168CSSM_RETURN CertParser::initWithCFData(
169	CFDataRef			cfData)
170{
171	CSSM_DATA   cdata;
172
173	cdata.Data = (uint8 *)CFDataGetBytePtr(cfData);
174	cdata.Length = CFDataGetLength(cfData);
175	return initWithData(cdata);
176}
177
178/*
179 * Obtain atrbitrary field from cached cert. This class takes care of freeing
180 * the field in its destructor.
181 *
182 * Returns NULL if field not found (not exception).
183 *
184 * Caller optionally specifies field length to check - specifying zero means
185 * "don't care, don't check". Actual field length always returned in fieldLength.
186 */
187const void *CertParser::fieldForOid(
188	const CSSM_OID		&oid,
189	CSSM_SIZE			&fieldLength)		// IN/OUT
190{
191	CSSM_RETURN crtn;
192
193	uint32 NumberOfFields = 0;
194	CSSM_HANDLE resultHand = 0;
195	CSSM_DATA_PTR fieldData = NULL;
196
197	assert(mClHand != 0);
198	assert(mCacheHand != 0);
199	crtn = CSSM_CL_CertGetFirstCachedFieldValue(
200		mClHand,
201		mCacheHand,
202	    &oid,
203	    &resultHand,
204	    &NumberOfFields,
205		&fieldData);
206	if(crtn) {
207		/* not an error; just means that the cert doesn't have this field */
208		return NULL;
209	}
210	assert(NumberOfFields == 1);
211  	CSSM_CL_CertAbortQuery(mClHand, resultHand);
212
213	if(fieldLength) {
214		if(fieldLength != fieldData->Length) {
215			/* FIXME what's a good way to log in this situation? */
216			printf("***CertParser::fieldForOid: field length mismatch\n");
217			return NULL;
218		}
219	}
220	/* Store the OID and the field for autorelease */
221	CP_FetchedField *cpField = new CP_FetchedField(oid, fieldData, mClHand);
222	mFetchedFields.push_back(cpField);
223	fieldLength = fieldData->Length;
224	return fieldData->Data;
225}
226
227/*
228 * Conveneince routine to fetch an extension we "know" the CL can parse.
229 * The return value gets cast to one of the CE_Data types.
230 */
231const void *CertParser::extensionForOid(
232	const CSSM_OID		&oid)
233{
234	CSSM_SIZE len = sizeof(CSSM_X509_EXTENSION);
235	CSSM_X509_EXTENSION *cssmExt =
236		(CSSM_X509_EXTENSION *)fieldForOid(oid,	len);
237	if(cssmExt) {
238		if(cssmExt->format != CSSM_X509_DATAFORMAT_PARSED) {
239			printf("***Badly formatted extension");
240			return NULL;
241		}
242		return cssmExt->value.parsedValue;
243	}
244	else {
245		return NULL;
246	}
247}
248
249