X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_keychain/lib/SecKeychain.cpp?ds=inline diff --git a/Security/libsecurity_keychain/lib/SecKeychain.cpp b/Security/libsecurity_keychain/lib/SecKeychain.cpp new file mode 100644 index 00000000..8a48d3f2 --- /dev/null +++ b/Security/libsecurity_keychain/lib/SecKeychain.cpp @@ -0,0 +1,1283 @@ +/* + * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "SecBridge.h" +#include "CCallbackMgr.h" +#include +#include +#include + +OSStatus +SecKeychainMDSInstall() +{ + BEGIN_SECAPI + + Security::MDSClient::Directory d; + d.install(); + + END_SECAPI +} + +CFTypeID +SecKeychainGetTypeID(void) +{ + BEGIN_SECAPI + + return gTypes().KeychainImpl.typeID; + + END_SECAPI1(_kCFRuntimeNotATypeID) +} + + +OSStatus +SecKeychainGetVersion(UInt32 *returnVers) +{ + if (!returnVers) + return errSecSuccess; + + *returnVers = 0x02028000; + return errSecSuccess; +} + + +OSStatus +SecKeychainOpen(const char *pathName, SecKeychainRef *keychainRef) +{ + BEGIN_SECAPI + + RequiredParam(keychainRef)=globals().storageManager.make(pathName, false)->handle(); + + END_SECAPI +} + + +OSStatus +SecKeychainOpenWithGuid(const CSSM_GUID *guid, uint32 subserviceId, uint32 subserviceType, const char* dbName, + const CSSM_NET_ADDRESS *dbLocation, SecKeychainRef *keychain) +{ + BEGIN_SECAPI + + // range check parameters + RequiredParam (guid); + RequiredParam (dbName); + + // create a DLDbIdentifier that describes what should be opened + const CSSM_VERSION *version = NULL; + const CssmSubserviceUid ssuid(*guid, version, subserviceId, subserviceType); + DLDbIdentifier dLDbIdentifier(ssuid, dbName, dbLocation); + + // make a keychain from the supplied info + RequiredParam(keychain) = globals().storageManager.makeKeychain(dLDbIdentifier, false)->handle (); + + END_SECAPI +} + + +OSStatus +SecKeychainCreate(const char *pathName, UInt32 passwordLength, const void *password, + Boolean promptUser, SecAccessRef initialAccess, SecKeychainRef *keychainRef) +{ + BEGIN_SECAPI + + KCThrowParamErrIf_(!pathName); + Keychain keychain = globals().storageManager.make(pathName); + + // @@@ the call to StorageManager::make above leaves keychain the the cache. + // If the create below fails we should probably remove it. + if(promptUser) + keychain->create(); + else + { + KCThrowParamErrIf_(!password); + keychain->create(passwordLength, password); + } + + RequiredParam(keychainRef)=keychain->handle(); + + END_SECAPI +} + + +OSStatus +SecKeychainDelete(SecKeychainRef keychainOrArray) +{ + BEGIN_SECAPI + + KCThrowIf_(!keychainOrArray, errSecInvalidKeychain); + StorageManager::KeychainList keychains; + globals().storageManager.optionalSearchList(keychainOrArray, keychains); + + globals().storageManager.remove(keychains, true); + + END_SECAPI +} + + +OSStatus +SecKeychainSetSettings(SecKeychainRef keychainRef, const SecKeychainSettings *newSettings) +{ + BEGIN_SECAPI + + Keychain keychain = Keychain::optional(keychainRef); + if (newSettings->version==SEC_KEYCHAIN_SETTINGS_VERS1) + { + UInt32 lockInterval=newSettings->lockInterval; + bool lockOnSleep=newSettings->lockOnSleep; + keychain->setSettings(lockInterval, lockOnSleep); + } + + END_SECAPI +} + + +OSStatus +SecKeychainCopySettings(SecKeychainRef keychainRef, SecKeychainSettings *outSettings) +{ + BEGIN_SECAPI + + Keychain keychain = Keychain::optional(keychainRef); + if (outSettings->version==SEC_KEYCHAIN_SETTINGS_VERS1) + { + uint32 lockInterval; + bool lockOnSleep; + + keychain->getSettings(lockInterval, lockOnSleep); + outSettings->lockInterval=lockInterval; + outSettings->lockOnSleep=lockOnSleep; + } + + END_SECAPI +} + + +OSStatus +SecKeychainUnlock(SecKeychainRef keychainRef, UInt32 passwordLength, const void *password, Boolean usePassword) +{ + BEGIN_SECAPI + + Keychain keychain = Keychain::optional(keychainRef); + + if (usePassword) + keychain->unlock(CssmData(const_cast(password), passwordLength)); + else + keychain->unlock(); + + END_SECAPI +} + + +OSStatus +SecKeychainLock(SecKeychainRef keychainRef) +{ + BEGIN_SECAPI + + Keychain keychain = Keychain::optional(keychainRef); + keychain->lock(); + + END_SECAPI +} + + +OSStatus +SecKeychainLockAll(void) +{ + BEGIN_SECAPI + + globals().storageManager.lockAll(); + + END_SECAPI +} + + +OSStatus SecKeychainResetLogin(UInt32 passwordLength, const void* password, Boolean resetSearchList) +{ + BEGIN_SECAPI + // + // Get the current user (using fallback method if necessary) + // + char* uName = getenv("USER"); + string userName = uName ? uName : ""; + if ( userName.length() == 0 ) + { + uid_t uid = geteuid(); + if (!uid) uid = getuid(); + struct passwd *pw = getpwuid(uid); // fallback case... + if (pw) + userName = pw->pw_name; + endpwent(); + } + if ( userName.length() == 0 ) // did we ultimately get one? + MacOSError::throwMe(errAuthorizationInternal); + + SecurityServer::ClientSession().resetKeyStorePassphrase(password ? CssmData(const_cast(password), passwordLength) : CssmData()); + + if (password) + { + // Clear the plist and move aside (rename) the existing login.keychain + globals().storageManager.resetKeychain(resetSearchList); + + // Create the login keychain without UI + globals().storageManager.login((UInt32)userName.length(), userName.c_str(), passwordLength, password); + + // Set it as the default + Keychain keychain = globals().storageManager.loginKeychain(); + globals().storageManager.defaultKeychain(keychain); + } + else + { + // Create the login keychain, prompting for password + // (implicitly calls resetKeychain, login, and defaultKeychain) + globals().storageManager.makeLoginAuthUI(NULL); + } + + // Post a "list changed" event after a reset, so apps can refresh their list. + // Make sure we are not holding mLock when we post this event. + KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); + + END_SECAPI +} + +OSStatus +SecKeychainCopyDefault(SecKeychainRef *keychainRef) +{ + BEGIN_SECAPI + + RequiredParam(keychainRef)=globals().storageManager.defaultKeychain()->handle(); + + END_SECAPI +} + + +OSStatus +SecKeychainSetDefault(SecKeychainRef keychainRef) +{ + BEGIN_SECAPI + + globals().storageManager.defaultKeychain(Keychain::optional(keychainRef)); + + END_SECAPI +} + +OSStatus SecKeychainCopySearchList(CFArrayRef *searchList) +{ + BEGIN_SECAPI + + RequiredParam(searchList); + StorageManager &smr = globals().storageManager; + StorageManager::KeychainList keychainList; + smr.getSearchList(keychainList); + *searchList = smr.convertFromKeychainList(keychainList); + + END_SECAPI +} + +OSStatus SecKeychainSetSearchList(CFArrayRef searchList) +{ + BEGIN_SECAPI + + RequiredParam(searchList); + StorageManager &smr = globals().storageManager; + StorageManager::KeychainList keychainList; + smr.convertToKeychainList(searchList, keychainList); + smr.setSearchList(keychainList); + + END_SECAPI +} + +OSStatus SecKeychainCopyDomainDefault(SecPreferencesDomain domain, SecKeychainRef *keychainRef) +{ + BEGIN_SECAPI + + RequiredParam(keychainRef)=globals().storageManager.defaultKeychain(domain)->handle(); + + END_SECAPI +} + +OSStatus SecKeychainSetDomainDefault(SecPreferencesDomain domain, SecKeychainRef keychainRef) +{ + BEGIN_SECAPI + + globals().storageManager.defaultKeychain(domain, Keychain::optional(keychainRef)); + + END_SECAPI +} + +OSStatus SecKeychainCopyDomainSearchList(SecPreferencesDomain domain, CFArrayRef *searchList) +{ + BEGIN_SECAPI + + RequiredParam(searchList); + StorageManager &smr = globals().storageManager; + StorageManager::KeychainList keychainList; + smr.getSearchList(domain, keychainList); + *searchList = smr.convertFromKeychainList(keychainList); + + END_SECAPI +} + +OSStatus SecKeychainSetDomainSearchList(SecPreferencesDomain domain, CFArrayRef searchList) +{ + BEGIN_SECAPI + + RequiredParam(searchList); + StorageManager &smr = globals().storageManager; + StorageManager::KeychainList keychainList; + smr.convertToKeychainList(searchList, keychainList); + smr.setSearchList(domain, keychainList); + + END_SECAPI +} + +OSStatus SecKeychainSetPreferenceDomain(SecPreferencesDomain domain) +{ + BEGIN_SECAPI + + globals().storageManager.domain(domain); + + END_SECAPI +} + +OSStatus SecKeychainGetPreferenceDomain(SecPreferencesDomain *domain) +{ + BEGIN_SECAPI + + *domain = globals().storageManager.domain(); + + END_SECAPI +} + + +OSStatus +SecKeychainGetStatus(SecKeychainRef keychainRef, SecKeychainStatus *keychainStatus) +{ + BEGIN_SECAPI + + RequiredParam(keychainStatus) = (SecKeychainStatus)Keychain::optional(keychainRef)->status(); + + END_SECAPI +} + + +OSStatus +SecKeychainGetPath(SecKeychainRef keychainRef, UInt32 *ioPathLength, char *pathName) +{ + BEGIN_SECAPI + + RequiredParam(pathName); + RequiredParam(ioPathLength); + + const char *name = Keychain::optional(keychainRef)->name(); + UInt32 nameLen = (UInt32)strlen(name); + UInt32 callersLen = *ioPathLength; + *ioPathLength = nameLen; + if (nameLen+1 > callersLen) // if the client's buffer is too small (including null-termination), throw + return errSecBufferTooSmall; + strncpy(pathName, name, nameLen); + pathName[nameLen] = 0; + *ioPathLength = nameLen; // set the length. + + END_SECAPI +} + + +// @@@ Deprecated +UInt16 +SecKeychainListGetCount(void) +{ + BEGIN_SECAPI + + return globals().storageManager.size(); + + END_SECAPI1(0) +} + + +// @@@ Deprecated +OSStatus +SecKeychainListCopyKeychainAtIndex(UInt16 index, SecKeychainRef *keychainRef) +{ + BEGIN_SECAPI + + KeychainCore::StorageManager &smgr=KeychainCore::globals().storageManager; + RequiredParam(keychainRef)=smgr[index]->handle(); + + END_SECAPI +} + + +// @@@ Deprecated +OSStatus +SecKeychainListRemoveKeychain(SecKeychainRef *keychainRef) +{ + BEGIN_SECAPI + + Required(keychainRef); + Keychain keychain = Keychain::optional(*keychainRef); + StorageManager::KeychainList keychainList; + keychainList.push_back(keychain); + globals().storageManager.remove(keychainList); + *keychainRef = NULL; + + END_SECAPI +} + + +OSStatus +SecKeychainAttributeInfoForItemID(SecKeychainRef keychainRef, UInt32 itemID, SecKeychainAttributeInfo **info) +{ + BEGIN_SECAPI + + Keychain keychain = Keychain::optional(keychainRef); + keychain->getAttributeInfoForItemID(itemID, info); + + END_SECAPI +} + + +OSStatus +SecKeychainFreeAttributeInfo(SecKeychainAttributeInfo *info) +{ + BEGIN_SECAPI + + KeychainImpl::freeAttributeInfo(info); + + END_SECAPI +} + + +pascal OSStatus +SecKeychainAddCallback(SecKeychainCallback callbackFunction, SecKeychainEventMask eventMask, void* userContext) +{ + BEGIN_SECAPI + + RequiredParam(callbackFunction); + CCallbackMgr::AddCallback(callbackFunction,eventMask,userContext); + + END_SECAPI +} + + +OSStatus +SecKeychainRemoveCallback(SecKeychainCallback callbackFunction) +{ + BEGIN_SECAPI + + RequiredParam(callbackFunction); + CCallbackMgr::RemoveCallback(callbackFunction); + + END_SECAPI +} + +OSStatus +SecKeychainAddInternetPassword(SecKeychainRef keychainRef, UInt32 serverNameLength, const char *serverName, UInt32 securityDomainLength, const char *securityDomain, UInt32 accountNameLength, const char *accountName, UInt32 pathLength, const char *path, UInt16 port, SecProtocolType protocol, SecAuthenticationType authenticationType, UInt32 passwordLength, const void *passwordData, SecKeychainItemRef *itemRef) +{ + BEGIN_SECAPI + + KCThrowParamErrIf_(passwordLength!=0 && passwordData==NULL); + // @@@ Get real itemClass + Item item(kSecInternetPasswordItemClass, 'aapl', passwordLength, passwordData, false); + + if (serverName && serverNameLength) + { + CssmData server(const_cast(reinterpret_cast(serverName)), serverNameLength); + item->setAttribute(Schema::attributeInfo(kSecServerItemAttr), server); + // use server name as default label + item->setAttribute(Schema::attributeInfo(kSecLabelItemAttr), server); + } + + if (accountName && accountNameLength) + { + CssmData account(const_cast(reinterpret_cast(accountName)), accountNameLength); + item->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account); + } + + if (securityDomain && securityDomainLength) + item->setAttribute(Schema::attributeInfo(kSecSecurityDomainItemAttr), + CssmData(const_cast(reinterpret_cast(securityDomain)), securityDomainLength)); + + item->setAttribute(Schema::attributeInfo(kSecPortItemAttr), UInt32(port)); + item->setAttribute(Schema::attributeInfo(kSecProtocolItemAttr), protocol); + item->setAttribute(Schema::attributeInfo(kSecAuthenticationTypeItemAttr), authenticationType); + + if (path && pathLength) + item->setAttribute(Schema::attributeInfo(kSecPathItemAttr), + CssmData(const_cast(reinterpret_cast(path)), pathLength)); + + 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 +SecKeychainFindInternetPassword(CFTypeRef keychainOrArray, UInt32 serverNameLength, const char *serverName, UInt32 securityDomainLength, const char *securityDomain, UInt32 accountNameLength, const char *accountName, UInt32 pathLength, const char *path, UInt16 port, SecProtocolType protocol, SecAuthenticationType authenticationType, UInt32 *passwordLength, void **passwordData, SecKeychainItemRef *itemRef) + +{ + BEGIN_SECAPI + + StorageManager::KeychainList keychains; + globals().storageManager.optionalSearchList(keychainOrArray, keychains); + KCCursor cursor(keychains, kSecInternetPasswordItemClass, NULL); + + if (serverName && serverNameLength) + { + cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServerItemAttr), + CssmData(const_cast(serverName), serverNameLength)); + } + + if (securityDomain && securityDomainLength) + { + cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecSecurityDomainItemAttr), + CssmData (const_cast(securityDomain), securityDomainLength)); + } + + if (accountName && accountNameLength) + { + cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecAccountItemAttr), + CssmData (const_cast(accountName), accountNameLength)); + } + + if (port) + { + cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecPortItemAttr), + UInt32(port)); + } + + if (protocol) + { + cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecProtocolItemAttr), + protocol); + } + + if (authenticationType) + { + cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecAuthenticationTypeItemAttr), + authenticationType); + } + + if (path && pathLength) + { + cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecPathItemAttr), path); + } + + Item item; + if (!cursor->next(item)) + return errSecItemNotFound; + + // Get its data (only if necessary) + if (passwordData || passwordLength) + { + CssmDataContainer outData; + item->getData(outData); + *passwordLength=(UInt32)outData.length(); + outData.Length=0; + *passwordData=outData.data(); + outData.Data=NULL; + } + + if (itemRef) + *itemRef=item->handle(); + + END_SECAPI +} + + +OSStatus +SecKeychainAddGenericPassword(SecKeychainRef keychainRef, UInt32 serviceNameLength, const char *serviceName, UInt32 accountNameLength, const char *accountName, UInt32 passwordLength, const void *passwordData, SecKeychainItemRef *itemRef) +{ + BEGIN_SECAPI + + KCThrowParamErrIf_(passwordLength!=0 && passwordData==NULL); + // @@@ Get real itemClass + + Item item(kSecGenericPasswordItemClass, 'aapl', passwordLength, passwordData, false); + + if (serviceName && serviceNameLength) + { + CssmData service(const_cast(reinterpret_cast(serviceName)), serviceNameLength); + item->setAttribute(Schema::attributeInfo(kSecServiceItemAttr), service); + // use service name as default label (UNLESS the service is iTools and we have an account name [3787371]) + const char *iTools = "iTools"; + if (accountNameLength && serviceNameLength==strlen(iTools) && !memcmp(serviceName, iTools, serviceNameLength)) + { + CssmData account(const_cast(reinterpret_cast(accountName)), accountNameLength); + item->setAttribute(Schema::attributeInfo(kSecLabelItemAttr), account); + } + else + { + item->setAttribute(Schema::attributeInfo(kSecLabelItemAttr), service); + } + } + + if (accountName && accountNameLength) + { + CssmData account(const_cast(reinterpret_cast(accountName)), accountNameLength); + item->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account); + } + + 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 +SecKeychainFindGenericPassword(CFTypeRef keychainOrArray, UInt32 serviceNameLength, const char *serviceName, UInt32 accountNameLength, const char *accountName, UInt32 *passwordLength, void **passwordData, SecKeychainItemRef *itemRef) + +{ + BEGIN_SECAPI + + StorageManager::KeychainList keychains; + globals().storageManager.optionalSearchList(keychainOrArray, keychains); + KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL); + + if (serviceName && serviceNameLength) + { + cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), + CssmData(const_cast(serviceName), serviceNameLength)); + } + + if (accountName && accountNameLength) + { + cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecAccountItemAttr), + CssmData(const_cast(accountName), accountNameLength)); + } + + Item item; + if (!cursor->next(item)) + return errSecItemNotFound; + + // Get its data (only if necessary) + if (passwordData || passwordLength) + { + CssmDataContainer outData; + item->getData(outData); + *passwordLength=(UInt32)outData.length(); + outData.Length=0; + *passwordData=outData.data(); + outData.Data=NULL; + } + + if (itemRef) + *itemRef=item->handle(); + + END_SECAPI +} + + +OSStatus +SecKeychainSetUserInteractionAllowed(Boolean state) +{ + BEGIN_SECAPI + + globals().setUserInteractionAllowed(state); + + END_SECAPI +} + + +OSStatus +SecKeychainGetUserInteractionAllowed(Boolean *state) +{ + BEGIN_SECAPI + + Required(state)=globals().getUserInteractionAllowed(); + + END_SECAPI +} + + +OSStatus +SecKeychainGetDLDBHandle(SecKeychainRef keychainRef, CSSM_DL_DB_HANDLE *dldbHandle) +{ + BEGIN_SECAPI + + RequiredParam(dldbHandle); + + Keychain keychain = Keychain::optional(keychainRef); + *dldbHandle = keychain->database()->handle(); + + END_SECAPI +} + + +OSStatus +SecKeychainGetCSPHandle(SecKeychainRef keychainRef, CSSM_CSP_HANDLE *cspHandle) +{ + BEGIN_SECAPI + + RequiredParam(cspHandle); + + Keychain keychain = Keychain::optional(keychainRef); + *cspHandle = keychain->csp()->handle(); + + END_SECAPI +} + + +OSStatus +SecKeychainCopyAccess(SecKeychainRef keychainRef, SecAccessRef *accessRef) +{ + BEGIN_SECAPI + + MacOSError::throwMe(errSecUnimplemented);//%%%for now + + END_SECAPI +} + + +OSStatus +SecKeychainSetAccess(SecKeychainRef keychainRef, SecAccessRef accessRef) +{ + BEGIN_SECAPI + + MacOSError::throwMe(errSecUnimplemented);//%%%for now + + END_SECAPI +} + + +#pragma mark ---- Private API ---- + + +OSStatus +SecKeychainChangePassword(SecKeychainRef keychainRef, UInt32 oldPasswordLength, const void *oldPassword, UInt32 newPasswordLength, const void *newPassword) +{ + BEGIN_SECAPI + + Keychain keychain = Keychain::optional(keychainRef); + keychain->changePassphrase (oldPasswordLength, oldPassword, newPasswordLength, newPassword); + + END_SECAPI +} + + +OSStatus +SecKeychainCopyLogin(SecKeychainRef *keychainRef) +{ + BEGIN_SECAPI + + RequiredParam(keychainRef)=globals().storageManager.loginKeychain()->handle(); + + END_SECAPI +} + + +OSStatus +SecKeychainLogin(UInt32 nameLength, const void* name, UInt32 passwordLength, const void* password) +{ + BEGIN_SECAPI + + try + { + if (password) { + globals().storageManager.login(nameLength, name, passwordLength, password); + } else { + globals().storageManager.stashLogin(); + } + } + catch (CommonError &e) + { + if (e.osStatus() == CSSMERR_DL_OPERATION_AUTH_DENIED) + { + return errSecAuthFailed; + } + else + { + return e.osStatus(); + } + } + + END_SECAPI +} + +OSStatus SecKeychainStash() +{ + BEGIN_SECAPI + + try + { + globals().storageManager.stashKeychain(); + } + catch (CommonError &e) + { + if (e.osStatus() == CSSMERR_DL_OPERATION_AUTH_DENIED) + { + return errSecAuthFailed; + } + else + { + return e.osStatus(); + } + } + + END_SECAPI +} + +OSStatus +SecKeychainLogout() +{ + BEGIN_SECAPI + + globals().storageManager.logout(); + + END_SECAPI +} + +/* (non-exported C utility routine) 'Makes' a keychain based on a full path +*/ +static Keychain make(const char *name) +{ + return globals().storageManager.make(name); +} + +/* 'Makes' a keychain based on a full path for legacy "KC" CoreServices APIs. + Note this version doesn't take an accessRef or password. + The "KC" create API takes a keychainRef... +*/ +OSStatus SecKeychainMakeFromFullPath(const char *fullPathName, SecKeychainRef *keychainRef) +{ + BEGIN_SECAPI + RequiredParam(fullPathName); + RequiredParam(keychainRef)=make(fullPathName)->handle(); + END_SECAPI +} + + +/* Determines if the keychainRef is a valid keychain. +*/ +OSStatus SecKeychainIsValid(SecKeychainRef keychainRef, Boolean* isValid) +{ + BEGIN_SECAPI + *isValid = false; + if (KeychainImpl::optional(keychainRef)->dlDbIdentifier().ssuid().guid() == gGuidAppleCSPDL) + *isValid = true; + END_SECAPI +} + +/* Removes a keychain from the keychain search list for legacy "KC" CoreServices APIs. +*/ +OSStatus SecKeychainRemoveFromSearchList(SecKeychainRef keychainRef) +{ + BEGIN_SECAPI + StorageManager::KeychainList singleton; + singleton.push_back(KeychainImpl::required(keychainRef)); + globals().storageManager.remove(singleton); + END_SECAPI +} + +/* Create a keychain based on a keychain Ref for legacy "KC" CoreServices APIs. +*/ +OSStatus SecKeychainCreateNew(SecKeychainRef keychainRef, UInt32 passwordLength, const char* inPassword) +{ + BEGIN_SECAPI + RequiredParam(inPassword); + KeychainImpl::required(keychainRef)->create(passwordLength, inPassword); + END_SECAPI +} + +/* Modify a keychain so that it can be synchronized. +*/ +OSStatus SecKeychainRecodeKeychain(SecKeychainRef keychainRef, CFArrayRef dbBlobArray, CFDataRef extraData) +{ + BEGIN_SECAPI + + // do error checking for required parameters + RequiredParam(dbBlobArray); + RequiredParam(extraData); + + const CssmData extraCssmData(const_cast(CFDataGetBytePtr(extraData)), + CFDataGetLength(extraData)); + + CFIndex dbBlobArrayCount = CFArrayGetCount(dbBlobArray); + size_t space = sizeof(uint8) + (dbBlobArrayCount * sizeof(SecurityServer::DbHandle)); + void *dataPtr = (void*)malloc(space); + if ( !dataPtr ) + return errSecAllocate; + // + // Get a DbHandle(IPCDbHandle) from securityd for each blob in the array that we'll authenticate with. + // + uint8* sizePtr = (uint8*)dataPtr; + *sizePtr = dbBlobArrayCount; + SecurityServer::DbHandle *currDbHandle = (SecurityServer::DbHandle *)(sizePtr+1); + CFIndex index; + SecurityServer::ClientSession ss(Allocator::standard(), Allocator::standard()); + for (index=0; index < dbBlobArrayCount; index++) + { + CFDataRef cfBlobData = (CFDataRef)CFArrayGetValueAtIndex(dbBlobArray, index); + const CssmData thisKCData(const_cast(CFDataGetBytePtr(cfBlobData)), CFDataGetLength(cfBlobData)); + // + // Since it's to a DbHandle that's not on our disk (it came from user's iDisk), + // it's OK to use the mIdentifier and access credentials of the keychain we're recoding. + // + Keychain kc = KeychainImpl::required(keychainRef); + *currDbHandle = ss.decodeDb(kc->dlDbIdentifier(), kc->defaultCredentials(), thisKCData); /* returns a DbHandle (IPCDbHandle) */ + + currDbHandle++; + } + // do the work + Keychain keychain = Keychain::optional(keychainRef); + const CssmData data(const_cast((uint8*)dataPtr), space); + Boolean recodeFailed = false; + + int errCode=errSecSuccess; + + try + { + keychain->recode(data, extraCssmData); + } + catch (MacOSError e) + { + errCode = e.osStatus(); + recodeFailed = true; + } + catch (UnixError ue) + { + errCode = ue.unixError(); + } + + currDbHandle = (SecurityServer::DbHandle *)(sizePtr+1); + for (index=0; index < dbBlobArrayCount; index++) + { + ss.releaseDb(*currDbHandle); + currDbHandle++; + } + if ( dataPtr ) + free(dataPtr); + + if ( recodeFailed ) + { + return errCode; + } + + END_SECAPI +} + +OSStatus SecKeychainCopySignature(SecKeychainRef keychainRef, CFDataRef *keychainSignature) +{ + BEGIN_SECAPI + + // do error checking for required parameters + RequiredParam(keychainSignature); + + // make a keychain object "wrapper" for this keychain ref + Keychain keychain = Keychain::optional(keychainRef); + CssmAutoData data(keychain->database()->allocator()); + keychain->copyBlob(data.get()); + + // get the cssmDBBlob + const SecurityServer::DbBlob *cssmDBBlob = + data.get().interpretedAs(); + + // convert from CDSA standards to CF standards + *keychainSignature = CFDataCreate(kCFAllocatorDefault, + cssmDBBlob->randomSignature.bytes, + sizeof(SecurityServer::DbBlob::Signature)); + + END_SECAPI +} + +OSStatus SecKeychainCopyBlob(SecKeychainRef keychainRef, CFDataRef *dbBlob) +{ + BEGIN_SECAPI + + // do error checking for required parameters + RequiredParam(dbBlob); + + // make a keychain object "wrapper" for this keychain ref + Keychain keychain = Keychain::optional(keychainRef); + CssmAutoData data(keychain->database()->allocator()); + keychain->copyBlob(data.get()); + + // convert from CDSA standards to CF standards + *dbBlob = CFDataCreate(kCFAllocatorDefault, data, data.length()); + + END_SECAPI +} + +// make a new keychain with pre-existing secrets +OSStatus SecKeychainCreateWithBlob(const char* fullPathName, CFDataRef dbBlob, SecKeychainRef *kcRef) +{ + BEGIN_SECAPI + + KCThrowParamErrIf_(!fullPathName); + KCThrowParamErrIf_(!dbBlob); + + Keychain keychain = globals().storageManager.make(fullPathName); + + CssmData blob(const_cast(CFDataGetBytePtr(dbBlob)), CFDataGetLength(dbBlob)); + + // @@@ the call to StorageManager::make above leaves keychain the the cache. + // If the create below fails we should probably remove it. + keychain->createWithBlob(blob); + + RequiredParam(kcRef)=keychain->handle(); + + // + + END_SECAPI +} + +// add a non-file based DB to the keychain list +OSStatus SecKeychainAddDBToKeychainList (SecPreferencesDomain domain, const char* dbName, + const CSSM_GUID *guid, uint32 subServiceType) +{ + BEGIN_SECAPI + + RequiredParam(dbName); + StorageManager &smr = globals().storageManager; + smr.addToDomainList(domain, dbName, *guid, subServiceType); + + END_SECAPI +} + +// determine if a non-file based DB is in the keychain list +OSStatus SecKeychainDBIsInKeychainList (SecPreferencesDomain domain, const char* dbName, + const CSSM_GUID *guid, uint32 subServiceType) +{ + BEGIN_SECAPI + RequiredParam(dbName); + StorageManager &smr = globals().storageManager; + smr.isInDomainList(domain, dbName, *guid, subServiceType); + END_SECAPI +} + +// remove a non-file based DB from the keychain list +OSStatus SecKeychainRemoveDBFromKeychainList (SecPreferencesDomain domain, const char* dbName, + const CSSM_GUID *guid, uint32 subServiceType) +{ + BEGIN_SECAPI + RequiredParam(dbName); + StorageManager &smr = globals().storageManager; + smr.removeFromDomainList(domain, dbName, *guid, subServiceType); + END_SECAPI +} + + +// set server mode -- must be called before any other Sec* etc. call +void SecKeychainSetServerMode() +{ + gServerMode = true; +} + + + +OSStatus SecKeychainSetBatchMode (SecKeychainRef kcRef, Boolean mode, Boolean rollback) +{ + BEGIN_SECAPI + RequiredParam(kcRef); + Keychain keychain = Keychain::optional(kcRef); + keychain->setBatchMode(mode, rollback); + END_SECAPI +} + + + +OSStatus SecKeychainCleanupHandles() +{ + BEGIN_SECAPI + END_SECAPI // which causes the handle cache cleanup routine to run +} + +OSStatus SecKeychainVerifyKeyStorePassphrase(uint32_t retries) +{ + BEGIN_SECAPI + SecurityServer::ClientSession().verifyKeyStorePassphrase(retries); + END_SECAPI +} + +OSStatus SecKeychainChangeKeyStorePassphrase() +{ + BEGIN_SECAPI + SecurityServer::ClientSession().changeKeyStorePassphrase(); + END_SECAPI +} + +static OSStatus SecKeychainGetMasterKey(SecKeychainRef userKeychainRef, CFDataRef *masterKey, CFStringRef password) +{ + BEGIN_SECAPI + + // make a keychain object "wrapper" for this keychain ref + Keychain keychain = Keychain::optional(userKeychainRef); + + CssmClient::Db db = keychain->database(); + + // create the keychain, using appropriate credentials + Allocator &alloc = db->allocator(); + AutoCredentials cred(alloc); // will leak, but we're quitting soon :-) + + char passphrase[1024]; + CFStringGetCString(password, passphrase, sizeof(passphrase), kCFStringEncodingUTF8); + + // use this passphrase + cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, + new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD), + new(alloc) ListElement(StringData(passphrase))); + db->accessCredentials(&cred); + + CSSM_DL_DB_HANDLE dlDb = db->handle(); + CssmData dlDbData = CssmData::wrap(dlDb); + CssmKey refKey; + KeySpec spec(CSSM_KEYUSE_ANY, + CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE); + + DeriveKey derive(keychain->csp(), CSSM_ALGID_KEYCHAIN_KEY, CSSM_ALGID_3DES_3KEY, 3 * 64); + derive(&dlDbData, spec, refKey); + + // now extract the raw keybits + CssmKey rawKey; + WrapKey wrap(keychain->csp(), CSSM_ALGID_NONE); + wrap(refKey, rawKey); + + *masterKey = CFDataCreate(kCFAllocatorDefault, rawKey.keyData(), rawKey.length()); + + END_SECAPI +} + + +OSStatus SecKeychainStoreUnlockKey(SecKeychainRef userKeychainRef, SecKeychainRef systemKeychainRef, CFStringRef username, CFStringRef password) { + SecTrustedApplicationRef itemPath; + SecAccessRef ourAccessRef = NULL; + + OSStatus result = errSecParam; + + CFDataRef masterKey = NULL; + result = SecKeychainGetMasterKey(userKeychainRef, &masterKey, password); + if (errSecSuccess != result) { + return result; + } + + result = SecKeychainStash(); + if (errSecSuccess != result) { + if (NULL != masterKey) CFRelease(masterKey); + return result; + } + + CFMutableArrayRef trustedApplications = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + if ( noErr == SecTrustedApplicationCreateApplicationGroup("com.apple.security.auto-login", NULL, &itemPath) && itemPath ) + CFArrayAppendValue(trustedApplications, itemPath); + + if ( trustedApplications && (CFArrayGetCount(trustedApplications) > 0)) { + if (errSecSuccess == (result = SecAccessCreate(CFSTR("Auto-Login applications"), trustedApplications, &ourAccessRef))) { + if (NULL == systemKeychainRef) { + SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, &systemKeychainRef); + } + + const void *queryKeys[] = { kSecClass, + kSecAttrService, + kSecAttrAccount, + kSecUseKeychain, + }; + const void *queryValues[] = { kSecClassGenericPassword, + CFSTR("com.apple.loginwindow.auto-login"), + username, + systemKeychainRef, + }; + + const void *updateKeys[] = { kSecAttrAccess, + kSecValueData, + }; + const void *updateValues[] = { ourAccessRef, + masterKey, + }; + + CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefault, queryKeys, queryValues, sizeof(queryValues)/sizeof(*queryValues), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryRef update = CFDictionaryCreate(kCFAllocatorDefault, updateKeys, updateValues, sizeof(updateValues)/sizeof(*updateValues), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + result = SecItemUpdate(query, update); + + if (errSecSuccess != result) { + const void *addKeys[] = { kSecClass, + kSecAttrService, + kSecAttrAccount, + kSecUseKeychain, + kSecAttrAccess, + kSecValueData, + }; + const void *addValues[] = { kSecClassGenericPassword, + CFSTR("com.apple.loginwindow.auto-login"), + username, + systemKeychainRef, + ourAccessRef, + masterKey, + }; + + CFDictionaryRef add = CFDictionaryCreate(kCFAllocatorDefault, addKeys, addValues, sizeof(addValues)/sizeof(*addValues), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + result = SecItemAdd(add, NULL); + if (NULL != add) CFRelease(add); + } + + if (NULL != query) CFRelease(query); + if (NULL != update) CFRelease(update); + } + } + + if (NULL != masterKey) CFRelease(masterKey); + if (NULL != trustedApplications) CFRelease(trustedApplications); + if (NULL != ourAccessRef) CFRelease(ourAccessRef); + if (NULL != systemKeychainRef) CFRelease(systemKeychainRef); + + return result; +}