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