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