1/*
2 * Copyright (c) 2002-2007,2012 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
24//
25// Certificate.cpp
26//
27#include <security_keychain/Certificate.h>
28#include <security_cdsa_utilities/Schema.h>
29#include <Security/oidscert.h>
30#include <Security/oidsattr.h>
31#include <Security/SecCertificate.h>
32#include <Security/SecCertificatePriv.h>
33#include <security_cdsa_client/cspclient.h>
34#include <security_keychain/KeyItem.h>
35#include <security_keychain/KCCursor.h>
36#include <vector>
37#include <CommonCrypto/CommonDigestSPI.h>
38#include <SecBase.h>
39
40using namespace KeychainCore;
41
42CL
43Certificate::clForType(CSSM_CERT_TYPE type)
44{
45	return CL(gGuidAppleX509CL);
46}
47
48Certificate::Certificate(const CSSM_DATA &data, CSSM_CERT_TYPE type, CSSM_CERT_ENCODING encoding) :
49	ItemImpl(CSSM_DL_DB_RECORD_X509_CERTIFICATE, reinterpret_cast<SecKeychainAttributeList *>(NULL), UInt32(data.Length), reinterpret_cast<const void *>(data.Data)),
50	mHaveTypeAndEncoding(true),
51	mPopulated(false),
52	mType(type),
53	mEncoding(encoding),
54	mCL(clForType(type)),
55	mCertHandle(0),
56	mV1SubjectPublicKeyCStructValue(NULL),
57	mV1SubjectNameCStructValue(NULL),
58	mV1IssuerNameCStructValue(NULL),
59	mSha1Hash(NULL)
60{
61	if (data.Length == 0 || data.Data == NULL)
62		MacOSError::throwMe(errSecParam);
63}
64
65// db item constructor
66Certificate::Certificate(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) :
67	ItemImpl(keychain, primaryKey, uniqueId),
68	mHaveTypeAndEncoding(false),
69	mPopulated(false),
70	mCL(NULL),
71	mCertHandle(0),
72	mV1SubjectPublicKeyCStructValue(NULL),
73	mV1SubjectNameCStructValue(NULL),
74	mV1IssuerNameCStructValue(NULL),
75	mSha1Hash(NULL)
76{
77}
78
79
80
81Certificate* Certificate::make(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId)
82{
83	Certificate* c = new Certificate(keychain, primaryKey, uniqueId);
84	keychain->addItem(primaryKey, c);
85	return c;
86}
87
88
89
90Certificate* Certificate::make(const Keychain &keychain, const PrimaryKey &primaryKey)
91{
92	Certificate* c = new Certificate(keychain, primaryKey);
93	keychain->addItem(primaryKey, c);
94	return c;
95}
96
97
98
99
100// PrimaryKey item constructor
101Certificate::Certificate(const Keychain &keychain, const PrimaryKey &primaryKey) :
102	ItemImpl(keychain, primaryKey),
103	mHaveTypeAndEncoding(false),
104	mPopulated(false),
105	mCL(NULL),
106	mCertHandle(0),
107	mV1SubjectPublicKeyCStructValue(NULL),
108	mV1SubjectNameCStructValue(NULL),
109	mV1IssuerNameCStructValue(NULL),
110	mSha1Hash(NULL)
111{
112	// @@@ In this case we don't know the type...
113}
114
115Certificate::Certificate(Certificate &certificate) :
116	ItemImpl(certificate),
117	mHaveTypeAndEncoding(certificate.mHaveTypeAndEncoding),
118	mPopulated(false /* certificate.mPopulated */),
119	mType(certificate.mType),
120	mEncoding(certificate.mEncoding),
121	mCL(certificate.mCL),
122	mCertHandle(0),
123	mV1SubjectPublicKeyCStructValue(NULL),
124	mV1SubjectNameCStructValue(NULL),
125	mV1IssuerNameCStructValue(NULL),
126	mSha1Hash(NULL)
127{
128}
129
130Certificate::~Certificate()
131try
132{
133	if (mV1SubjectPublicKeyCStructValue)
134		releaseFieldValue(CSSMOID_X509V1SubjectPublicKeyCStruct, mV1SubjectPublicKeyCStructValue);
135
136	if (mCertHandle && mCL)
137		CSSM_CL_CertAbortCache(mCL->handle(), mCertHandle);
138
139	if (mV1SubjectNameCStructValue)
140		releaseFieldValue(CSSMOID_X509V1SubjectNameCStruct, mV1SubjectNameCStructValue);
141
142	if (mV1IssuerNameCStructValue)
143		releaseFieldValue(CSSMOID_X509V1IssuerNameCStruct, mV1IssuerNameCStructValue);
144
145	if (mSha1Hash)
146		CFRelease(mSha1Hash);
147}
148catch (...)
149{
150}
151
152CSSM_HANDLE
153Certificate::certHandle()
154{
155	StLock<Mutex>_(mMutex);
156	const CSSM_DATA *cert = &data();
157	if (!mCertHandle)
158	{
159		if (CSSM_RETURN retval = CSSM_CL_CertCache(clHandle(), cert, &mCertHandle))
160			CssmError::throwMe(retval);
161	}
162
163	return mCertHandle;
164}
165
166/* Return a zero terminated list of CSSM_DATA_PTR's with the values of the field specified by field.  Caller must call releaseFieldValues to free the storage allocated by this call.  */
167CSSM_DATA_PTR *
168Certificate::copyFieldValues(const CSSM_OID &field)
169{
170	StLock<Mutex>_(mMutex);
171	CSSM_CL_HANDLE clh = clHandle();
172	CSSM_DATA_PTR fieldValue, *fieldValues;
173	CSSM_HANDLE resultsHandle = 0;
174	uint32 numberOfFields = 0;
175	CSSM_RETURN result;
176
177	result = CSSM_CL_CertGetFirstCachedFieldValue(clh, certHandle(), &field, &resultsHandle, &numberOfFields, &fieldValue);
178	if (result)
179	{
180		if (result == CSSMERR_CL_NO_FIELD_VALUES)
181			return NULL;
182
183		CssmError::throwMe(result);
184	}
185
186	fieldValues = new CSSM_DATA_PTR[numberOfFields + 1];
187	fieldValues[0] = fieldValue;
188	fieldValues[numberOfFields] = NULL;
189
190	for (uint32 value = 1; value < numberOfFields; ++value)
191	{
192		CSSM_RETURN cresult = CSSM_CL_CertGetNextCachedFieldValue(clh, resultsHandle, &fieldValues[value]);
193		if (cresult)
194		{
195			fieldValues[value] = NULL;
196			result = cresult;
197			break; // No point in continuing really.
198		}
199	}
200
201	CSSM_CL_CertAbortQuery(clh, resultsHandle);
202
203	if (result)
204	{
205		releaseFieldValues(field, fieldValues);
206		CssmError::throwMe(result);
207	}
208
209	return fieldValues;
210}
211
212void
213Certificate::releaseFieldValues(const CSSM_OID &field, CSSM_DATA_PTR *fieldValues)
214{
215	StLock<Mutex>_(mMutex);
216	if (fieldValues)
217	{
218		CSSM_CL_HANDLE clh = clHandle();
219
220		for (int ix = 0; fieldValues[ix]; ++ix)
221			CSSM_CL_FreeFieldValue(clh, &field, fieldValues[ix]);
222
223		delete[] fieldValues;
224	}
225}
226
227void
228Certificate::addParsedAttribute(const CSSM_DB_ATTRIBUTE_INFO &info, const CSSM_OID &field)
229{
230	StLock<Mutex>_(mMutex);
231	CSSM_DATA_PTR *fieldValues = copyFieldValues(field);
232	if (fieldValues)
233	{
234		CssmDbAttributeData &anAttr = mDbAttributes->add(info);
235		for (int ix = 0; fieldValues[ix]; ++ix)
236			anAttr.add(*fieldValues[ix], *mDbAttributes);
237
238		releaseFieldValues(field, fieldValues);
239	}
240}
241
242void
243Certificate::addSubjectKeyIdentifier()
244{
245	StLock<Mutex>_(mMutex);
246	const CSSM_DB_ATTRIBUTE_INFO &info = Schema::attributeInfo(kSecSubjectKeyIdentifierItemAttr);
247	const CSSM_OID &field = CSSMOID_SubjectKeyIdentifier;
248
249	CSSM_DATA_PTR *fieldValues = copyFieldValues(field);
250	if (fieldValues)
251	{
252		CssmDbAttributeData &anAttr = mDbAttributes->add(info);
253		for (int ix = 0; fieldValues[ix]; ++ix)
254		{
255			const CSSM_X509_EXTENSION *extension = reinterpret_cast<const CSSM_X509_EXTENSION *>(fieldValues[ix]->Data);
256			if (extension == NULL || fieldValues[ix]->Length != sizeof(CSSM_X509_EXTENSION))
257			{
258				assert(extension != NULL && fieldValues[ix]->Length == sizeof(CSSM_X509_EXTENSION));
259				continue;
260			}
261			const CE_SubjectKeyID *skid = reinterpret_cast<CE_SubjectKeyID *>(extension->value.parsedValue);
262			if (skid == NULL)
263			{
264				assert(skid != NULL);
265				continue;
266			}
267			anAttr.add(*skid, *mDbAttributes);
268		}
269
270		releaseFieldValues(field, fieldValues);
271	}
272}
273
274/* Return a CSSM_DATA_PTR with the value of the first field specified by field.  Caller must call releaseFieldValue to free the storage allocated by this call.  */
275CSSM_DATA_PTR
276Certificate::copyFirstFieldValue(const CSSM_OID &field)
277{
278	StLock<Mutex>_(mMutex);
279	CSSM_CL_HANDLE clh = clHandle();
280	CSSM_DATA_PTR fieldValue;
281	CSSM_HANDLE resultsHandle = 0;
282	uint32 numberOfFields = 0;
283	CSSM_RETURN result;
284
285	result = CSSM_CL_CertGetFirstCachedFieldValue(clh, certHandle(), &field, &resultsHandle, &numberOfFields, &fieldValue);
286	if (result)
287	{
288		if (result == CSSMERR_CL_NO_FIELD_VALUES)
289			return NULL;
290
291		CssmError::throwMe(result);
292	}
293
294	result = CSSM_CL_CertAbortQuery(clh, resultsHandle);
295
296	if (result)
297	{
298		releaseFieldValue(field, fieldValue);
299		CssmError::throwMe(result);
300	}
301
302	return fieldValue;
303}
304
305void
306Certificate::releaseFieldValue(const CSSM_OID &field, CSSM_DATA_PTR fieldValue)
307{
308	StLock<Mutex>_(mMutex);
309	if (fieldValue)
310	{
311		CSSM_CL_HANDLE clh = clHandle();
312		CSSM_CL_FreeFieldValue(clh, &field, fieldValue);
313	}
314}
315
316
317
318/*
319	This method computes the keyIdentifier for the public key in the cert as
320	described below:
321
322      The keyIdentifier is composed of the 160-bit SHA-1 hash of the
323      value of the BIT STRING subjectPublicKey (excluding the tag,
324      length, and number of unused bits).
325*/
326const CssmData &
327Certificate::publicKeyHash()
328{
329	StLock<Mutex>_(mMutex);
330	if (mPublicKeyHash.Length)
331		return mPublicKeyHash;
332
333	CSSM_DATA_PTR keyPtr = copyFirstFieldValue(CSSMOID_CSSMKeyStruct);
334	if (keyPtr && keyPtr->Data)
335	{
336		CssmClient::CSP csp(gGuidAppleCSP);
337		CssmClient::PassThrough passThrough(csp);
338		CSSM_KEY *key = reinterpret_cast<CSSM_KEY *>(keyPtr->Data);
339		void *outData;
340		CssmData *cssmData;
341
342		/* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
343		 * associated key blob.
344		 * Key is specified in CSSM_CSP_CreatePassThroughContext.
345		 * Hash is allocated by the CSP, in the App's memory, and returned
346		 * in *outData. */
347		passThrough.key(key);
348		passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
349		cssmData = reinterpret_cast<CssmData *>(outData);
350
351		assert(cssmData->Length <= sizeof(mPublicKeyHashBytes));
352		mPublicKeyHash.Data = mPublicKeyHashBytes;
353		mPublicKeyHash.Length = cssmData->Length;
354		memcpy(mPublicKeyHash.Data, cssmData->Data, cssmData->Length);
355		csp.allocator().free(cssmData->Data);
356		csp.allocator().free(cssmData);
357	}
358
359	releaseFieldValue(CSSMOID_CSSMKeyStruct, keyPtr);
360
361	return mPublicKeyHash;
362}
363
364const CssmData &
365Certificate::subjectKeyIdentifier()
366{
367	StLock<Mutex>_(mMutex);
368	if (mSubjectKeyID.Length)
369		return mSubjectKeyID;
370
371	CSSM_DATA_PTR fieldValue = copyFirstFieldValue(CSSMOID_SubjectKeyIdentifier);
372	if (fieldValue && fieldValue->Data && fieldValue->Length == sizeof(CSSM_X509_EXTENSION))
373	{
374		const CSSM_X509_EXTENSION *extension = reinterpret_cast<const CSSM_X509_EXTENSION *>(fieldValue->Data);
375		const CE_SubjectKeyID *skid = reinterpret_cast<CE_SubjectKeyID *>(extension->value.parsedValue);	// CSSM_DATA
376
377		if (skid->Length <= sizeof(mSubjectKeyIDBytes))
378		{
379			mSubjectKeyID.Data = mSubjectKeyIDBytes;
380			mSubjectKeyID.Length = skid->Length;
381			memcpy(mSubjectKeyID.Data, skid->Data, skid->Length);
382		}
383		else
384			mSubjectKeyID.Length = 0;
385	}
386
387	releaseFieldValue(CSSMOID_SubjectKeyIdentifier, fieldValue);
388
389	return mSubjectKeyID;
390}
391
392
393/*
394 * Given an CSSM_X509_NAME, Find the first (or last) name/value pair with
395 * a printable value which matches the specified OID (e.g., CSSMOID_CommonName).
396 * Returns the CFString-style encoding associated with name component's BER tag.
397 * Returns NULL if none found.
398 */
399static const CSSM_DATA *
400findPrintableField(
401	const CSSM_X509_NAME &x509Name,
402	const CSSM_OID *tvpType,				// NULL means "any printable field"
403	bool lastInstance,						// false means return first instance
404	CFStringBuiltInEncodings *encoding)		// RETURNED
405{
406	const CSSM_DATA *result = NULL;
407	for(uint32 rdnDex=0; rdnDex<x509Name.numberOfRDNs; rdnDex++) {
408		const CSSM_X509_RDN *rdnPtr =
409			&x509Name.RelativeDistinguishedName[rdnDex];
410		for(uint32 tvpDex=0; tvpDex<rdnPtr->numberOfPairs; tvpDex++) {
411			const CSSM_X509_TYPE_VALUE_PAIR *tvpPtr =
412				&rdnPtr->AttributeTypeAndValue[tvpDex];
413
414			/* type/value pair: match caller's specified type? */
415			if(tvpType != NULL && tvpType->Data != NULL) {
416				if(tvpPtr->type.Length != tvpType->Length) {
417					continue;
418				}
419				if(memcmp(tvpPtr->type.Data, tvpType->Data, tvpType->Length)) {
420					/* If we don't have a match but the requested OID is CSSMOID_UserID,
421					 * look for a matching X.500 UserID OID: (0.9.2342.19200300.100.1.1)  */
422					const char cssm_userid_oid[] = { 0x09,0x49,0x86,0x49,0x1f,0x12,0x8c,0xe4,0x81,0x81 };
423					const char x500_userid_oid[] = { 0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x01 };
424					if(!(tvpType->Length == sizeof(cssm_userid_oid) &&
425						!memcmp(tvpPtr->type.Data, x500_userid_oid, sizeof(x500_userid_oid)) &&
426						!memcmp(tvpType->Data, cssm_userid_oid, sizeof(cssm_userid_oid)))) {
427						continue;
428					}
429				}
430			}
431
432			/* printable? */
433			switch(tvpPtr->valueType) {
434				case BER_TAG_PRINTABLE_STRING:
435				case BER_TAG_IA5_STRING:
436					*encoding = kCFStringEncodingASCII;
437					result = &tvpPtr->value;
438					break;
439				case BER_TAG_PKIX_UTF8_STRING:
440				case BER_TAG_GENERAL_STRING:
441				case BER_TAG_PKIX_UNIVERSAL_STRING:
442					*encoding = kCFStringEncodingUTF8;
443					result = &tvpPtr->value;
444					break;
445				case BER_TAG_T61_STRING:
446				case BER_TAG_VIDEOTEX_STRING:
447				case BER_TAG_ISO646_STRING:
448					*encoding = kCFStringEncodingISOLatin1;
449					result = &tvpPtr->value;
450					break;
451				case BER_TAG_PKIX_BMP_STRING:
452					*encoding = kCFStringEncodingUnicode;
453					result = &tvpPtr->value;
454					break;
455				default:
456					/* not printable */
457					break;
458			}
459			/* if we found a result and we want the first instance, return it now. */
460			if(result && !lastInstance) {
461				return result;
462			}
463
464		}	/* for each pair */
465	}		/* for each RDN */
466
467	/* result is NULL if no printable component was found */
468	return result;
469}
470
471/*
472 * Infer printable label for a given CSSM_X509_NAME. Returns NULL
473 * if no appropriate printable name found. Returns the CFString-style
474 * encoding associated with name component's BER tag. Also optionally
475 * returns Description component and its encoding if present and the
476 * returned name component was one we explicitly requested.
477 */
478static const CSSM_DATA *inferLabelFromX509Name(
479	const CSSM_X509_NAME *x509Name,
480	CFStringBuiltInEncodings *encoding,			// RETURNED
481	const CSSM_DATA **description,				// optionally RETURNED
482	CFStringBuiltInEncodings *descrEncoding)	// RETURNED if description != NULL
483{
484	const CSSM_DATA	*printValue;
485	if(description != NULL) {
486		*description = findPrintableField(*x509Name, &CSSMOID_Description, false, descrEncoding);
487	}
488	/*
489	 * Search order (take the first one found with a printable
490	 * value):
491	 *  -- common name
492	 *  -- Organizational Unit
493	 *  -- Organization
494	 *  -- email address
495	 *  -- field of any kind
496	 */
497	printValue = findPrintableField(*x509Name, &CSSMOID_CommonName, true, encoding);
498	if(printValue != NULL) {
499		return printValue;
500	}
501	printValue = findPrintableField(*x509Name, &CSSMOID_OrganizationalUnitName, false, encoding);
502	if(printValue != NULL) {
503		return printValue;
504	}
505	printValue = findPrintableField(*x509Name, &CSSMOID_OrganizationName, false, encoding);
506	if(printValue != NULL) {
507		return printValue;
508	}
509	printValue = findPrintableField(*x509Name, &CSSMOID_EmailAddress, false, encoding);
510	if(printValue != NULL) {
511		return printValue;
512	}
513	/* if we didn't get one of the above names, don't append description */
514	if(description != NULL) {
515		*description = NULL;
516	}
517	/* take anything */
518	return findPrintableField(*x509Name, NULL, false, encoding);
519}
520
521/*
522 * Infer printable label for a given an CSSM_X509_NAME. Returns NULL
523 * if no appropriate printable name found.
524 */
525const CSSM_DATA *SecInferLabelFromX509Name(
526	const CSSM_X509_NAME *x509Name)
527{
528	/* callees of this routine don't care about the encoding */
529	CFStringBuiltInEncodings encoding = kCFStringEncodingASCII;
530	return inferLabelFromX509Name(x509Name, &encoding, NULL, &encoding);
531}
532
533
534void
535Certificate::inferLabel(bool addLabel, CFStringRef *rtnString)
536{
537	StLock<Mutex>_(mMutex);
538	// Set PrintName and optionally the Alias attribute for this certificate, based on the
539	// X509 SubjectAltName and SubjectName.
540	const CSSM_DATA *printName = NULL;
541	const CSSM_DATA *description = NULL;
542	std::vector<CssmData> emailAddresses;
543	CSSM_DATA puntData;
544	CssmAutoData printPlusDescr(Allocator::standard());
545	CssmData printPlusDescData;
546	CFStringBuiltInEncodings printEncoding = kCFStringEncodingUTF8;
547	CFStringBuiltInEncodings descrEncoding = kCFStringEncodingUTF8;
548
549	// Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
550	const CSSM_OID &sanOid = CSSMOID_SubjectAltName;
551	CSSM_DATA_PTR *sanValues = copyFieldValues(sanOid);
552	const CSSM_OID &snOid = CSSMOID_X509V1SubjectNameCStruct;
553	CSSM_DATA_PTR snValue = copyFirstFieldValue(snOid);
554
555	getNames(sanValues, snValue, GNT_RFC822Name, emailAddresses);
556
557	if (snValue && snValue->Data)
558	{
559		const CSSM_X509_NAME &x509Name = *(const CSSM_X509_NAME *)snValue->Data;
560		printName = inferLabelFromX509Name(&x509Name, &printEncoding,
561			&description, &descrEncoding);
562        if (printName)
563        {
564            /* Don't ever use "Thawte Freemail Member" as the label for a cert.  Instead force
565               a fall back on the email address. */
566            const char tfm[] = "Thawte Freemail Member";
567            if ( (printName->Length == sizeof(tfm) - 1) &&
568			      !memcmp(printName->Data, tfm, sizeof(tfm) - 1)) {
569                printName = NULL;
570			}
571        }
572	}
573
574	/* Do a check to see if a '\0' was at the end of printName and strip it. */
575	CssmData cleanedUpPrintName;
576	if((printName != NULL) &&
577	   (printName->Length != 0) &&
578	   (printEncoding != kCFStringEncodingISOLatin1) &&
579	   (printEncoding != kCFStringEncodingUnicode) &&
580	   (printName->Data[printName->Length - 1] == '\0')) {
581		cleanedUpPrintName.Data = printName->Data;
582		cleanedUpPrintName.Length = printName->Length - 1;
583		printName = &cleanedUpPrintName;
584	}
585
586	if((printName != NULL) && (description != NULL) && (description->Length != 0))
587	{
588		/*
589		 * Munge Print Name (which in this case is the CommonName) and Description
590		 * together with the Description in parentheses. We convert from whatever
591		 * format Print Name and Description are in to UTF8 here.
592		 */
593		CFRef<CFMutableStringRef> combo(CFStringCreateMutable(NULL, 0));
594		CFRef<CFStringRef> cfPrint(CFStringCreateWithBytes(NULL, printName->Data,
595			(CFIndex)printName->Length, printEncoding, true));
596		CssmData cleanedUpDescr(description->Data, description->Length);
597		if ((cleanedUpDescr.Data[cleanedUpDescr.Length - 1] == '\0') &&
598			(descrEncoding != kCFStringEncodingISOLatin1) &&
599			(descrEncoding != kCFStringEncodingUnicode)) {
600			cleanedUpDescr.Length--;
601		}
602		CFRef<CFStringRef> cfDesc(CFStringCreateWithBytes(NULL, cleanedUpDescr.Data,
603			(CFIndex)cleanedUpDescr.Length, descrEncoding, true));
604		CFStringAppend(combo, cfPrint);
605		CFStringAppendCString(combo, " (", kCFStringEncodingASCII);
606		CFStringAppend(combo, cfDesc);
607		CFStringAppendCString(combo, ")", kCFStringEncodingASCII);
608		CFRef<CFDataRef> comboData(CFStringCreateExternalRepresentation(NULL, combo,
609			kCFStringEncodingUTF8, 0));
610		printPlusDescr.copy(CFDataGetBytePtr(comboData), CFDataGetLength(comboData));
611		printPlusDescData = printPlusDescr;
612		printName = &printPlusDescData;
613		printEncoding = kCFStringEncodingUTF8;
614	}
615
616	if (printName == NULL)
617	{
618		/* If the we couldn't find a label use the emailAddress instead. */
619		if (!emailAddresses.empty())
620			printName = &emailAddresses[0];
621		else
622		{
623			/* punt! */
624			puntData.Data = (uint8 *)"X509 Certificate";
625			puntData.Length = 16;
626			printName = &puntData;
627		}
628		printEncoding = kCFStringEncodingUTF8;
629	}
630
631	/* If we couldn't find an email address just use the printName which might be the url or something else useful. */
632	if (emailAddresses.empty())
633		emailAddresses.push_back(CssmData::overlay(*printName));
634
635	/* What do we do with the inferred label - return it or add it mDbAttributes? */
636	if (addLabel)
637	{
638		mDbAttributes->add(Schema::kX509CertificatePrintName, *printName);
639		CssmDbAttributeData &attrData = mDbAttributes->add(Schema::kX509CertificateAlias);
640
641		/* Add the email addresses to attrData and normalize them. */
642		uint32 ix = 0;
643		for (std::vector<CssmData>::const_iterator it = emailAddresses.begin(); it != emailAddresses.end(); ++it, ++ix)
644		{
645			/* Add the email address using the allocator from mDbAttributes. */
646			attrData.add(*it, *mDbAttributes);
647			/* Normalize the emailAddresses in place since attrData already copied it. */
648			normalizeEmailAddress(attrData.Value[ix]);
649		}
650	}
651
652	if (rtnString)
653	{
654		CFStringBuiltInEncodings testEncoding = printEncoding;
655		if(testEncoding == kCFStringEncodingISOLatin1) {
656			// try UTF-8 first
657			testEncoding = kCFStringEncodingUTF8;
658		}
659		*rtnString = CFStringCreateWithBytes(NULL, printName->Data,
660			(CFIndex)printName->Length, testEncoding, true);
661		if(*rtnString == NULL && printEncoding == kCFStringEncodingISOLatin1) {
662			// string cannot be represented in UTF-8, fall back to ISO Latin 1
663			*rtnString = CFStringCreateWithBytes(NULL, printName->Data,
664				(CFIndex)printName->Length, printEncoding, true);
665		}
666	}
667
668	// Clean up
669	if (snValue)
670		releaseFieldValue(snOid, snValue);
671	if (sanValues)
672		releaseFieldValues(sanOid, sanValues);
673}
674
675void
676Certificate::populateAttributes()
677{
678	StLock<Mutex>_(mMutex);
679	if (mPopulated)
680		return;
681
682	addParsedAttribute(Schema::attributeInfo(kSecSubjectItemAttr), CSSMOID_X509V1SubjectName);
683	addParsedAttribute(Schema::attributeInfo(kSecIssuerItemAttr), CSSMOID_X509V1IssuerName);
684	addParsedAttribute(Schema::attributeInfo(kSecSerialNumberItemAttr), CSSMOID_X509V1SerialNumber);
685
686	addSubjectKeyIdentifier();
687
688	if(!mHaveTypeAndEncoding)
689		MacOSError::throwMe(errSecDataNotAvailable); // @@@ Or some other error.
690
691	// Adjust mType based on the actual version of the cert.
692	CSSM_DATA_PTR versionPtr = copyFirstFieldValue(CSSMOID_X509V1Version);
693	if (versionPtr && versionPtr->Data && versionPtr->Length == sizeof(uint32))
694	{
695		mType = CSSM_CERT_X_509v1 + (*reinterpret_cast<uint32 *>(versionPtr->Data));
696	}
697	else
698		mType = CSSM_CERT_X_509v1;
699
700	releaseFieldValue(CSSMOID_X509V1Version, versionPtr);
701
702	mDbAttributes->add(Schema::attributeInfo(kSecCertTypeItemAttr), mType);
703	mDbAttributes->add(Schema::attributeInfo(kSecCertEncodingItemAttr), mEncoding);
704	mDbAttributes->add(Schema::attributeInfo(kSecPublicKeyHashItemAttr), publicKeyHash());
705	inferLabel(true);
706
707	mPopulated = true;
708}
709
710const CssmData &
711Certificate::data()
712{
713	StLock<Mutex>_(mMutex);
714	CssmDataContainer *data = mData.get();
715	if (!data && mKeychain)
716	{
717	    // Make sure mUniqueId is set.
718		dbUniqueRecord();
719		CssmDataContainer _data;
720		mData = NULL;
721		/* new data allocated by CSPDL, implicitly freed by CssmDataContainer */
722		mUniqueId->get(NULL, &_data);
723		/* this saves a copy to be freed at destruction and to be passed to caller */
724		setData((UInt32)_data.length(), _data.data());
725		return *mData.get();
726	}
727
728	// If the data hasn't been set we can't return it.
729	if (!data)
730		MacOSError::throwMe(errSecDataNotAvailable);
731
732	return *data;
733}
734
735CFHashCode Certificate::hash()
736{
737	(void)data();  // ensure that mData is set up
738	return ItemImpl::hash();
739}
740
741CSSM_CERT_TYPE
742Certificate::type()
743{
744	StLock<Mutex>_(mMutex);
745	if (!mHaveTypeAndEncoding)
746	{
747		SecKeychainAttribute attr;
748		attr.tag = kSecCertTypeItemAttr;
749		attr.data = &mType;
750		attr.length = sizeof(mType);
751		getAttribute(attr, NULL);
752	}
753
754	return mType;
755}
756
757CSSM_CERT_ENCODING
758Certificate::encoding()
759{
760	StLock<Mutex>_(mMutex);
761	if (!mHaveTypeAndEncoding)
762	{
763		SecKeychainAttribute attr;
764		attr.tag = kSecCertEncodingItemAttr;
765		attr.data = &mEncoding;
766		attr.length = sizeof(mEncoding);
767		getAttribute(attr, NULL);
768	}
769
770	return mEncoding;
771}
772
773const CSSM_X509_ALGORITHM_IDENTIFIER_PTR
774Certificate::algorithmID()
775{
776	StLock<Mutex>_(mMutex);
777	if (!mV1SubjectPublicKeyCStructValue)
778		mV1SubjectPublicKeyCStructValue = copyFirstFieldValue(CSSMOID_X509V1SubjectPublicKeyCStruct);
779
780	CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *info = (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *)mV1SubjectPublicKeyCStructValue->Data;
781	CSSM_X509_ALGORITHM_IDENTIFIER *algid = &info->algorithm;
782	return algid;
783}
784
785CFDataRef
786Certificate::sha1Hash()
787{
788	StLock<Mutex>_(mMutex);
789	if (!mSha1Hash) {
790		SecCertificateRef certRef = handle(false);
791		CFAllocatorRef allocRef = (certRef) ? CFGetAllocator(certRef) : NULL;
792		CSSM_DATA certData = data();
793		if (certData.Length == 0 || !certData.Data) {
794			MacOSError::throwMe(errSecDataNotAvailable);
795		}
796		const UInt8 *dataPtr = (const UInt8 *)certData.Data;
797		CFIndex dataLen = (CFIndex)certData.Length;
798		CFMutableDataRef digest = CFDataCreateMutable(allocRef, CC_SHA1_DIGEST_LENGTH);
799		CFDataSetLength(digest, CC_SHA1_DIGEST_LENGTH);
800		CCDigest(kCCDigestSHA1, dataPtr, dataLen, CFDataGetMutableBytePtr(digest));
801		mSha1Hash = digest;
802	}
803	return mSha1Hash; /* object is owned by our instance; caller should NOT release it */
804}
805
806CFStringRef
807Certificate::commonName()
808{
809	StLock<Mutex>_(mMutex);
810	return distinguishedName(&CSSMOID_X509V1SubjectNameCStruct, &CSSMOID_CommonName);
811}
812
813CFStringRef
814Certificate::distinguishedName(const CSSM_OID *sourceOid, const CSSM_OID *componentOid)
815{
816	StLock<Mutex>_(mMutex);
817	CFStringRef rtnString = NULL;
818	CSSM_DATA_PTR fieldValue = copyFirstFieldValue(*sourceOid);
819	CSSM_X509_NAME_PTR x509Name = (CSSM_X509_NAME_PTR)fieldValue->Data;
820	const CSSM_DATA	*printValue = NULL;
821	CFStringBuiltInEncodings encoding;
822
823	if (fieldValue && fieldValue->Data)
824		printValue = findPrintableField(*x509Name, componentOid, true, &encoding);
825
826	if (printValue)
827		rtnString = CFStringCreateWithBytes(NULL, printValue->Data,
828			CFIndex(printValue->Length), encoding, true);
829
830	releaseFieldValue(*sourceOid, fieldValue);
831
832	return rtnString;
833}
834
835
836/*
837 * Return a CFString containing the first email addresses for this certificate, based on the
838 * X509 SubjectAltName and SubjectName.
839 */
840CFStringRef
841Certificate::copyFirstEmailAddress()
842{
843	StLock<Mutex>_(mMutex);
844	CFStringRef rtnString;
845
846	const CSSM_OID &sanOid = CSSMOID_SubjectAltName;
847	CSSM_DATA_PTR *sanValues = copyFieldValues(sanOid);
848	const CSSM_OID &snOid = CSSMOID_X509V1SubjectNameCStruct;
849	CSSM_DATA_PTR snValue = copyFirstFieldValue(snOid);
850	std::vector<CssmData> emailAddresses;
851
852	getNames(sanValues, snValue, GNT_RFC822Name, emailAddresses);
853	if (emailAddresses.empty())
854		rtnString = NULL;
855	else
856	{
857		/* Encoding is kCFStringEncodingUTF8 since the string is either
858		   PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
859		rtnString = CFStringCreateWithBytes(NULL, emailAddresses[0].Data,
860			(CFIndex)emailAddresses[0].Length, kCFStringEncodingUTF8, true);
861	}
862
863	// Clean up
864	if (snValue)
865		releaseFieldValue(snOid, snValue);
866	if (sanValues)
867		releaseFieldValues(sanOid, sanValues);
868
869	return rtnString;
870}
871
872/*
873 * Return a CFArray containing the DNS hostnames for this certificate, based on the
874 * X509 SubjectAltName and SubjectName.
875 */
876CFArrayRef
877Certificate::copyDNSNames()
878{
879	StLock<Mutex>_(mMutex);
880	CFMutableArrayRef array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
881	std::vector<CssmData> dnsNames;
882
883	// Find the SubjectAltName fields, if any, and extract the GNT_DNSName entries from all of them
884	const CSSM_OID &sanOid = CSSMOID_SubjectAltName;
885	CSSM_DATA_PTR *sanValues = copyFieldValues(sanOid);
886
887	const CSSM_OID &snOid = CSSMOID_X509V1SubjectNameCStruct;
888	CSSM_DATA_PTR snValue = copyFirstFieldValue(snOid);
889
890	getNames(sanValues, snValue, GNT_DNSName, dnsNames);
891
892	for (std::vector<CssmData>::const_iterator it = dnsNames.begin(); it != dnsNames.end(); ++it)
893	{
894		/* Encoding is kCFStringEncodingUTF8 since the string is either
895		   PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
896		CFStringRef string = CFStringCreateWithBytes(NULL, it->Data, static_cast<CFIndex>(it->Length), kCFStringEncodingUTF8, true);
897		/* Be prepared for improperly formatted (non-UTF8) strings! */
898		if (!string) continue;
899		CFArrayAppendValue(array, string);
900		CFRelease(string);
901	}
902
903	// Clean up
904	if (snValue)
905		releaseFieldValue(snOid, snValue);
906	if (sanValues)
907		releaseFieldValues(sanOid, sanValues);
908
909	return array;
910}
911
912/*
913 * Return a CFArray containing the email addresses for this certificate, based on the
914 * X509 SubjectAltName and SubjectName.
915 */
916CFArrayRef
917Certificate::copyEmailAddresses()
918{
919	StLock<Mutex>_(mMutex);
920	CFMutableArrayRef array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
921	std::vector<CssmData> emailAddresses;
922
923	// Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
924	const CSSM_OID &sanOid = CSSMOID_SubjectAltName;
925	CSSM_DATA_PTR *sanValues = copyFieldValues(sanOid);
926
927	const CSSM_OID &snOid = CSSMOID_X509V1SubjectNameCStruct;
928	CSSM_DATA_PTR snValue = copyFirstFieldValue(snOid);
929
930	getNames(sanValues, snValue, GNT_RFC822Name, emailAddresses);
931
932	for (std::vector<CssmData>::const_iterator it = emailAddresses.begin(); it != emailAddresses.end(); ++it)
933	{
934		/* Encoding is kCFStringEncodingUTF8 since the string is either
935		   PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
936		CFStringRef string = CFStringCreateWithBytes(NULL, it->Data, static_cast<CFIndex>(it->Length), kCFStringEncodingUTF8, true);
937		/* Be prepared for improperly formatted (non-UTF8) strings! */
938		if (!string) continue;
939		CFArrayAppendValue(array, string);
940		CFRelease(string);
941	}
942
943	// Clean up
944	if (snValue)
945		releaseFieldValue(snOid, snValue);
946	if (sanValues)
947		releaseFieldValues(sanOid, sanValues);
948
949	return array;
950}
951
952const CSSM_X509_NAME_PTR
953Certificate::subjectName()
954{
955	StLock<Mutex>_(mMutex);
956	if (!mV1SubjectNameCStructValue)
957		if ((mV1SubjectNameCStructValue = copyFirstFieldValue(CSSMOID_X509V1SubjectNameCStruct)) == NULL)
958            return NULL;
959
960    return (const CSSM_X509_NAME_PTR)mV1SubjectNameCStructValue->Data;
961}
962
963const CSSM_X509_NAME_PTR
964Certificate::issuerName()
965{
966	StLock<Mutex>_(mMutex);
967	if (!mV1IssuerNameCStructValue)
968		if ((mV1IssuerNameCStructValue = copyFirstFieldValue(CSSMOID_X509V1IssuerNameCStruct)) == NULL)
969            return NULL;
970
971    return (const CSSM_X509_NAME_PTR)mV1IssuerNameCStructValue->Data;
972}
973
974CSSM_CL_HANDLE
975Certificate::clHandle()
976{
977	StLock<Mutex>_(mMutex);
978	if (!mCL)
979		mCL = clForType(type());
980
981	return mCL->handle();
982}
983
984bool
985Certificate::operator < (Certificate &other)
986{
987	// Certificates in different keychains are considered equal if data is equal
988	// Note that the Identity '<' operator relies on this assumption.
989	return data() < other.data();
990}
991
992bool
993Certificate::operator == (Certificate &other)
994{
995	// Certificates in different keychains are considered equal if data is equal
996	// Note that the Identity '==' operator relies on this assumption.
997	return data() == other.data();
998}
999
1000void
1001Certificate::update()
1002{
1003	ItemImpl::update();
1004}
1005
1006Item
1007Certificate::copyTo(const Keychain &keychain, Access *newAccess)
1008{
1009	StLock<Mutex>_(mMutex);
1010	/* Certs can't have access controls. */
1011	if (newAccess)
1012		MacOSError::throwMe(errSecNoAccessForItem);
1013
1014	Item item(new Certificate(data(), type(), encoding()));
1015	keychain->add(item);
1016	return item;
1017}
1018
1019void
1020Certificate::didModify()
1021{
1022}
1023
1024PrimaryKey
1025Certificate::add(Keychain &keychain)
1026{
1027	StLock<Mutex>_(mMutex);
1028	// If we already have a Keychain we can't be added.
1029	if (mKeychain)
1030		MacOSError::throwMe(errSecDuplicateItem);
1031
1032	populateAttributes();
1033
1034	CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType();
1035
1036	Db db(keychain->database());
1037	// add the item to the (regular) db
1038	try
1039	{
1040		mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
1041	}
1042	catch (const CssmError &e)
1043	{
1044		if (e.osStatus() != CSSMERR_DL_INVALID_RECORDTYPE)
1045			throw;
1046
1047		// Create the cert relation and try again.
1048		db->createRelation(CSSM_DL_DB_RECORD_X509_CERTIFICATE,
1049			"CSSM_DL_DB_RECORD_X509_CERTIFICATE",
1050			Schema::X509CertificateSchemaAttributeCount,
1051			Schema::X509CertificateSchemaAttributeList,
1052			Schema::X509CertificateSchemaIndexCount,
1053			Schema::X509CertificateSchemaIndexList);
1054		keychain->keychainSchema()->didCreateRelation(
1055			CSSM_DL_DB_RECORD_X509_CERTIFICATE,
1056			"CSSM_DL_DB_RECORD_X509_CERTIFICATE",
1057			Schema::X509CertificateSchemaAttributeCount,
1058			Schema::X509CertificateSchemaAttributeList,
1059			Schema::X509CertificateSchemaIndexCount,
1060			Schema::X509CertificateSchemaIndexList);
1061
1062		mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
1063	}
1064
1065	mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId);
1066    mKeychain = keychain;
1067
1068	return mPrimaryKey;
1069}
1070
1071SecPointer<KeyItem>
1072Certificate::publicKey()
1073{
1074	StLock<Mutex>_(mMutex);
1075	SecPointer<KeyItem> keyItem;
1076	// Return a CSSM_DATA_PTR with the value of the first field specified by field.
1077	// Caller must call releaseFieldValue to free the storage allocated by this call.
1078	// call OSStatus SecKeyGetCSSMKey(SecKeyRef key, const CSSM_KEY **cssmKey); to retrieve
1079
1080	CSSM_DATA_PTR keyPtr = copyFirstFieldValue(CSSMOID_CSSMKeyStruct);
1081	if (keyPtr && keyPtr->Data)
1082	{
1083		CssmClient::CSP csp(gGuidAppleCSP);
1084		CssmKey *cssmKey = reinterpret_cast<CssmKey *>(keyPtr->Data);
1085		CssmClient::Key key(csp, *cssmKey);
1086		keyItem = new KeyItem(key);
1087		// Clear out KeyData since KeyItem() takes over ownership of the key, and we don't want it getting released.
1088		cssmKey->KeyData.Data = NULL;
1089		cssmKey->KeyData.Length = 0;
1090	}
1091
1092	releaseFieldValue(CSSMOID_CSSMKeyStruct, keyPtr);
1093
1094	return keyItem;
1095}
1096
1097// This function "borrowed" from the X509 CL, which is (currently) linked into
1098// the Security.framework as a built-in plugin.
1099extern "C" bool getField_normRDN_NSS (
1100	const CSSM_DATA		&derName,
1101	uint32				&numFields,		// RETURNED (if successful, 0 or 1)
1102	CssmOwnedData		&fieldValue);	// RETURNED
1103
1104KCCursor
1105Certificate::cursorForIssuerAndSN(const StorageManager::KeychainList &keychains, const CssmData &issuer, const CssmData &serialNumber)
1106{
1107	CssmAutoData fieldValue(Allocator::standard(Allocator::normal));
1108	uint32 numFields;
1109
1110	// We need to decode issuer, normalize it, then re-encode it
1111	if (!getField_normRDN_NSS(issuer, numFields, fieldValue))
1112		MacOSError::throwMe(errSecDataNotAvailable);
1113
1114	// Code basically copied from SecKeychainSearchCreateFromAttributes and SecKeychainSearchCopyNext:
1115	KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
1116	cursor->conjunctive(CSSM_DB_AND);
1117	cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateIssuer, fieldValue.get());
1118	cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateSerialNumber, serialNumber);
1119
1120	return cursor;
1121}
1122
1123KCCursor
1124Certificate::cursorForIssuerAndSN_CF(const StorageManager::KeychainList &keychains, CFDataRef issuer, CFDataRef serialNumber)
1125{
1126	// This assumes a normalized issuer
1127	CSSM_DATA issuerCSSM, serialNumberCSSM;
1128
1129	issuerCSSM.Length = CFDataGetLength(issuer);
1130	issuerCSSM.Data = const_cast<uint8 *>(CFDataGetBytePtr(issuer));
1131
1132	serialNumberCSSM.Length = CFDataGetLength(serialNumber);
1133	serialNumberCSSM.Data = const_cast<uint8 *>(CFDataGetBytePtr(serialNumber));
1134
1135	// Code basically copied from SecKeychainSearchCreateFromAttributes and SecKeychainSearchCopyNext:
1136	KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
1137	cursor->conjunctive(CSSM_DB_AND);
1138	cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateIssuer, issuerCSSM);
1139	cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateSerialNumber, serialNumberCSSM);
1140
1141	return cursor;
1142}
1143
1144KCCursor
1145Certificate::cursorForSubjectKeyID(const StorageManager::KeychainList &keychains, const CssmData &subjectKeyID)
1146{
1147	KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
1148	cursor->conjunctive(CSSM_DB_AND);
1149	cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateSubjectKeyIdentifier, subjectKeyID);
1150
1151	return cursor;
1152}
1153
1154KCCursor
1155Certificate::cursorForEmail(const StorageManager::KeychainList &keychains, const char *emailAddress)
1156{
1157	KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
1158	if (emailAddress)
1159	{
1160		cursor->conjunctive(CSSM_DB_AND);
1161		CssmSelectionPredicate &pred = cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateAlias, emailAddress);
1162		/* Normalize the emailAddresses in place since cursor already copied it. */
1163		normalizeEmailAddress(pred.Attribute.Value[0]);
1164	}
1165
1166	return cursor;
1167}
1168
1169SecPointer<Certificate>
1170Certificate::findInKeychain(const StorageManager::KeychainList &keychains)
1171{
1172	StLock<Mutex>_(mMutex);
1173	const CSSM_OID &issuerOid = CSSMOID_X509V1IssuerName;
1174	CSSM_DATA_PTR issuerPtr = copyFirstFieldValue(issuerOid);
1175	CssmData issuer(issuerPtr->Data, issuerPtr->Length);
1176
1177	const CSSM_OID &serialOid = CSSMOID_X509V1SerialNumber;
1178	CSSM_DATA_PTR serialPtr = copyFirstFieldValue(serialOid);
1179	CssmData serial(serialPtr->Data, serialPtr->Length);
1180
1181	SecPointer<Certificate> foundCert = NULL;
1182	try {
1183		foundCert = findByIssuerAndSN(keychains, issuer, serial);
1184	} catch (...) {
1185		foundCert = NULL;
1186	}
1187
1188	releaseFieldValue(issuerOid, issuerPtr);
1189	releaseFieldValue(serialOid, serialPtr);
1190
1191	return foundCert;
1192}
1193
1194SecPointer<Certificate>
1195Certificate::findByIssuerAndSN(const StorageManager::KeychainList &keychains, const CssmData &issuer, const CssmData &serialNumber)
1196{
1197	Item item;
1198	if (!cursorForIssuerAndSN(keychains, issuer, serialNumber)->next(item))
1199		CssmError::throwMe(errSecItemNotFound);
1200
1201	return static_cast<Certificate *>(&*item);
1202}
1203
1204SecPointer<Certificate>
1205Certificate::findBySubjectKeyID(const StorageManager::KeychainList &keychains, const CssmData &subjectKeyID)
1206{
1207	Item item;
1208	if (!cursorForSubjectKeyID(keychains, subjectKeyID)->next(item))
1209		CssmError::throwMe(errSecItemNotFound);
1210
1211	return static_cast<Certificate *>(&*item);
1212}
1213
1214SecPointer<Certificate>
1215Certificate::findByEmail(const StorageManager::KeychainList &keychains, const char *emailAddress)
1216{
1217	Item item;
1218	if (!cursorForEmail(keychains, emailAddress)->next(item))
1219		CssmError::throwMe(errSecItemNotFound);
1220
1221	return static_cast<Certificate *>(&*item);
1222}
1223
1224/* Normalize emailAddresses in place. */
1225void
1226Certificate::normalizeEmailAddress(CSSM_DATA &emailAddress)
1227{
1228	/* Do a check to see if a '\0' was at the end of emailAddress and strip it. */
1229	if (emailAddress.Length && emailAddress.Data[emailAddress.Length - 1] == '\0')
1230		emailAddress.Length--;
1231	bool foundAt = false;
1232	for (uint32 ix = 0; ix < emailAddress.Length; ++ix)
1233	{
1234		uint8 ch = emailAddress.Data[ix];
1235		if (foundAt)
1236		{
1237			if ('A' <= ch && ch <= 'Z')
1238				emailAddress.Data[ix] = ch + 'a' - 'A';
1239		}
1240		else if (ch == '@')
1241			foundAt = true;
1242	}
1243}
1244
1245void
1246Certificate::getNames(CSSM_DATA_PTR *sanValues, CSSM_DATA_PTR snValue, CE_GeneralNameType generalNameType, std::vector<CssmData> &names)
1247{
1248	// Get the DNS host names or RFC822 email addresses for this certificate (depending on generalNameType),
1249	// within the X509 SubjectAltName and SubjectName.
1250
1251	// Find the SubjectAltName fields, if any, and extract the nameType entries from all of them
1252	if (sanValues)
1253	{
1254		for (CSSM_DATA_PTR *sanIx = sanValues; *sanIx; ++sanIx)
1255		{
1256			CSSM_DATA_PTR sanValue = *sanIx;
1257			if (sanValue && sanValue->Data)
1258			{
1259				CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)sanValue->Data;
1260				CE_GeneralNames *parsedValue = (CE_GeneralNames *)cssmExt->value.parsedValue;
1261
1262				/* Grab all the values that are of the specified name type. */
1263				for (uint32 i = 0; i < parsedValue->numNames; ++i)
1264				{
1265					if (parsedValue->generalName[i].nameType == generalNameType)
1266					{
1267						if (parsedValue->generalName[i].berEncoded) // can't handle this
1268							continue;
1269
1270						names.push_back(CssmData::overlay(parsedValue->generalName[i].name));
1271					}
1272				}
1273			}
1274		}
1275	}
1276
1277	if (names.empty() && snValue && snValue->Data)
1278	{
1279		const CSSM_X509_NAME &x509Name = *(const CSSM_X509_NAME *)snValue->Data;
1280		for (uint32 rdnDex = 0; rdnDex < x509Name.numberOfRDNs; rdnDex++)
1281		{
1282			const CSSM_X509_RDN *rdnPtr =
1283				&x509Name.RelativeDistinguishedName[rdnDex];
1284			for (uint32 tvpDex = 0; tvpDex < rdnPtr->numberOfPairs; tvpDex++)
1285			{
1286				const CSSM_X509_TYPE_VALUE_PAIR *tvpPtr =
1287					&rdnPtr->AttributeTypeAndValue[tvpDex];
1288
1289				/* type/value pair: match caller's specified type */
1290				if (GNT_RFC822Name == generalNameType) {
1291					if (((tvpPtr->type.Length != CSSMOID_EmailAddress.Length) ||
1292						memcmp(tvpPtr->type.Data, CSSMOID_EmailAddress.Data, CSSMOID_EmailAddress.Length))) {
1293							continue;
1294					}
1295				}
1296				if (GNT_DNSName == generalNameType) {
1297					if (((tvpPtr->type.Length != CSSMOID_CommonName.Length) ||
1298						memcmp(tvpPtr->type.Data, CSSMOID_CommonName.Data, CSSMOID_CommonName.Length))) {
1299							continue;
1300					}
1301				}
1302
1303				/* printable? */
1304				switch (tvpPtr->valueType)
1305				{
1306					case BER_TAG_PRINTABLE_STRING:
1307					case BER_TAG_IA5_STRING:
1308					case BER_TAG_T61_STRING:
1309					case BER_TAG_PKIX_UTF8_STRING:
1310						/* success */
1311						names.push_back(CssmData::overlay(tvpPtr->value));
1312						break;
1313					default:
1314						break;
1315				}
1316			}	/* for each pair */
1317		}		/* for each RDN */
1318	}
1319}
1320
1321void Certificate::willRead()
1322{
1323	populateAttributes();
1324}
1325
1326Boolean Certificate::isSelfSigned()
1327{
1328	StLock<Mutex>_(mMutex);
1329	CSSM_DATA_PTR issuer = NULL;
1330	CSSM_DATA_PTR subject = NULL;
1331	OSStatus ortn = errSecSuccess;
1332	Boolean brtn = false;
1333
1334	issuer  = copyFirstFieldValue(CSSMOID_X509V1IssuerNameStd);
1335	subject = copyFirstFieldValue(CSSMOID_X509V1SubjectNameStd);
1336	if((issuer == NULL) || (subject == NULL)) {
1337		ortn = errSecParam;
1338	}
1339	else if((issuer->Length == subject->Length) &&
1340		!memcmp(issuer->Data, subject->Data, issuer->Length)) {
1341		brtn = true;
1342	}
1343	if(brtn) {
1344		/* names match: verify signature */
1345		CSSM_RETURN crtn;
1346		CSSM_DATA certData = data();
1347		crtn = CSSM_CL_CertVerify(clHandle(), 0,
1348			&certData, &certData, NULL, 0);
1349		if(crtn) {
1350			brtn = false;
1351		}
1352	}
1353	if(issuer) {
1354		releaseFieldValue(CSSMOID_X509V1IssuerNameStd,	issuer);
1355	}
1356	if(subject) {
1357		releaseFieldValue(CSSMOID_X509V1SubjectNameStd, subject);
1358	}
1359	if(ortn) {
1360		MacOSError::throwMe(ortn);
1361	}
1362	return brtn;
1363}
1364