Item ItemImpl::makeFromPersistentReference(const CFDataRef persistentRef, bool *isIdentityRef) { CssmData dictData((void*)::CFDataGetBytePtr(persistentRef), ::CFDataGetLength(persistentRef)); NameValueDictionary dict(dictData); Keychain keychain; Item item = (ItemImpl *) NULL; if (isIdentityRef) { *isIdentityRef = (dict.FindByName(IDENTITY_KEY) != 0) ? true : false; } // make sure we have a database identifier if (dict.FindByName(SSUID_KEY) != 0) { DLDbIdentifier dlDbIdentifier = NameValueDictionary::MakeDLDbIdentifierFromNameValueDictionary(dict); DLDbIdentifier newDlDbIdentifier(dlDbIdentifier.ssuid(), DLDbListCFPref::ExpandTildesInPath(dlDbIdentifier.dbName()).c_str(), dlDbIdentifier.dbLocation()); keychain = globals().storageManager.keychain(newDlDbIdentifier); const NameValuePair* aDictItem = dict.FindByName(ITEM_KEY); if (aDictItem && keychain) { PrimaryKey primaryKey(aDictItem->Value()); item = keychain->item(primaryKey); } } KCThrowIf_( !item, errSecItemNotFound ); return item; }
OSStatus SecKeychainItemCreateFromContent(SecItemClass itemClass, SecKeychainAttributeList *attrList, UInt32 length, const void *data, SecKeychainRef keychainRef, SecAccessRef initialAccess, SecKeychainItemRef *itemRef) { BEGIN_SECAPI KCThrowParamErrIf_(length!=0 && data==NULL); Item item(itemClass, attrList, length, data); if (initialAccess) { item->setAccess(Access::required(initialAccess)); } Keychain keychain = nil; try { keychain = Keychain::optional(keychainRef); if ( !keychain->exists() ) { MacOSError::throwMe(errSecNoSuchKeychain); // Might be deleted or not available at this time. } } catch(...) { keychain = globals().storageManager.defaultKeychainUI(item); } keychain->add(item); if (itemRef) { *itemRef = item->handle(); } END_SECAPI }
OSStatus SecKeychainItemDelete(SecKeychainItemRef itemRef) { BEGIN_SECKCITEMAPI Item item = ItemImpl::required(__itemImplRef); Keychain keychain = item->keychain(); // item must be persistent. KCThrowIf_( !keychain, errSecInvalidItemRef ); /* * Before deleting the item, delete any existing Extended Attributes. */ OSStatus ortn; CFArrayRef attrNames = NULL; ortn = SecKeychainItemCopyAllExtendedAttributes(__itemImplRef, &attrNames, NULL); if(ortn == errSecSuccess) { CFIndex numAttrs = CFArrayGetCount(attrNames); for(CFIndex dex=0; dex<numAttrs; dex++) { CFStringRef attrName = (CFStringRef)CFArrayGetValueAtIndex(attrNames, dex); /* setting value to NULL ==> delete */ SecKeychainItemSetExtendedAttribute(__itemImplRef, attrName, NULL); } } /* now delete the item */ keychain->deleteItem( item ); END_SECKCITEMAPI }
// // Add item to keychain // PrimaryKey UnlockReferralItem::add(Keychain &keychain) { StLock<Mutex>_(mMutex); // If we already have a Keychain we can't be added. if (mKeychain) MacOSError::throwMe(errSecDuplicateItem); populateAttributes(); CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType(); Db db(keychain->database()); // add the item to the (regular) db try { mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get()); secdebug("usertrust", "%p inserted", this); } catch (const CssmError &e) { if (e.osStatus() != CSSMERR_DL_INVALID_RECORDTYPE) throw; // Create the referral relation and try again. secdebug("usertrust", "adding schema relation for user trusts"); #if 0 db->createRelation(CSSM_DL_DB_RECORD_UNLOCK_REFERRAL, "CSSM_DL_DB_RECORD_UNLOCK_REFERRAL", Schema::UnlockReferralSchemaAttributeCount, Schema::UnlockReferralSchemaAttributeList, Schema::UnlockReferralSchemaIndexCount, Schema::UnlockReferralSchemaIndexList); keychain->keychainSchema()->didCreateRelation( CSSM_DL_DB_RECORD_UNLOCK_REFERRAL, "CSSM_DL_DB_RECORD_UNLOCK_REFERRAL", Schema::UnlockReferralSchemaAttributeCount, Schema::UnlockReferralSchemaAttributeList, Schema::UnlockReferralSchemaIndexCount, Schema::UnlockReferralSchemaIndexList); #endif //keychain->resetSchema(); mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get()); secdebug("usertrust", "%p inserted now", this); } mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId); mKeychain = keychain; return mPrimaryKey; }
OSStatus SecKeychainItemCopyKeychain(SecKeychainItemRef itemRef, SecKeychainRef* keychainRef) { BEGIN_SECKCITEMAPI // make sure this item has a keychain Keychain kc = ItemImpl::required(__itemImplRef)->keychain(); if (kc == NULL) { MacOSError::throwMe(errSecNoSuchKeychain); } Required(keychainRef) = kc->handle(); END_SECKCITEMAPI }
PrimaryKey Certificate::add(Keychain &keychain) { StLock<Mutex>_(mMutex); // If we already have a Keychain we can't be added. if (mKeychain) MacOSError::throwMe(errSecDuplicateItem); populateAttributes(); CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType(); Db db(keychain->database()); // add the item to the (regular) db try { mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get()); } catch (const CssmError &e) { if (e.osStatus() != CSSMERR_DL_INVALID_RECORDTYPE) throw; // Create the cert relation and try again. db->createRelation(CSSM_DL_DB_RECORD_X509_CERTIFICATE, "CSSM_DL_DB_RECORD_X509_CERTIFICATE", Schema::X509CertificateSchemaAttributeCount, Schema::X509CertificateSchemaAttributeList, Schema::X509CertificateSchemaIndexCount, Schema::X509CertificateSchemaIndexList); keychain->keychainSchema()->didCreateRelation( CSSM_DL_DB_RECORD_X509_CERTIFICATE, "CSSM_DL_DB_RECORD_X509_CERTIFICATE", Schema::X509CertificateSchemaAttributeCount, Schema::X509CertificateSchemaAttributeList, Schema::X509CertificateSchemaIndexCount, Schema::X509CertificateSchemaIndexList); mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get()); } mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId); mKeychain = keychain; return mPrimaryKey; }
// // Retrieve the trust setting for a (certificate, policy) pair. // SecTrustUserSetting TrustStore::find(Certificate *cert, Policy *policy, StorageManager::KeychainList &keychainList) { StLock<Mutex> _(mMutex); if (Item item = findItem(cert, policy, keychainList)) { // Make sure that the certificate is available in some keychain, // to provide a basis for editing the trust setting that we're returning. if (cert->keychain() == NULL) { if (cert->findInKeychain(keychainList) == NULL) { Keychain defaultKeychain = Keychain::optional(NULL); if (Keychain location = item->keychain()) { try { cert->copyTo(location); // add cert to the trust item's keychain } catch (...) { secdebug("trusteval", "failed to add certificate %p to keychain \"%s\"", cert, location->name()); try { if (&*location != &*defaultKeychain) cert->copyTo(defaultKeychain); // try the default (if it's not the same) } catch (...) { // unable to add the certificate secdebug("trusteval", "failed to add certificate %p to keychain \"%s\"", cert, defaultKeychain->name()); } } } } } CssmDataContainer data; item->getData(data); if (data.length() != sizeof(TrustData)) MacOSError::throwMe(errSecInvalidTrustSetting); TrustData &trust = *data.interpretedAs<TrustData>(); if (trust.version != UserTrustItem::currentVersion) MacOSError::throwMe(errSecInvalidTrustSetting); return trust.trust; } else { return kSecTrustResultUnspecified; } }
Item Certificate::copyTo(const Keychain &keychain, Access *newAccess) { StLock<Mutex>_(mMutex); /* Certs can't have access controls. */ if (newAccess) MacOSError::throwMe(errSecNoAccessForItem); Item item(new Certificate(data(), type(), encoding())); keychain->add(item); return item; }
void KCEventNotifier::PostKeychainEvent(SecKeychainEvent whichEvent, const Keychain &keychain, const Item &kcItem) { DLDbIdentifier dlDbIdentifier; PrimaryKey primaryKey; if (keychain) dlDbIdentifier = keychain->dlDbIdentifier(); if (kcItem) primaryKey = kcItem->primaryKey(); PostKeychainEvent(whichEvent, dlDbIdentifier, primaryKey); }
// // Set an individual trust element // void TrustStore::assign(Certificate *cert, Policy *policy, SecTrustUserSetting trust) { StLock<Mutex> _(mMutex); TrustData trustData = { UserTrustItem::currentVersion, trust }; Keychain defaultKeychain = Keychain::optional(NULL); Keychain trustLocation = defaultKeychain; // default keychain, unless trust entry found StorageManager::KeychainList searchList; globals().storageManager.getSearchList(searchList); if (Item item = findItem(cert, policy, searchList)) { // user has a trust setting in a keychain - modify that trustLocation = item->keychain(); if (trust == kSecTrustResultUnspecified) item->keychain()->deleteItem(item); else item->modifyContent(NULL, sizeof(trustData), &trustData); } else { // no trust entry: make one if (trust != kSecTrustResultUnspecified) { Item item = new UserTrustItem(cert, policy, trustData); if (Keychain location = cert->keychain()) { try { location->add(item); // try the cert's keychain first trustLocation = location; } catch (...) { if (&*location != &*defaultKeychain) defaultKeychain->add(item); // try the default (if it's not the same) } } else { defaultKeychain->add(item); // raw cert - use default keychain } } } // Make sure that the certificate is available in some keychain, // to provide a basis for editing the trust setting that we're assigning. if (cert->keychain() == NULL) { if (cert->findInKeychain(searchList) == NULL) { try { cert->copyTo(trustLocation); // add cert to the trust item's keychain } catch (...) { secdebug("trusteval", "failed to add certificate %p to keychain \"%s\"", cert, trustLocation->name()); try { if (&*trustLocation != &*defaultKeychain) cert->copyTo(defaultKeychain); // try the default (if it's not the same) } catch (...) { // unable to add the certificate secdebug("trusteval", "failed to add certificate %p to keychain \"%s\"", cert, defaultKeychain->name()); } } } } }
Item ItemImpl::copyTo(const Keychain &keychain, Access *newAccess) { StLock<Mutex>_(mMutex); Item item(*this); if (newAccess) item->setAccess(newAccess); else { /* Attempt to copy the access from the current item to the newly created one. */ SSGroup myGroup = group(); if (myGroup) { SecPointer<Access> access = new Access(*myGroup); item->setAccess(access); } } keychain->addCopy(item); return item; }
static OSStatus _SecIdentityAddPreferenceItemWithName( SecKeychainRef keychainRef, SecIdentityRef identityRef, CFStringRef idString, SecKeychainItemRef *itemRef) { // this is NOT exported, and called only from SecIdentityAddPreferenceItem (below), so no BEGIN/END macros here; // caller must handle exceptions if (!identityRef || !idString) return errSecParam; SecPointer<Certificate> cert(Identity::required(identityRef)->certificate()); Item item(kSecGenericPasswordItemClass, 'aapl', 0, NULL, false); sint32 keyUsage = 0; // determine the account attribute // // This attribute must be synthesized from certificate label + pref item type + key usage, // as only the account and service attributes can make a generic keychain item unique. // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that // we can save a certificate preference if an identity preference already exists for the // given service name, and vice-versa. // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string. // CFStringRef labelStr = nil; cert->inferLabel(false, &labelStr); if (!labelStr) { return errSecDataTooLarge; // data is "in a format which cannot be displayed" } CFIndex accountUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr), kCFStringEncodingUTF8) + 1; const char *templateStr = "%s [key usage 0x%X]"; const int keyUsageMaxStrLen = 8; accountUTF8Len += strlen(templateStr) + keyUsageMaxStrLen; char accountUTF8[accountUTF8Len]; if (!CFStringGetCString(labelStr, accountUTF8, accountUTF8Len-1, kCFStringEncodingUTF8)) accountUTF8[0] = (char)'\0'; if (keyUsage) snprintf(accountUTF8, accountUTF8Len-1, templateStr, accountUTF8, keyUsage); snprintf(accountUTF8, accountUTF8Len-1, "%s ", accountUTF8); CssmData account(const_cast<char *>(accountUTF8), strlen(accountUTF8)); CFRelease(labelStr); // service attribute (name provided by the caller) CFIndex serviceUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(idString), kCFStringEncodingUTF8) + 1;; char serviceUTF8[serviceUTF8Len]; if (!CFStringGetCString(idString, serviceUTF8, serviceUTF8Len-1, kCFStringEncodingUTF8)) serviceUTF8[0] = (char)'\0'; CssmData service(const_cast<char *>(serviceUTF8), strlen(serviceUTF8)); // set item attribute values item->setAttribute(Schema::attributeInfo(kSecServiceItemAttr), service); item->setAttribute(Schema::attributeInfo(kSecLabelItemAttr), service); item->setAttribute(Schema::attributeInfo(kSecTypeItemAttr), (FourCharCode)'iprf'); item->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account); item->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr), keyUsage); // generic attribute (store persistent certificate reference) CFDataRef pItemRef = nil; OSStatus status = SecKeychainItemCreatePersistentReference((SecKeychainItemRef)cert->handle(), &pItemRef); if (!pItemRef) status = errSecInvalidItemRef; if (status) return status; const UInt8 *dataPtr = CFDataGetBytePtr(pItemRef); CFIndex dataLen = CFDataGetLength(pItemRef); CssmData pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr)), dataLen); item->setAttribute(Schema::attributeInfo(kSecGenericItemAttr), pref); CFRelease(pItemRef); Keychain keychain = nil; try { keychain = Keychain::optional(keychainRef); if (!keychain->exists()) MacOSError::throwMe(errSecNoSuchKeychain); // Might be deleted or not available at this time. } catch(...) { keychain = globals().storageManager.defaultKeychainUI(item); } try { keychain->add(item); } catch (const MacOSError &err) { if (err.osStatus() != errSecDuplicateItem) throw; // if item already exists, fall through to update } item->update(); if (itemRef) *itemRef = item->handle(); return status; }
OSStatus SecIdentitySetPreference( SecIdentityRef identity, CFStringRef name, CSSM_KEYUSE keyUsage) { if (!name) { return errSecParam; } if (!identity) { // treat NULL identity as a request to clear the preference // (note: if keyUsage is 0, this clears all key usage prefs for name) return SecIdentityDeletePreferenceItemWithNameAndKeyUsage(NULL, name, keyUsage); } BEGIN_SECAPI SecPointer<Certificate> certificate(Identity::required(identity)->certificate()); // determine the account attribute // // This attribute must be synthesized from certificate label + pref item type + key usage, // as only the account and service attributes can make a generic keychain item unique. // For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that // we can save a certificate preference if an identity preference already exists for the // given service name, and vice-versa. // If the key usage is 0 (i.e. the normal case), we omit the appended key usage string. // CFStringRef labelStr = nil; certificate->inferLabel(false, &labelStr); if (!labelStr) { MacOSError::throwMe(errSecDataTooLarge); // data is "in a format which cannot be displayed" } CFIndex accountUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr), kCFStringEncodingUTF8) + 1; const char *templateStr = "%s [key usage 0x%X]"; const int keyUsageMaxStrLen = 8; accountUTF8Len += strlen(templateStr) + keyUsageMaxStrLen; char accountUTF8[accountUTF8Len]; if (!CFStringGetCString(labelStr, accountUTF8, accountUTF8Len-1, kCFStringEncodingUTF8)) accountUTF8[0] = (char)'\0'; if (keyUsage) snprintf(accountUTF8, accountUTF8Len-1, templateStr, accountUTF8, keyUsage); snprintf(accountUTF8, accountUTF8Len-1, "%s ", accountUTF8); CssmData account(const_cast<char *>(accountUTF8), strlen(accountUTF8)); CFRelease(labelStr); // service attribute (name provided by the caller) CFIndex serviceUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(name), kCFStringEncodingUTF8) + 1;; char serviceUTF8[serviceUTF8Len]; if (!CFStringGetCString(name, serviceUTF8, serviceUTF8Len-1, kCFStringEncodingUTF8)) serviceUTF8[0] = (char)'\0'; CssmData service(const_cast<char *>(serviceUTF8), strlen(serviceUTF8)); // look for existing identity preference item, in case this is an update StorageManager::KeychainList keychains; globals().storageManager.getSearchList(keychains); KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL); FourCharCode itemType = 'iprf'; cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), service); cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecTypeItemAttr), itemType); if (keyUsage) { cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage); } Item item(kSecGenericPasswordItemClass, 'aapl', 0, NULL, false); bool add = (!cursor->next(item)); // at this point, we either have a new item to add or an existing item to update // set item attribute values item->setAttribute(Schema::attributeInfo(kSecServiceItemAttr), service); item->setAttribute(Schema::attributeInfo(kSecTypeItemAttr), itemType); item->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account); item->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage); item->setAttribute(Schema::attributeInfo(kSecLabelItemAttr), service); // generic attribute (store persistent certificate reference) CFDataRef pItemRef = nil; certificate->copyPersistentReference(pItemRef); if (!pItemRef) { MacOSError::throwMe(errSecInvalidItemRef); } const UInt8 *dataPtr = CFDataGetBytePtr(pItemRef); CFIndex dataLen = CFDataGetLength(pItemRef); CssmData pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr)), dataLen); item->setAttribute(Schema::attributeInfo(kSecGenericItemAttr), pref); CFRelease(pItemRef); if (add) { Keychain keychain = nil; try { keychain = globals().storageManager.defaultKeychain(); if (!keychain->exists()) MacOSError::throwMe(errSecNoSuchKeychain); // Might be deleted or not available at this time. } catch(...) { keychain = globals().storageManager.defaultKeychainUI(item); } try { keychain->add(item); } catch (const MacOSError &err) { if (err.osStatus() != errSecDuplicateItem) throw; // if item already exists, fall through to update } } item->update(); END_SECAPI }
SecPointer<KeyItem> KeyItem::generateWithAttributes(const SecKeychainAttributeList *attrList, Keychain keychain, CSSM_ALGORITHMS algorithm, uint32 keySizeInBits, CSSM_CC_HANDLE contextHandle, CSSM_KEYUSE keyUsage, uint32 keyAttr, SecPointer<Access> initialAccess) { CssmClient::CSP appleCsp(gGuidAppleCSP); CssmClient::CSP csp(NULL); SSDb ssDb(NULL); uint8 labelBytes[20]; CssmData label(labelBytes, sizeof(labelBytes)); bool freeKey = false; bool deleteContext = false; const CSSM_DATA *plabel = NULL; if (keychain) { if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)) MacOSError::throwMe(errSecInvalidKeychain); SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*keychain->database())); if (impl == NULL) { CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); } ssDb = SSDb(impl); csp = keychain->csp(); // Generate a random label to use initially CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW); random.generate(label, label.Length); plabel = &label; } else { // Not a persistent key so create it in the regular csp csp = appleCsp; } // Create a Access::Maker for the initial owner of the private key. ResourceControlContext *prcc = NULL, rcc; const AccessCredentials *cred = NULL; Access::Maker maker; if (keychain && initialAccess) { memset(&rcc, 0, sizeof(rcc)); // @@@ Potentially provide a credential argument which allows us to generate keys in the csp. // Currently the CSP lets anyone do this, but we might restrict this in the future, e.g. a smartcard // could require out-of-band pin entry before a key can be generated. maker.initialOwner(rcc); // Create the cred we need to manipulate the keys until we actually set a new access control for them. cred = maker.cred(); prcc = &rcc; } CSSM_KEY cssmKey; CSSM_CC_HANDLE ccHandle = 0; Item keyItem; try { CSSM_RETURN status; if (contextHandle) ccHandle = contextHandle; else { status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle); if (status) CssmError::throwMe(status); deleteContext = true; } if (ssDb) { CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle(); CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle; CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } }; status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes); if (status) CssmError::throwMe(status); keyAttr |= CSSM_KEYATTR_PERMANENT; } // Generate the key status = CSSM_GenerateKey(ccHandle, keyUsage, keyAttr, plabel, prcc, &cssmKey); if (status) CssmError::throwMe(status); if (ssDb) { freeKey = true; // Find the key we just generated in the DL and get a SecKeyRef // so we can specify the label attribute(s) and initial ACL. // Look up key in the DLDB. DbAttributes dbAttributes; DbUniqueRecord uniqueId; SSDbCursor dbCursor(ssDb, 1); dbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY); dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label); CssmClient::Key key; if (!dbCursor->nextKey(&dbAttributes, key, uniqueId)) MacOSError::throwMe(errSecItemNotFound); // Set the initial label, application label, and application tag (if provided) if (attrList) { DbAttributes newDbAttributes; SSDbCursor otherDbCursor(ssDb, 1); otherDbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY); bool checkForDuplicates = false; for (UInt32 index=0; index < attrList->count; index++) { SecKeychainAttribute attr = attrList->attr[index]; CssmData attrData(attr.data, attr.length); if (attr.tag == kSecKeyPrintName) { newDbAttributes.add(kInfoKeyPrintName, attrData); } if (attr.tag == kSecKeyLabel) { newDbAttributes.add(kInfoKeyLabel, attrData); otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, attrData); checkForDuplicates = true; } if (attr.tag == kSecKeyApplicationTag) { newDbAttributes.add(kInfoKeyApplicationTag, attrData); otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyApplicationTag, attrData); checkForDuplicates = true; } } DbAttributes otherDbAttributes; DbUniqueRecord otherUniqueId; CssmClient::Key otherKey; if (checkForDuplicates && otherDbCursor->nextKey(&otherDbAttributes, otherKey, otherUniqueId)) MacOSError::throwMe(errSecDuplicateItem); uniqueId->modify(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, &newDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); } // Finally, fix the acl and owner of the key to the specified access control settings. if (initialAccess) initialAccess->setAccess(*key, maker); // Create keychain item which will represent the key. keyItem = keychain->item(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, uniqueId); } else { CssmClient::Key tempKey(csp, cssmKey); keyItem = new KeyItem(tempKey); } } catch (...) { if (freeKey) { // Delete the key if something goes wrong so we don't end up with inaccessible keys in the database. CSSM_FreeKey(csp->handle(), cred, &cssmKey, TRUE); } if (deleteContext) CSSM_DeleteContext(ccHandle); throw; } if (freeKey) { CSSM_FreeKey(csp->handle(), NULL, &cssmKey, FALSE); } if (deleteContext) CSSM_DeleteContext(ccHandle); if (keychain && keyItem) keychain->postEvent(kSecAddEvent, keyItem); KeyItem* item = dynamic_cast<KeyItem*>(&*keyItem); if (item == NULL) { CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); } return item; }
void KeyItem::importPair( Keychain keychain, const CSSM_KEY &publicWrappedKey, const CSSM_KEY &privateWrappedKey, SecPointer<Access> initialAccess, SecPointer<KeyItem> &outPublicKey, SecPointer<KeyItem> &outPrivateKey) { bool freePublicKey = false; bool freePrivateKey = false; bool deleteContext = false; if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)) MacOSError::throwMe(errSecInvalidKeychain); SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*keychain->database())); if (impl == NULL) { CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); } SSDb ssDb(impl); CssmClient::CSP csp(keychain->csp()); CssmClient::CSP appleCsp(gGuidAppleCSP); // Create a Access::Maker for the initial owner of the private key. ResourceControlContext rcc; memset(&rcc, 0, sizeof(rcc)); Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType); // @@@ Potentially provide a credential argument which allows us to unwrap keys in the csp. // Currently the CSP lets anyone do this, but we might restrict this in the future, e.g. // a smartcard could require out of band pin entry before a key can be generated. maker.initialOwner(rcc); // Create the cred we need to manipulate the keys until we actually set a new access control for them. const AccessCredentials *cred = maker.cred(); CSSM_KEY publicCssmKey, privateCssmKey; memset(&publicCssmKey, 0, sizeof(publicCssmKey)); memset(&privateCssmKey, 0, sizeof(privateCssmKey)); CSSM_CC_HANDLE ccHandle = 0; Item publicKeyItem, privateKeyItem; try { CSSM_RETURN status; // Calculate the hash of the public key using the appleCSP. CssmClient::PassThrough passThrough(appleCsp); void *outData; CssmData *cssmData; /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the * associated key blob. * Key is specified in CSSM_CSP_CreatePassThroughContext. * Hash is allocated bythe CSP, in the App's memory, and returned * in *outData. */ passThrough.key(&publicWrappedKey); passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData); cssmData = reinterpret_cast<CssmData *>(outData); CssmData &pubKeyHash = *cssmData; status = CSSM_CSP_CreateSymmetricContext(csp->handle(), publicWrappedKey.KeyHeader.WrapAlgorithmId, CSSM_ALGMODE_NONE, NULL, NULL, NULL, CSSM_PADDING_NONE, NULL, &ccHandle); if (status) CssmError::throwMe(status); deleteContext = true; CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle(); CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle; CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } }; status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes); if (status) CssmError::throwMe(status); // Unwrap the the keys CSSM_DATA descriptiveData = {0, NULL}; status = CSSM_UnwrapKey( ccHandle, NULL, &publicWrappedKey, publicWrappedKey.KeyHeader.KeyUsage, publicWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT, &pubKeyHash, &rcc, &publicCssmKey, &descriptiveData); if (status) CssmError::throwMe(status); freePublicKey = true; if (descriptiveData.Data != NULL) free (descriptiveData.Data); status = CSSM_UnwrapKey( ccHandle, NULL, &privateWrappedKey, privateWrappedKey.KeyHeader.KeyUsage, privateWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT, &pubKeyHash, &rcc, &privateCssmKey, &descriptiveData); if (status) CssmError::throwMe(status); if (descriptiveData.Data != NULL) free (descriptiveData.Data); freePrivateKey = true; // Find the keys we just generated in the DL to get SecKeyRefs to them // so we can change the label to be the hash of the public key, and // fix up other attributes. // Look up public key in the DLDB. DbAttributes pubDbAttributes; DbUniqueRecord pubUniqueId; SSDbCursor dbPubCursor(ssDb, 1); dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY); dbPubCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, pubKeyHash); CssmClient::Key publicKey; if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId)) MacOSError::throwMe(errSecItemNotFound); // Look up private key in the DLDB. DbAttributes privDbAttributes; DbUniqueRecord privUniqueId; SSDbCursor dbPrivCursor(ssDb, 1); dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY); dbPrivCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, pubKeyHash); CssmClient::Key privateKey; if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId)) MacOSError::throwMe(errSecItemNotFound); // @@@ Not exception safe! csp.allocator().free(cssmData->Data); csp.allocator().free(cssmData); auto_ptr<string>privDescription; auto_ptr<string>pubDescription; try { privDescription.reset(new string(initialAccess->promptDescription())); pubDescription.reset(new string(initialAccess->promptDescription())); } catch(...) { /* this path taken if no promptDescription available, e.g., for complex ACLs */ privDescription.reset(new string("Private key")); pubDescription.reset(new string("Public key")); } // Set the label of the public key to the public key hash. // Set the PrintName of the public key to the description in the acl. pubDbAttributes.add(kInfoKeyPrintName, *pubDescription); pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); // Set the label of the private key to the public key hash. // Set the PrintName of the private key to the description in the acl. privDbAttributes.add(kInfoKeyPrintName, *privDescription); privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); // Finally fix the acl and owner of the private key to the specified access control settings. initialAccess->setAccess(*privateKey, maker); // Make the public key acl completely open SecPointer<Access> pubKeyAccess(new Access()); pubKeyAccess->setAccess(*publicKey, maker); // Create keychain items which will represent the keys. publicKeyItem = keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId); privateKeyItem = keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId); KeyItem* impl = dynamic_cast<KeyItem*>(&(*publicKeyItem)); if (impl == NULL) { CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); } outPublicKey = impl; impl = dynamic_cast<KeyItem*>(&(*privateKeyItem)); if (impl == NULL) { CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); } outPrivateKey = impl; } catch (...) { if (freePublicKey) CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE); if (freePrivateKey) CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE); if (deleteContext) CSSM_DeleteContext(ccHandle); throw; } if (freePublicKey) CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, FALSE); if (freePrivateKey) CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, FALSE); if (deleteContext) CSSM_DeleteContext(ccHandle); if (keychain && publicKeyItem && privateKeyItem) { KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, publicKeyItem); KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, privateKeyItem); } }
void KeyItem::createPair( Keychain keychain, CSSM_ALGORITHMS algorithm, uint32 keySizeInBits, CSSM_CC_HANDLE contextHandle, CSSM_KEYUSE publicKeyUsage, uint32 publicKeyAttr, CSSM_KEYUSE privateKeyUsage, uint32 privateKeyAttr, SecPointer<Access> initialAccess, SecPointer<KeyItem> &outPublicKey, SecPointer<KeyItem> &outPrivateKey) { bool freeKeys = false; bool deleteContext = false; if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)) MacOSError::throwMe(errSecInvalidKeychain); SSDbImpl* impl = dynamic_cast<SSDbImpl*>(&(*keychain->database())); if (impl == NULL) { CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); } SSDb ssDb(impl); CssmClient::CSP csp(keychain->csp()); CssmClient::CSP appleCsp(gGuidAppleCSP); // Generate a random label to use initially CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW); uint8 labelBytes[20]; CssmData label(labelBytes, sizeof(labelBytes)); random.generate(label, label.Length); // Create a Access::Maker for the initial owner of the private key. ResourceControlContext rcc; memset(&rcc, 0, sizeof(rcc)); Access::Maker maker; // @@@ Potentially provide a credential argument which allows us to generate keys in the csp. Currently the CSP let's anyone do this, but we might restrict this in the future, f.e. a smartcard could require out of band pin entry before a key can be generated. maker.initialOwner(rcc); // Create the cred we need to manipulate the keys until we actually set a new access control for them. const AccessCredentials *cred = maker.cred(); CSSM_KEY publicCssmKey, privateCssmKey; memset(&publicCssmKey, 0, sizeof(publicCssmKey)); memset(&privateCssmKey, 0, sizeof(privateCssmKey)); CSSM_CC_HANDLE ccHandle = 0; Item publicKeyItem, privateKeyItem; try { CSSM_RETURN status; if (contextHandle) ccHandle = contextHandle; else { status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle); if (status) CssmError::throwMe(status); deleteContext = true; } CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle(); CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle; CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } }; status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes); if (status) CssmError::throwMe(status); // Generate the keypair status = CSSM_GenerateKeyPair(ccHandle, publicKeyUsage, publicKeyAttr, &label, &publicCssmKey, privateKeyUsage, privateKeyAttr, &label, &rcc, &privateCssmKey); if (status) CssmError::throwMe(status); freeKeys = true; // Find the keys we just generated in the DL to get SecKeyRef's to them // so we can change the label to be the hash of the public key, and // fix up other attributes. // Look up public key in the DLDB. DbAttributes pubDbAttributes; DbUniqueRecord pubUniqueId; SSDbCursor dbPubCursor(ssDb, 1); dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY); dbPubCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label); CssmClient::Key publicKey; if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId)) MacOSError::throwMe(errSecItemNotFound); // Look up private key in the DLDB. DbAttributes privDbAttributes; DbUniqueRecord privUniqueId; SSDbCursor dbPrivCursor(ssDb, 1); dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY); dbPrivCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label); CssmClient::Key privateKey; if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId)) MacOSError::throwMe(errSecItemNotFound); // Convert reference public key to a raw key so we can use it // in the appleCsp. CssmClient::WrapKey wrap(csp, CSSM_ALGID_NONE); wrap.cred(cred); CssmClient::Key rawPubKey = wrap(publicKey); // Calculate the hash of the public key using the appleCSP. CssmClient::PassThrough passThrough(appleCsp); void *outData; CssmData *cssmData; /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the * associated key blob. * Key is specified in CSSM_CSP_CreatePassThroughContext. * Hash is allocated bythe CSP, in the App's memory, and returned * in *outData. */ passThrough.key(rawPubKey); passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData); cssmData = reinterpret_cast<CssmData *>(outData); CssmData &pubKeyHash = *cssmData; auto_ptr<string>privDescription; auto_ptr<string>pubDescription; try { privDescription.reset(new string(initialAccess->promptDescription())); pubDescription.reset(new string(initialAccess->promptDescription())); } catch(...) { /* this path taken if no promptDescription available, e.g., for complex ACLs */ privDescription.reset(new string("Private key")); pubDescription.reset(new string("Public key")); } // Set the label of the public key to the public key hash. // Set the PrintName of the public key to the description in the acl. pubDbAttributes.add(kInfoKeyLabel, pubKeyHash); pubDbAttributes.add(kInfoKeyPrintName, *pubDescription); pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); // Set the label of the private key to the public key hash. // Set the PrintName of the private key to the description in the acl. privDbAttributes.add(kInfoKeyLabel, pubKeyHash); privDbAttributes.add(kInfoKeyPrintName, *privDescription); privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); // @@@ Not exception safe! csp.allocator().free(cssmData->Data); csp.allocator().free(cssmData); // Finally fix the acl and owner of the private key to the specified access control settings. initialAccess->setAccess(*privateKey, maker); if(publicKeyAttr & CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT) { /* * Make the public key acl completely open. * If the key was not encrypted, it already has a wide-open * ACL (though that is a feature of securityd; it's not * CDSA-specified behavior). */ SecPointer<Access> pubKeyAccess(new Access()); pubKeyAccess->setAccess(*publicKey, maker); } // Create keychain items which will represent the keys. publicKeyItem = keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId); privateKeyItem = keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId); KeyItem* impl = dynamic_cast<KeyItem*>(&(*publicKeyItem)); if (impl == NULL) { CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); } outPublicKey = impl; impl = dynamic_cast<KeyItem*>(&(*privateKeyItem)); if (impl == NULL) { CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); } outPrivateKey = impl; } catch (...) { if (freeKeys) { // Delete the keys if something goes wrong so we don't end up with inaccessible keys in the database. CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE); CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE); } if (deleteContext) CSSM_DeleteContext(ccHandle); throw; } if (freeKeys) { CSSM_FreeKey(csp->handle(), NULL, &publicCssmKey, FALSE); CSSM_FreeKey(csp->handle(), NULL, &privateCssmKey, FALSE); } if (deleteContext) CSSM_DeleteContext(ccHandle); if (keychain && publicKeyItem && privateKeyItem) { keychain->postEvent(kSecAddEvent, publicKeyItem); keychain->postEvent(kSecAddEvent, privateKeyItem); } }
Item KeyItem::importTo(const Keychain &keychain, Access *newAccess, SecKeychainAttributeList *attrList) { if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)) MacOSError::throwMe(errSecInvalidKeychain); /* Get the destination keychain's db. */ SSDbImpl* dbImpl = dynamic_cast<SSDbImpl*>(&(*keychain->database())); if (dbImpl == NULL) CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); SSDb ssDb(dbImpl); /* Make sure mKey is valid. */ /* We can't call key() here, since we won't have a unique record id yet */ if (!mKey) CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); // Generate a random label to use initially CssmClient::CSP appleCsp(gGuidAppleCSP); CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW); uint8 labelBytes[20]; CssmData label(labelBytes, sizeof(labelBytes)); random.generate(label, label.Length); /* Set up the ACL for the new key. */ SecPointer<Access> access; if (newAccess) access = newAccess; else access = new Access(*mKey); /* Generate a random 3DES wrapping Key. */ CssmClient::GenerateKey genKey(csp(), CSSM_ALGID_3DES_3KEY, 192); CssmClient::Key wrappingKey(genKey(KeySpec(CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP, CSSM_KEYATTR_EXTRACTABLE /* | CSSM_KEYATTR_RETURN_DATA */))); /* make a random IV */ uint8 ivBytes[8]; CssmData iv(ivBytes, sizeof(ivBytes)); random.generate(iv, iv.length()); /* Extract the key by wrapping it with the wrapping key. */ CssmClient::WrapKey wrap(csp(), CSSM_ALGID_3DES_3KEY_EDE); wrap.key(wrappingKey); wrap.cred(getCredentials(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, kSecCredentialTypeDefault)); wrap.mode(CSSM_ALGMODE_ECBPad); wrap.padding(CSSM_PADDING_PKCS7); wrap.initVector(iv); CssmClient::Key wrappedKey(wrap(mKey)); /* Unwrap the new key into the new Keychain. */ CssmClient::UnwrapKey unwrap(keychain->csp(), CSSM_ALGID_3DES_3KEY_EDE); unwrap.key(wrappingKey); unwrap.mode(CSSM_ALGMODE_ECBPad); unwrap.padding(CSSM_PADDING_PKCS7); unwrap.initVector(iv); /* Setup the dldbHandle in the context. */ unwrap.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, ssDb->handle()); /* Set up an initial aclEntry so we can change it after the unwrap. */ Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType); ResourceControlContext rcc; maker.initialOwner(rcc, NULL); unwrap.owner(rcc.input()); /* Unwrap the key. */ uint32 usage = mKey->usage(); /* Work around csp brokeness where it sets all usage bits in the Keyheader when CSSM_KEYUSE_ANY is set. */ if (usage & CSSM_KEYUSE_ANY) usage = CSSM_KEYUSE_ANY; CssmClient::Key unwrappedKey(unwrap(wrappedKey, KeySpec(usage, (mKey->attributes() | CSSM_KEYATTR_PERMANENT) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE), label))); /* Look up unwrapped key in the DLDB. */ DbUniqueRecord uniqueId; SSDbCursor dbCursor(ssDb, 1); dbCursor->recordType(recordType()); dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label); CssmClient::Key copiedKey; if (!dbCursor->nextKey(NULL, copiedKey, uniqueId)) MacOSError::throwMe(errSecItemNotFound); // Set the initial label, application label, and application tag (if provided) if (attrList) { DbAttributes newDbAttributes; SSDbCursor otherDbCursor(ssDb, 1); otherDbCursor->recordType(recordType()); bool checkForDuplicates = false; for (UInt32 index=0; index < attrList->count; index++) { SecKeychainAttribute attr = attrList->attr[index]; CssmData attrData(attr.data, attr.length); if (attr.tag == kSecKeyPrintName) { newDbAttributes.add(kInfoKeyPrintName, attrData); } if (attr.tag == kSecKeyLabel) { newDbAttributes.add(kInfoKeyLabel, attrData); otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, attrData); checkForDuplicates = true; } if (attr.tag == kSecKeyApplicationTag) { newDbAttributes.add(kInfoKeyApplicationTag, attrData); otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyApplicationTag, attrData); checkForDuplicates = true; } } DbAttributes otherDbAttributes; DbUniqueRecord otherUniqueId; CssmClient::Key otherKey; try { if (checkForDuplicates && otherDbCursor->nextKey(&otherDbAttributes, otherKey, otherUniqueId)) MacOSError::throwMe(errSecDuplicateItem); uniqueId->modify(recordType(), &newDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); } catch (CssmError e) { // clean up after trying to insert a duplicate key uniqueId->deleteRecord (); throw; } } /* Set the acl and owner on the unwrapped key. */ access->setAccess(*unwrappedKey, maker); /* Return a keychain item which represents the new key. */ Item item(keychain->item(recordType(), uniqueId)); KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, item); return item; }
PrimaryKey ItemImpl::addWithCopyInfo (Keychain &keychain, bool isCopy) { StLock<Mutex>_(mMutex); // If we already have a Keychain we can't be added. if (mKeychain) MacOSError::throwMe(errSecDuplicateItem); // If we don't have any attributes we can't be added. // (this might occur if attempting to add the item twice, since our attributes // and data are set to NULL at the end of this function.) if (!mDbAttributes.get()) MacOSError::throwMe(errSecDuplicateItem); CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType(); // update the creation and update dates on the new item if (!isCopy) { KeychainSchema schema = keychain->keychainSchema(); SInt64 date; GetCurrentMacLongDateTime(date); if (schema->hasAttribute(recordType, kSecCreationDateItemAttr)) { setAttribute(schema->attributeInfoFor(recordType, kSecCreationDateItemAttr), date); } if (schema->hasAttribute(recordType, kSecModDateItemAttr)) { setAttribute(schema->attributeInfoFor(recordType, kSecModDateItemAttr), date); } } // If the label (PrintName) attribute isn't specified, set a default label. if (!mDoNotEncrypt && !mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr))) { // if doNotEncrypt was set all of the attributes are wrapped in the data blob. Don't calculate here. CssmDbAttributeData *label = NULL; switch (recordType) { case CSSM_DL_DB_RECORD_GENERIC_PASSWORD: label = mDbAttributes->find(Schema::attributeInfo(kSecServiceItemAttr)); break; case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD: case CSSM_DL_DB_RECORD_INTERNET_PASSWORD: label = mDbAttributes->find(Schema::attributeInfo(kSecServerItemAttr)); // if AppleShare server name wasn't specified, try the server address if (!label) label = mDbAttributes->find(Schema::attributeInfo(kSecAddressItemAttr)); break; default: break; } // if all else fails, use the account name. if (!label) label = mDbAttributes->find(Schema::attributeInfo(kSecAccountItemAttr)); if (label && label->size()) setAttribute (Schema::attributeInfo(kSecLabelItemAttr), label->at<CssmData>(0)); } // get the attributes that are part of the primary key const CssmAutoDbRecordAttributeInfo &primaryKeyInfos = keychain->primaryKeyInfosFor(recordType); // make sure each primary key element has a value in the item, otherwise // the database will complain. we make a set of the provided attribute infos // to avoid O(N^2) behavior. DbAttributes *attributes = mDbAttributes.get(); typedef set<CssmDbAttributeInfo> InfoSet; InfoSet infoSet; if (!mDoNotEncrypt) { // make a set of all the attributes in the key for (uint32 i = 0; i < attributes->size(); i++) infoSet.insert(attributes->at(i).Info); for (uint32 i = 0; i < primaryKeyInfos.size(); i++) { // check to make sure all required attributes are in the key InfoSet::const_iterator it = infoSet.find(primaryKeyInfos.at(i)); if (it == infoSet.end()) { // not in the key? add the default // we need to add a default value to the item attributes attributes->add(primaryKeyInfos.at(i), defaultAttributeValue(primaryKeyInfos.at(i))); } } } Db db(keychain->database()); if (mDoNotEncrypt) { mUniqueId = db->insertWithoutEncryption (recordType, NULL, mData.get()); } else if (useSecureStorage(db)) { // Add the item to the secure storage db SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*db)); if (impl == NULL) { CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); } SSDb ssDb(impl); TrackingAllocator allocator(Allocator::standard()); // hhs replaced with the new aclFactory class AclFactory aclFactory; const AccessCredentials *nullCred = aclFactory.nullCred(); SecPointer<Access> access = mAccess; if (!access) { // create default access controls for the new item CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr)); string printName = data ? CssmData::overlay(data->Value[0]).toString() : "keychain item"; access = new Access(printName); // special case for "iTools" password - allow anyone to decrypt the item if (recordType == CSSM_DL_DB_RECORD_GENERIC_PASSWORD) { CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecServiceItemAttr)); if (data && data->Value[0].Length == 6 && !memcmp("iTools", data->Value[0].Data, 6)) { typedef vector<SecPointer<ACL> > AclSet; AclSet acls; access->findAclsForRight(CSSM_ACL_AUTHORIZATION_DECRYPT, acls); for (AclSet::const_iterator it = acls.begin(); it != acls.end(); it++) (*it)->form(ACL::allowAllForm); } } } // Get the handle of the DL underlying this CSPDL. CSSM_DL_DB_HANDLE dldbh; db->passThrough(CSSM_APPLECSPDL_DB_GET_HANDLE, NULL, reinterpret_cast<void **>(&dldbh)); // Turn off autocommit on the underlying DL and remember the old state. CSSM_BOOL autoCommit = CSSM_TRUE; ObjectImpl::check(CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 0, reinterpret_cast<void **>(&autoCommit))); try { // Create a new SSGroup with temporary access controls Access::Maker maker; ResourceControlContext prototype; maker.initialOwner(prototype, nullCred); SSGroup ssGroup(ssDb, &prototype); try { // Insert the record using the newly created group. mUniqueId = ssDb->insert(recordType, mDbAttributes.get(), mData.get(), ssGroup, maker.cred()); } catch(...) { ssGroup->deleteKey(nullCred); throw; } // now finalize the access controls on the group access->setAccess(*ssGroup, maker); mAccess = NULL; // use them and lose them if (autoCommit) { // autoCommit was on so commit now that we are done and turn // it back on. ObjectImpl::check(CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_COMMIT, NULL, NULL)); CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, reinterpret_cast<const void *>(autoCommit), NULL); } } catch (...) { if (autoCommit) { // autoCommit was off so rollback since we failed and turn // autoCommit back on. CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_ROLLBACK, NULL, NULL); CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, reinterpret_cast<const void *>(autoCommit), NULL); } throw; } } else { // add the item to the (regular) db mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get()); } mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId); mKeychain = keychain; // Forget our data and attributes. mData = NULL; mDbAttributes.reset(NULL); return mPrimaryKey; }
bool operator () (Keychain kc) { return kc->database()->dl()->guid() != guid; }