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