]> git.saurik.com Git - apple/security.git/blob - Keychain/Certificate.cpp
Security-164.1.tar.gz
[apple/security.git] / Keychain / Certificate.cpp
1 /*
2 * Copyright (c) 2002 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18 //
19 // Certificate.cpp
20 //
21 #include <Security/Certificate.h>
22 #include <Security/Schema.h>
23 #include <Security/oidscert.h>
24 #include <Security/oidsattr.h>
25 #include <Security/SecCertificate.h>
26 #include <Security/SecCertificatePriv.h>
27 #include <Security/cspclient.h>
28 #include <Security/KeyItem.h>
29 #include <Security/KCCursor.h>
30 #include <vector>
31 #include "CLFieldsCommon.h"
32
33
34 using namespace KeychainCore;
35
36 CL
37 Certificate::clForType(CSSM_CERT_TYPE type)
38 {
39 return CL(gGuidAppleX509CL);
40 }
41
42 Certificate::Certificate(const CSSM_DATA &data, CSSM_CERT_TYPE type, CSSM_CERT_ENCODING encoding) :
43 ItemImpl(CSSM_DL_DB_RECORD_X509_CERTIFICATE, reinterpret_cast<SecKeychainAttributeList *>(NULL), UInt32(data.Length), reinterpret_cast<const void *>(data.Data)),
44 mHaveTypeAndEncoding(true),
45 mType(type),
46 mEncoding(encoding),
47 mCL(clForType(type)),
48 mCertHandle(0),
49 mV1SubjectPublicKeyCStructValue(NULL)
50 {
51 }
52
53 // db item contstructor
54 Certificate::Certificate(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) :
55 ItemImpl(keychain, primaryKey, uniqueId),
56 mHaveTypeAndEncoding(false),
57 mCL(NULL),
58 mCertHandle(0),
59 mV1SubjectPublicKeyCStructValue(NULL)
60 {
61 }
62
63 // PrimaryKey item contstructor
64 Certificate::Certificate(const Keychain &keychain, const PrimaryKey &primaryKey) :
65 ItemImpl(keychain, primaryKey),
66 mHaveTypeAndEncoding(false),
67 mCL(NULL),
68 mCertHandle(0),
69 mV1SubjectPublicKeyCStructValue(NULL)
70 {
71 // @@@ In this case we don't know the type...
72 }
73
74 Certificate::Certificate(Certificate &certificate) :
75 ItemImpl(certificate),
76 mHaveTypeAndEncoding(certificate.mHaveTypeAndEncoding),
77 mType(certificate.mType),
78 mEncoding(certificate.mEncoding),
79 mCL(certificate.mCL),
80 mCertHandle(0),
81 mV1SubjectPublicKeyCStructValue(NULL)
82 {
83 }
84
85 Certificate::~Certificate() throw()
86 {
87 if (mV1SubjectPublicKeyCStructValue)
88 releaseFieldValue(CSSMOID_X509V1SubjectPublicKeyCStruct, mV1SubjectPublicKeyCStructValue);
89
90 if (mCertHandle && mCL)
91 CSSM_CL_CertAbortCache(mCL->handle(), mCertHandle);
92 }
93
94 CSSM_HANDLE
95 Certificate::certHandle()
96 {
97 const CSSM_DATA *cert = &data();
98 if (!mCertHandle)
99 {
100 if (CSSM_RETURN retval = CSSM_CL_CertCache(clHandle(), cert, &mCertHandle))
101 CssmError::throwMe(retval);
102 }
103
104 return mCertHandle;
105 }
106
107 /* 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. */
108 CSSM_DATA_PTR *
109 Certificate::copyFieldValues(const CSSM_OID &field)
110 {
111 CSSM_CL_HANDLE clh = clHandle();
112 CSSM_DATA_PTR fieldValue, *fieldValues;
113 CSSM_HANDLE resultsHandle = 0;
114 uint32 numberOfFields = 0;
115 CSSM_RETURN result;
116
117 result = CSSM_CL_CertGetFirstCachedFieldValue(clh, certHandle(), &field, &resultsHandle, &numberOfFields, &fieldValue);
118 if (result)
119 {
120 if (result == CSSMERR_CL_NO_FIELD_VALUES)
121 return NULL;
122
123 CssmError::throwMe(result);
124 }
125
126 fieldValues = new CSSM_DATA_PTR[numberOfFields + 1];
127 fieldValues[0] = fieldValue;
128 fieldValues[numberOfFields] = NULL;
129
130 for (uint32 value = 1; value < numberOfFields; ++value)
131 {
132 CSSM_RETURN cresult = CSSM_CL_CertGetNextCachedFieldValue(clh, resultsHandle, &fieldValues[value]);
133 if (cresult)
134 {
135 fieldValues[value] = NULL;
136 result = cresult;
137 break; // No point in continuing really.
138 }
139 }
140
141 if (result)
142 {
143 releaseFieldValues(field, fieldValues);
144 CssmError::throwMe(result);
145 }
146
147 return fieldValues;
148 }
149
150 void
151 Certificate::releaseFieldValues(const CSSM_OID &field, CSSM_DATA_PTR *fieldValues)
152 {
153 if (fieldValues)
154 {
155 CSSM_CL_HANDLE clh = clHandle();
156
157 for (int ix = 0; fieldValues[ix]; ++ix)
158 CSSM_CL_FreeFieldValue(clh, &field, fieldValues[ix]);
159
160 delete[] fieldValues;
161 }
162 }
163
164 void
165 Certificate::addParsedAttribute(const CSSM_DB_ATTRIBUTE_INFO &info, const CSSM_OID &field)
166 {
167 CSSM_DATA_PTR *fieldValues = copyFieldValues(field);
168 if (fieldValues)
169 {
170 CssmDbAttributeData &anAttr = mDbAttributes->add(info);
171 for (int ix = 0; fieldValues[ix]; ++ix)
172 anAttr.add(*fieldValues[ix], *mDbAttributes);
173
174 releaseFieldValues(field, fieldValues);
175 }
176 }
177
178 /* 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. */
179 CSSM_DATA_PTR
180 Certificate::copyFirstFieldValue(const CSSM_OID &field)
181 {
182 CSSM_CL_HANDLE clh = clHandle();
183 CSSM_DATA_PTR fieldValue;
184 CSSM_HANDLE resultsHandle = 0;
185 uint32 numberOfFields = 0;
186 CSSM_RETURN result;
187
188 result = CSSM_CL_CertGetFirstCachedFieldValue(clh, certHandle(), &field, &resultsHandle, &numberOfFields, &fieldValue);
189 if (result)
190 {
191 if (result == CSSMERR_CL_NO_FIELD_VALUES)
192 return NULL;
193
194 CssmError::throwMe(result);
195 }
196
197 result = CSSM_CL_CertAbortQuery(clh, resultsHandle);
198
199 if (result)
200 {
201 releaseFieldValue(field, fieldValue);
202 CssmError::throwMe(result);
203 }
204
205 return fieldValue;
206 }
207
208 void
209 Certificate::releaseFieldValue(const CSSM_OID &field, CSSM_DATA_PTR fieldValue)
210 {
211 if (fieldValue)
212 {
213 CSSM_CL_HANDLE clh = clHandle();
214 CSSM_CL_FreeFieldValue(clh, &field, fieldValue);
215 }
216 }
217
218
219
220 /*
221 This method computes the keyIdentifier for the public key in the cert as
222 described below:
223
224 The keyIdentifier is composed of the 160-bit SHA-1 hash of the
225 value of the BIT STRING subjectPublicKey (excluding the tag,
226 length, and number of unused bits).
227 */
228 const CssmData &
229 Certificate::publicKeyHash()
230 {
231 if (mPublicKeyHash.Length)
232 return mPublicKeyHash;
233
234 CSSM_DATA_PTR keyPtr = copyFirstFieldValue(CSSMOID_CSSMKeyStruct);
235 if (keyPtr && keyPtr->Data)
236 {
237 CssmClient::CSP csp(gGuidAppleCSP);
238 CssmClient::PassThrough passThrough(csp);
239 CSSM_KEY *key = reinterpret_cast<CSSM_KEY *>(keyPtr->Data);
240 void *outData;
241 CssmData *cssmData;
242
243 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
244 * associated key blob.
245 * Key is specified in CSSM_CSP_CreatePassThroughContext.
246 * Hash is allocated by the CSP, in the App's memory, and returned
247 * in *outData. */
248 passThrough.key(key);
249 passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
250 cssmData = reinterpret_cast<CssmData *>(outData);
251
252 assert(cssmData->Length <= sizeof(mPublicKeyHashBytes));
253 mPublicKeyHash.Data = mPublicKeyHashBytes;
254 mPublicKeyHash.Length = cssmData->Length;
255 memcpy(mPublicKeyHash.Data, cssmData->Data, cssmData->Length);
256 csp.allocator().free(cssmData->Data);
257 csp.allocator().free(cssmData);
258 }
259
260 releaseFieldValue(CSSMOID_CSSMKeyStruct, keyPtr);
261
262 return mPublicKeyHash;
263 }
264
265 /*
266 * Given an CSSM_X509_NAME, Find the first name/value pair with
267 * a printable value which matches the specified OID (e.g., CSSMOID_CommonName).
268 * Returns NULL if none found.
269 */
270 static const CSSM_DATA *
271 findPrintableField(
272 const CSSM_X509_NAME &x509Name,
273 const CSSM_OID *tvpType) // NULL means "any printable field"
274 {
275 for(uint32 rdnDex=0; rdnDex<x509Name.numberOfRDNs; rdnDex++) {
276 const CSSM_X509_RDN *rdnPtr =
277 &x509Name.RelativeDistinguishedName[rdnDex];
278 for(uint32 tvpDex=0; tvpDex<rdnPtr->numberOfPairs; tvpDex++) {
279 const CSSM_X509_TYPE_VALUE_PAIR *tvpPtr =
280 &rdnPtr->AttributeTypeAndValue[tvpDex];
281
282 /* type/value pair: match caller's specified type? */
283 if((tvpType != NULL) &&
284 ((tvpPtr->type.Length != tvpType->Length) ||
285 memcmp(tvpPtr->type.Data, tvpType->Data, tvpType->Length))) {
286 continue;
287 }
288
289 /* printable? */
290 switch(tvpPtr->valueType) {
291 case BER_TAG_PRINTABLE_STRING:
292 case BER_TAG_IA5_STRING:
293 case BER_TAG_T61_STRING:
294 case BER_TAG_PKIX_UTF8_STRING:
295 /* success */
296 return &tvpPtr->value;
297 default:
298 break;
299 }
300 } /* for each pair */
301 } /* for each RDN */
302
303 /* no printable component of specified type found */
304 return NULL;
305 }
306
307 /*
308 * Infer printable label for a given an CSSM_X509_NAME. Returns NULL
309 * if no appropriate printable name found.
310 */
311 const CSSM_DATA *SecInferLabelFromX509Name(
312 const CSSM_X509_NAME *x509Name)
313 {
314 const CSSM_DATA *printValue;
315 /*
316 * Search order (take the first one found with a printable
317 * value):
318 * -- common name
319 * -- Orgnaizational Unit
320 * -- Organization
321 * -- field of any kind
322 */
323 printValue = findPrintableField(*x509Name, &CSSMOID_CommonName);
324 if(printValue != NULL) {
325 return printValue;
326 }
327 printValue = findPrintableField(*x509Name, &CSSMOID_OrganizationalUnitName);
328 if(printValue != NULL) {
329 return printValue;
330 }
331 printValue = findPrintableField(*x509Name, &CSSMOID_OrganizationName);
332 if(printValue != NULL) {
333 return printValue;
334 }
335 /* take anything */
336 return findPrintableField(*x509Name, NULL);
337 }
338
339 void
340 Certificate::inferLabel(bool addLabel, CFStringRef *rtnString)
341 {
342 // Set PrintName and optionally the Alias attribute for this certificate, based on the
343 // X509 SubjectAltName and SubjectName.
344 const CSSM_DATA *printName = NULL;
345 std::vector<CssmData> emailAddresses;
346 CSSM_DATA puntData;
347
348 // Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
349 const CSSM_OID &sanOid = CSSMOID_SubjectAltName;
350 CSSM_DATA_PTR *sanValues = copyFieldValues(sanOid);
351 const CSSM_OID &snOid = CSSMOID_X509V1SubjectNameCStruct;
352 CSSM_DATA_PTR snValue = copyFirstFieldValue(snOid);
353
354 getEmailAddresses(sanValues, snValue, emailAddresses);
355
356 if (snValue && snValue->Data)
357 {
358 const CSSM_X509_NAME &x509Name = *(const CSSM_X509_NAME *)snValue->Data;
359 printName = SecInferLabelFromX509Name(&x509Name);
360 }
361
362 if (printName == NULL)
363 {
364 /* If the we couldn't find a label use the emailAddress instead. */
365 if (!emailAddresses.empty())
366 printName = &emailAddresses[0];
367 else
368 {
369 /* punt! */
370 puntData.Data = (uint8 *)"X509 Certificate";
371 puntData.Length = 16;
372 printName = &puntData;
373 }
374 }
375
376 /* If we couldn't find an email address just use the printName which might be the url or something else useful. */
377 if (emailAddresses.empty())
378 emailAddresses.push_back(CssmData::overlay(*printName));
379
380 /* Do a check to see if a '\0' was at the end of printName and strip it. */
381 CssmData cleanedUpPrintName(printName->Data, printName->Length);
382 if (cleanedUpPrintName.Length && cleanedUpPrintName.Data[cleanedUpPrintName.Length - 1] == '\0')
383 cleanedUpPrintName.Length--;
384
385 /* What do we do with the inferred label - return it or add it mDbAttributes? */
386 if (addLabel)
387 {
388 mDbAttributes->add(Schema::kX509CertificatePrintName, cleanedUpPrintName);
389 CssmDbAttributeData &attrData = mDbAttributes->add(Schema::kX509CertificateAlias);
390
391 /* Add the email addresses to attrData and normalize them. */
392 uint32 ix = 0;
393 for (std::vector<CssmData>::const_iterator it = emailAddresses.begin(); it != emailAddresses.end(); ++it, ++ix)
394 {
395 /* Add the email address using the allocator from mDbAttributes. */
396 attrData.add(*it, *mDbAttributes);
397 /* Normalize the emailAddresses in place since attrData already copied it. */
398 normalizeEmailAddress(attrData.Value[ix]);
399 }
400 }
401
402 if (rtnString)
403 {
404 /* Encoding is kCFStringEncodingUTF8 since the string is either
405 PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
406 *rtnString = CFStringCreateWithBytes(NULL, cleanedUpPrintName.Data,
407 (CFIndex)cleanedUpPrintName.Length, kCFStringEncodingUTF8, true);
408 }
409
410 // Clean up
411 if (snValue)
412 releaseFieldValue(snOid, snValue);
413 if (sanValues)
414 releaseFieldValues(sanOid, sanValues);
415 }
416
417 void
418 Certificate::populateAttributes()
419 {
420 addParsedAttribute(Schema::attributeInfo(kSecSubjectItemAttr), CSSMOID_X509V1SubjectName);
421 addParsedAttribute(Schema::attributeInfo(kSecIssuerItemAttr), CSSMOID_X509V1IssuerName);
422 addParsedAttribute(Schema::attributeInfo(kSecSerialNumberItemAttr), CSSMOID_X509V1SerialNumber);
423
424 addParsedAttribute(Schema::attributeInfo(kSecSubjectKeyIdentifierItemAttr), CSSMOID_SubjectKeyIdentifier);
425
426 if(!mHaveTypeAndEncoding)
427 MacOSError::throwMe(errSecDataNotAvailable); // @@@ Or some other error.
428
429 // Adjust mType based on the actual version of the cert.
430 CSSM_DATA_PTR versionPtr = copyFirstFieldValue(CSSMOID_X509V1Version);
431 if (versionPtr && versionPtr->Data && versionPtr->Length == sizeof(uint32))
432 {
433 mType = CSSM_CERT_X_509v1 + (*reinterpret_cast<uint32 *>(versionPtr->Data));
434 }
435 else
436 mType = CSSM_CERT_X_509v1;
437
438 releaseFieldValue(CSSMOID_X509V1Version, versionPtr);
439
440 mDbAttributes->add(Schema::attributeInfo(kSecCertTypeItemAttr), mType);
441 mDbAttributes->add(Schema::attributeInfo(kSecCertEncodingItemAttr), mEncoding);
442 mDbAttributes->add(Schema::attributeInfo(kSecPublicKeyHashItemAttr), publicKeyHash());
443 inferLabel(true);
444 }
445
446 const CssmData &
447 Certificate::data()
448 {
449 CssmDataContainer *data = mData.get();
450 if (!data && mKeychain)
451 {
452 // Make sure mUniqueId is set.
453 dbUniqueRecord();
454 data = new CssmDataContainer();
455 mData.reset(data);
456 mUniqueId->get(NULL, data);
457 }
458
459 // If the data hasn't been set we can't return it.
460 if (!data)
461 MacOSError::throwMe(errSecDataNotAvailable);
462
463 return *data;
464 }
465
466 CSSM_CERT_TYPE
467 Certificate::type()
468 {
469 if (!mHaveTypeAndEncoding)
470 {
471 SecKeychainAttribute attr;
472 attr.tag = kSecCertTypeItemAttr;
473 attr.data = &mType;
474 attr.length = sizeof(mType);
475 getAttribute(attr, NULL);
476 }
477
478 return mType;
479 }
480
481 CSSM_CERT_ENCODING
482 Certificate::encoding()
483 {
484 if (!mHaveTypeAndEncoding)
485 {
486 SecKeychainAttribute attr;
487 attr.tag = kSecCertEncodingItemAttr;
488 attr.data = &mEncoding;
489 attr.length = sizeof(mEncoding);
490 getAttribute(attr, NULL);
491 }
492
493 return mEncoding;
494 }
495
496 const CSSM_X509_ALGORITHM_IDENTIFIER *
497 Certificate::algorithmID()
498 {
499 if (!mV1SubjectPublicKeyCStructValue)
500 mV1SubjectPublicKeyCStructValue = copyFirstFieldValue(CSSMOID_X509V1SubjectPublicKeyCStruct);
501
502 CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *info = (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *)mV1SubjectPublicKeyCStructValue->Data;
503 CSSM_X509_ALGORITHM_IDENTIFIER *algid = &info->algorithm;
504 return algid;
505 }
506
507 CFStringRef
508 Certificate::commonName()
509 {
510 CFStringRef rtnString;
511 const CSSM_OID &fieldOid = CSSMOID_X509V1SubjectNameCStruct;
512 CSSM_DATA_PTR fieldValue = copyFirstFieldValue(fieldOid);
513 CSSM_X509_NAME_PTR x509Name = (CSSM_X509_NAME_PTR)fieldValue->Data;
514 const CSSM_DATA *printValue = NULL;
515 if (fieldValue && fieldValue->Data)
516 printValue = findPrintableField(*x509Name, &CSSMOID_CommonName);
517
518 if (printValue == NULL)
519 rtnString = NULL;
520 else
521 {
522 /* Encoding is kCFStringEncodingUTF8 since the string is either
523 PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
524 rtnString = CFStringCreateWithBytes(NULL, printValue->Data,
525 (CFIndex)printValue->Length, kCFStringEncodingUTF8, true);
526 }
527
528 releaseFieldValue(CSSMOID_X509V1SubjectNameCStruct, fieldValue);
529
530 return rtnString;
531 }
532
533 /*
534 * Return a CFString containing the first email addresses for this certificate, based on the
535 * X509 SubjectAltName and SubjectName.
536 */
537 CFStringRef
538 Certificate::copyFirstEmailAddress()
539 {
540 CFStringRef rtnString;
541
542 const CSSM_OID &sanOid = CSSMOID_SubjectAltName;
543 CSSM_DATA_PTR *sanValues = copyFieldValues(sanOid);
544 const CSSM_OID &snOid = CSSMOID_X509V1SubjectNameCStruct;
545 CSSM_DATA_PTR snValue = copyFirstFieldValue(snOid);
546 std::vector<CssmData> emailAddresses;
547
548 getEmailAddresses(sanValues, snValue, emailAddresses);
549 if (emailAddresses.empty())
550 rtnString = NULL;
551 else
552 {
553 /* Encoding is kCFStringEncodingUTF8 since the string is either
554 PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
555 rtnString = CFStringCreateWithBytes(NULL, emailAddresses[0].Data,
556 (CFIndex)emailAddresses[0].Length, kCFStringEncodingUTF8, true);
557 }
558
559 // Clean up
560 if (snValue)
561 releaseFieldValue(snOid, snValue);
562 if (sanValues)
563 releaseFieldValues(sanOid, sanValues);
564
565 return rtnString;
566 }
567
568 /*
569 * Return a CFArray containing the email addresses for this certificate, based on the
570 * X509 SubjectAltName and SubjectName.
571 */
572 CFArrayRef
573 Certificate::copyEmailAddresses()
574 {
575 CFMutableArrayRef array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
576 std::vector<CssmData> emailAddresses;
577
578 // Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
579 const CSSM_OID &sanOid = CSSMOID_SubjectAltName;
580 CSSM_DATA_PTR *sanValues = copyFieldValues(sanOid);
581
582 const CSSM_OID &snOid = CSSMOID_X509V1SubjectNameCStruct;
583 CSSM_DATA_PTR snValue = copyFirstFieldValue(snOid);
584
585 getEmailAddresses(sanValues, snValue, emailAddresses);
586
587 for (std::vector<CssmData>::const_iterator it = emailAddresses.begin(); it != emailAddresses.end(); ++it)
588 {
589 /* Encoding is kCFStringEncodingUTF8 since the string is either
590 PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
591 CFStringRef string = CFStringCreateWithBytes(NULL, it->Data, static_cast<CFIndex>(it->Length), kCFStringEncodingUTF8, true);
592 CFArrayAppendValue(array, string);
593 CFRelease(string);
594 }
595
596 // Clean up
597 if (snValue)
598 releaseFieldValue(snOid, snValue);
599 if (sanValues)
600 releaseFieldValues(sanOid, sanValues);
601
602 return array;
603 }
604
605 void
606 Certificate::getSubject(CSSM_X509_NAME &outSubject)
607 {
608 }
609
610 void
611 Certificate::getIssuer(CSSM_X509_NAME &outName)
612 {
613 }
614
615 CSSM_CL_HANDLE
616 Certificate::clHandle()
617 {
618 if (!mCL)
619 mCL = clForType(type());
620
621 return mCL->handle();
622 }
623
624 bool
625 Certificate::operator < (Certificate &other)
626 {
627 return data() < other.data();
628 }
629
630 bool
631 Certificate::operator == (Certificate &other)
632 {
633 return data() == other.data();
634 }
635
636 void
637 Certificate::update()
638 {
639 ItemImpl::update();
640 }
641
642 Item
643 Certificate::copyTo(const Keychain &keychain, Access *newAccess)
644 {
645 /* Certs can't have access controls. */
646 if (newAccess)
647 MacOSError::throwMe(errSecNoAccessForItem);
648
649 Item item(new Certificate(data(), type(), encoding()));
650 keychain->add(item);
651 return item;
652 }
653
654 void
655 Certificate::didModify()
656 {
657 }
658
659 PrimaryKey
660 Certificate::add(Keychain &keychain)
661 {
662 // If we already have a Keychain we can't be added.
663 if (mKeychain)
664 MacOSError::throwMe(errSecDuplicateItem);
665
666 populateAttributes();
667
668 CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType();
669
670 Db db(keychain->database());
671 // add the item to the (regular) db
672 try
673 {
674 mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
675 }
676 catch (const CssmError &e)
677 {
678 if (e.cssmError() != CSSMERR_DL_INVALID_RECORDTYPE)
679 throw;
680
681 // Create the cert relation and try again.
682 db->createRelation(CSSM_DL_DB_RECORD_X509_CERTIFICATE, "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
683 Schema::X509CertificateSchemaAttributeCount,
684 Schema::X509CertificateSchemaAttributeList,
685 Schema::X509CertificateSchemaIndexCount,
686 Schema::X509CertificateSchemaIndexList);
687 keychain->resetSchema();
688
689 mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
690 }
691
692 mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId);
693 mKeychain = keychain;
694
695 return mPrimaryKey;
696 }
697
698 SecPointer<KeyItem>
699 Certificate::publicKey()
700 {
701 SecPointer<KeyItem> keyItem;
702 // Return a CSSM_DATA_PTR with the value of the first field specified by field.
703 // Caller must call releaseFieldValue to free the storage allocated by this call.
704 // call OSStatus SecKeyGetCSSMKey(SecKeyRef key, const CSSM_KEY **cssmKey); to retrieve
705
706 CSSM_DATA_PTR keyPtr = copyFirstFieldValue(CSSMOID_CSSMKeyStruct);
707 if (keyPtr && keyPtr->Data)
708 {
709 CssmClient::CSP csp(gGuidAppleCSP);
710 CssmKey *cssmKey = reinterpret_cast<CssmKey *>(keyPtr->Data);
711 CssmClient::Key key(csp, *cssmKey);
712 keyItem = new KeyItem(key);
713 // Clear out KeyData since KeyItem() takes over ownership of the key, and we don't want it getting released.
714 cssmKey->KeyData.Data = NULL;
715 cssmKey->KeyData.Length = 0;
716 }
717
718 releaseFieldValue(CSSMOID_CSSMKeyStruct, keyPtr);
719
720 return keyItem;
721 }
722
723 KCCursor
724 Certificate::cursorForIssuerAndSN(const StorageManager::KeychainList &keychains, const CssmData &issuer, const CssmData &serialNumber)
725 {
726 CssmAutoData fieldValue(CssmAllocator::standard(CssmAllocator::normal));
727 uint32 numFields;
728
729 // We need to decode issuer, normalize it, then re-encode it
730 if (!getField_normRDN_NSS(issuer, numFields, fieldValue))
731 MacOSError::throwMe(errSecDataNotAvailable);
732
733 // Code basically copied from SecKeychainSearchCreateFromAttributes and SecKeychainSearchCopyNext:
734 KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
735 cursor->conjunctive(CSSM_DB_AND);
736 cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateIssuer, fieldValue.get());
737 cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateSerialNumber, serialNumber);
738
739 return cursor;
740 }
741
742 KCCursor
743 Certificate::cursorForSubjectKeyID(const StorageManager::KeychainList &keychains, const CssmData &subjectKeyID)
744 {
745 KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
746 cursor->conjunctive(CSSM_DB_AND);
747 cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateSubjectKeyIdentifier, subjectKeyID);
748
749 return cursor;
750 }
751
752 KCCursor
753 Certificate::cursorForEmail(const StorageManager::KeychainList &keychains, const char *emailAddress)
754 {
755 KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
756 if (emailAddress)
757 {
758 cursor->conjunctive(CSSM_DB_AND);
759 CssmSelectionPredicate &pred = cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateAlias, emailAddress);
760 /* Normalize the emailAddresses in place since cursor already copied it. */
761 normalizeEmailAddress(pred.Attribute.Value[0]);
762 }
763
764 return cursor;
765 }
766
767 SecPointer<Certificate>
768 Certificate::findByIssuerAndSN(const StorageManager::KeychainList &keychains, const CssmData &issuer, const CssmData &serialNumber)
769 {
770 Item item;
771 if (!cursorForIssuerAndSN(keychains, issuer, serialNumber)->next(item))
772 CssmError::throwMe(errSecItemNotFound);
773
774 return static_cast<Certificate *>(&*item);
775 }
776
777 SecPointer<Certificate>
778 Certificate::findBySubjectKeyID(const StorageManager::KeychainList &keychains, const CssmData &subjectKeyID)
779 {
780 Item item;
781 if (!cursorForSubjectKeyID(keychains, subjectKeyID)->next(item))
782 CssmError::throwMe(errSecItemNotFound);
783
784 return static_cast<Certificate *>(&*item);
785 }
786
787 SecPointer<Certificate>
788 Certificate::findByEmail(const StorageManager::KeychainList &keychains, const char *emailAddress)
789 {
790 Item item;
791 if (!cursorForEmail(keychains, emailAddress)->next(item))
792 CssmError::throwMe(errSecItemNotFound);
793
794 return static_cast<Certificate *>(&*item);
795 }
796
797 /* Normalize emailAddresses in place. */
798 void
799 Certificate::normalizeEmailAddress(CSSM_DATA &emailAddress)
800 {
801 /* Do a check to see if a '\0' was at the end of emailAddress and strip it. */
802 if (emailAddress.Length && emailAddress.Data[emailAddress.Length - 1] == '\0')
803 emailAddress.Length--;
804 bool foundAt = false;
805 for (uint32 ix = 0; ix < emailAddress.Length; ++ix)
806 {
807 uint8 ch = emailAddress.Data[ix];
808 if (foundAt)
809 {
810 if ('A' <= ch && ch <= 'Z')
811 emailAddress.Data[ix] = ch + 'a' - 'A';
812 }
813 else if (ch == '@')
814 foundAt = true;
815 }
816 }
817
818 void
819 Certificate::getEmailAddresses(CSSM_DATA_PTR *sanValues, CSSM_DATA_PTR snValue, std::vector<CssmData> &emailAddresses)
820 {
821 // Get the email addresses for this certificate, based on the
822 // X509 SubjectAltName and SubjectName.
823
824 // Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
825 if (sanValues)
826 {
827 for (CSSM_DATA_PTR *sanIx = sanValues; *sanIx; ++sanIx)
828 {
829 CSSM_DATA_PTR sanValue = *sanIx;
830 if (sanValue && sanValue->Data)
831 {
832 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)sanValue->Data;
833 CE_GeneralNames *parsedValue = (CE_GeneralNames *)cssmExt->value.parsedValue;
834
835 /* Grab all the values that are of type GNT_RFC822Name. */
836 for (uint32 i = 0; i < parsedValue->numNames; ++i)
837 {
838 if (parsedValue->generalName[i].nameType == GNT_RFC822Name)
839 {
840 if (parsedValue->generalName[i].berEncoded) // can't handle this
841 continue;
842
843 emailAddresses.push_back(CssmData::overlay(parsedValue->generalName[i].name));
844 }
845 }
846 }
847 }
848 }
849
850 if (emailAddresses.empty() && snValue && snValue->Data)
851 {
852 const CSSM_X509_NAME &x509Name = *(const CSSM_X509_NAME *)snValue->Data;
853 for (uint32 rdnDex = 0; rdnDex < x509Name.numberOfRDNs; rdnDex++)
854 {
855 const CSSM_X509_RDN *rdnPtr =
856 &x509Name.RelativeDistinguishedName[rdnDex];
857 for (uint32 tvpDex = 0; tvpDex < rdnPtr->numberOfPairs; tvpDex++)
858 {
859 const CSSM_X509_TYPE_VALUE_PAIR *tvpPtr =
860 &rdnPtr->AttributeTypeAndValue[tvpDex];
861
862 /* type/value pair: match caller's specified type? */
863 if (((tvpPtr->type.Length != CSSMOID_EmailAddress.Length) ||
864 memcmp(tvpPtr->type.Data, CSSMOID_EmailAddress.Data, CSSMOID_EmailAddress.Length))) {
865 continue;
866 }
867
868 /* printable? */
869 switch (tvpPtr->valueType)
870 {
871 case BER_TAG_PRINTABLE_STRING:
872 case BER_TAG_IA5_STRING:
873 case BER_TAG_T61_STRING:
874 case BER_TAG_PKIX_UTF8_STRING:
875 /* success */
876 emailAddresses.push_back(CssmData::overlay(tvpPtr->value));
877 break;
878 default:
879 break;
880 }
881 } /* for each pair */
882 } /* for each RDN */
883 }
884 }