// // 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()); } } } } }
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 }
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; }
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 }