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