/* * Copyright (c) 2004,2008,2011-2012 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@ */ // // SDDLSession.h - DL session for security server CSP/DL. // #include "SDDLSession.h" #include "SDCSPDLPlugin.h" #include "SDKey.h" #include #include #include using namespace CssmClient; using namespace SecurityServer; using namespace std; // // SDDLSession -- Security Server DL session // SDDLSession::SDDLSession(CSSM_MODULE_HANDLE handle, SDCSPDLPlugin &plug, const CSSM_VERSION &version, uint32 subserviceId, CSSM_SERVICE_TYPE subserviceType, CSSM_ATTACH_FLAGS attachFlags, const CSSM_UPCALLS &upcalls, DatabaseManager &databaseManager, SDCSPDLSession &ssCSPDLSession) : DLPluginSession(handle, plug, version, subserviceId, subserviceType, attachFlags, upcalls, databaseManager), mSDCSPDLSession(ssCSPDLSession), mClientSession(Allocator::standard(), static_cast(*this)) //mAttachment(mClientSession.attach(version, subserviceId, subserviceType, attachFlags)) { } SDDLSession::~SDDLSession() { } // Utility functions void SDDLSession::GetDbNames(CSSM_NAME_LIST_PTR &outNameList) { outNameList->String = this->PluginSession::alloc(); outNameList->NumStrings = 1; outNameList->String[0] = (char*) ""; // empty name will trigger dynamic lookup } void SDDLSession::FreeNameList(CSSM_NAME_LIST &inNameList) { this->PluginSession::free(inNameList.String); } void SDDLSession::DbDelete(const char *inDbName, const CSSM_NET_ADDRESS *inDbLocation, const AccessCredentials *inAccessCred) { unimplemented(); } // DbContext creation and destruction. void SDDLSession::DbCreate(const char *inDbName, const CSSM_NET_ADDRESS *inDbLocation, const CSSM_DBINFO &inDBInfo, CSSM_DB_ACCESS_TYPE inAccessRequest, const CSSM_RESOURCE_CONTROL_CONTEXT *inCredAndAclEntry, const void *inOpenParameters, CSSM_DB_HANDLE &outDbHandle) { unimplemented(); } void SDDLSession::DbOpen(const char *inDbName, const CSSM_NET_ADDRESS *inDbLocation, CSSM_DB_ACCESS_TYPE inAccessRequest, const AccessCredentials *inAccessCred, const void *inOpenParameters, CSSM_DB_HANDLE &outDbHandle) { outDbHandle = mClientSession.openToken(subserviceId(), inAccessCred, inDbName); } // Operations using DbContext instances. void SDDLSession::DbClose(CSSM_DB_HANDLE inDbHandle) { mClientSession.releaseDb(ClientSession::toIPCHandle(inDbHandle)); } void SDDLSession::CreateRelation(CSSM_DB_HANDLE inDbHandle, CSSM_DB_RECORDTYPE inRelationID, const char *inRelationName, uint32 inNumberOfAttributes, const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *inAttributeInfo, uint32 inNumberOfIndexes, const CSSM_DB_SCHEMA_INDEX_INFO &inIndexInfo) { unimplemented(); } void SDDLSession::DestroyRelation(CSSM_DB_HANDLE inDbHandle, CSSM_DB_RECORDTYPE inRelationID) { unimplemented(); } void SDDLSession::Authenticate(CSSM_DB_HANDLE inDbHandle, CSSM_DB_ACCESS_TYPE inAccessRequest, const AccessCredentials &inAccessCred) { mClientSession.authenticateDb((DbHandle)inDbHandle, inAccessRequest, &inAccessCred); } void SDDLSession::GetDbAcl(CSSM_DB_HANDLE inDbHandle, const CSSM_STRING *inSelectionTag, uint32 &outNumberOfAclInfos, CSSM_ACL_ENTRY_INFO_PTR &outAclInfos) { // @@@ inSelectionTag shouldn't be a CSSM_STRING * but just a CSSM_STRING. mClientSession.getDbAcl(ClientSession::toIPCHandle(inDbHandle), *inSelectionTag, outNumberOfAclInfos, AclEntryInfo::overlayVar(outAclInfos)); } void SDDLSession::ChangeDbAcl(CSSM_DB_HANDLE inDbHandle, const AccessCredentials &inAccessCred, const CSSM_ACL_EDIT &inAclEdit) { mClientSession.changeDbAcl(ClientSession::toIPCHandle(inDbHandle), inAccessCred, AclEdit::overlay(inAclEdit)); } void SDDLSession::GetDbOwner(CSSM_DB_HANDLE inDbHandle, CSSM_ACL_OWNER_PROTOTYPE &outOwner) { mClientSession.getDbOwner(ClientSession::toIPCHandle(inDbHandle), AclOwnerPrototype::overlay(outOwner)); } void SDDLSession::ChangeDbOwner(CSSM_DB_HANDLE inDbHandle, const AccessCredentials &inAccessCred, const CSSM_ACL_OWNER_PROTOTYPE &inNewOwner) { mClientSession.changeDbOwner(ClientSession::toIPCHandle(inDbHandle), inAccessCred, AclOwnerPrototype::overlay(inNewOwner)); } void SDDLSession::GetDbNameFromHandle(CSSM_DB_HANDLE inDbHandle, char **outDbName) { string name; mClientSession.getDbName(ClientSession::toIPCHandle(inDbHandle), name); memcpy(Required(outDbName) = static_cast(this->malloc(name.length() + 1)), name.c_str(), name.length() + 1); } void SDDLSession::DataInsert(CSSM_DB_HANDLE inDbHandle, CSSM_DB_RECORDTYPE inRecordType, const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes, const CssmData *inData, CSSM_DB_UNIQUE_RECORD_PTR &outUniqueId) { RecordHandle record; record = mClientSession.insertRecord(ClientSession::toIPCHandle(inDbHandle), inRecordType, CssmDbRecordAttributeData::overlay(inAttributes), inData); outUniqueId = makeDbUniqueRecord(record); } void SDDLSession::DataDelete(CSSM_DB_HANDLE inDbHandle, const CSSM_DB_UNIQUE_RECORD &inUniqueRecordIdentifier) { RecordHandle record = ClientSession::toIPCHandle(findDbUniqueRecord(inUniqueRecordIdentifier)); mClientSession.deleteRecord(ClientSession::toIPCHandle(inDbHandle), record); } void SDDLSession::DataModify(CSSM_DB_HANDLE inDbHandle, CSSM_DB_RECORDTYPE inRecordType, CSSM_DB_UNIQUE_RECORD &inoutUniqueRecordIdentifier, const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributesToBeModified, const CssmData *inDataToBeModified, CSSM_DB_MODIFY_MODE inModifyMode) { RecordHandle record = ClientSession::toIPCHandle(findDbUniqueRecord(inoutUniqueRecordIdentifier)); mClientSession.modifyRecord(ClientSession::toIPCHandle(inDbHandle), record, inRecordType, CssmDbRecordAttributeData::overlay(inAttributesToBeModified), inDataToBeModified, inModifyMode); //@@@ make a (new) unique record out of possibly modified "record"... } void SDDLSession::postGetRecord(RecordHandle record, U32HandleObject::Handle resultsHandle, CSSM_DB_HANDLE db, CssmDbRecordAttributeData *pAttributes, CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes, CssmData *inoutData, KeyHandle hKey) { // If the client didn't ask for data then it doesn't matter // if this record is a key or not, just return it. if (inoutData) { CSSM_DB_RECORDTYPE recordType = pAttributes->DataRecordType; if (!inoutAttributes) { // @@@ Free pAttributes } if (recordType == CSSM_DL_DB_RECORD_PUBLIC_KEY || recordType == CSSM_DL_DB_RECORD_PRIVATE_KEY || recordType == CSSM_DL_DB_RECORD_SYMMETRIC_KEY) { // This record is a key. The data returned is a CSSM_KEY // (with empty key data) with the header filled in. try { if (hKey == noKey) // tokend error - should have returned key handle CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); // Allocate storage for the key. CssmKey *outKey = inoutData->interpretedAs(); new SDKey(*this, *outKey, hKey, db, record, recordType, *inoutData); } catch (...) { try { mClientSession.releaseRecord(record); } catch(...) { secdebug("ssCrypt", "releaseRecord threw during catch"); } if (resultsHandle != CSSM_INVALID_HANDLE) { try { mClientSession.releaseSearch(resultsHandle); } catch(...) { secdebug("ssCrypt", "releaseSearch threw during catch"); } } throw; } } else { // not a key if (hKey != noKey) { try { mClientSession.releaseRecord(record); } catch(...) { secdebug("ssCrypt", "failed releasing bogus key handle"); } CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); } } } } CSSM_HANDLE SDDLSession::DataGetFirst(CSSM_DB_HANDLE inDbHandle, const CssmQuery *inQuery, CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes, CssmData *inoutData, CSSM_DB_UNIQUE_RECORD_PTR &outUniqueRecord) { // Setup so we always retrieve the attributes if the client asks for data, // even if the client doesn't want them so we can figure out if we just // retrieved a key. CssmDbRecordAttributeData attributes; CssmDbRecordAttributeData *pAttributes; if (inoutAttributes) pAttributes = CssmDbRecordAttributeData::overlay(inoutAttributes); else { pAttributes = &attributes; memset(pAttributes, 0, sizeof(attributes)); } RecordHandle record; SearchHandle resultsHandle = noSearch; KeyHandle keyId = noKey; record = mClientSession.findFirst(ClientSession::toIPCHandle(inDbHandle), CssmQuery::required(inQuery), resultsHandle, pAttributes, inoutData, keyId); if (!record) return CSSM_INVALID_HANDLE; postGetRecord(record, resultsHandle, inDbHandle, pAttributes, inoutAttributes, inoutData, keyId); outUniqueRecord = makeDbUniqueRecord(record); return resultsHandle; } bool SDDLSession::DataGetNext(CSSM_DB_HANDLE inDbHandle, CSSM_HANDLE inResultsHandle, CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes, CssmData *inoutData, CSSM_DB_UNIQUE_RECORD_PTR &outUniqueRecord) { // Setup so we always retrieve the attributes if the client asks for data, // even if the client doesn't want them so we can figure out if we just // retrieved a key. CssmDbRecordAttributeData attributes; CssmDbRecordAttributeData *pAttributes; if (inoutAttributes) pAttributes = CssmDbRecordAttributeData::overlay(inoutAttributes); else { pAttributes = &attributes; memset(pAttributes, 0, sizeof(attributes)); } RecordHandle record; KeyHandle keyId = noKey; record = mClientSession.findNext(ClientSession::toIPCHandle(inResultsHandle), pAttributes, inoutData, keyId); if (!record) return false; postGetRecord(record, CSSM_INVALID_HANDLE, inDbHandle, pAttributes, inoutAttributes, inoutData, keyId); outUniqueRecord = makeDbUniqueRecord(record); return true; } void SDDLSession::DataAbortQuery(CSSM_DB_HANDLE inDbHandle, CSSM_HANDLE inResultsHandle) { mClientSession.releaseSearch(ClientSession::toIPCHandle(inResultsHandle)); } void SDDLSession::DataGetFromUniqueRecordId(CSSM_DB_HANDLE inDbHandle, const CSSM_DB_UNIQUE_RECORD &inUniqueRecord, CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes, CssmData *inoutData) { // Setup so we always retrieve the attributes if the client asks for data, // even if the client doesn't want them so we can figure out if we just // retrieved a key. CssmDbRecordAttributeData attributes; CssmDbRecordAttributeData *pAttributes; if (inoutAttributes) pAttributes = CssmDbRecordAttributeData::overlay(inoutAttributes); else { pAttributes = &attributes; memset(pAttributes, 0, sizeof(attributes)); } RecordHandle record = ClientSession::toIPCHandle(findDbUniqueRecord(inUniqueRecord)); KeyHandle keyId = noKey; mClientSession.findRecordHandle(record, pAttributes, inoutData, keyId); postGetRecord(record, CSSM_INVALID_HANDLE, inDbHandle, pAttributes, inoutAttributes, inoutData, keyId); } void SDDLSession::FreeUniqueRecord(CSSM_DB_HANDLE inDbHandle, CSSM_DB_UNIQUE_RECORD &inUniqueRecordIdentifier) { RecordHandle record = ClientSession::toIPCHandle(findDbUniqueRecord(inUniqueRecordIdentifier)); freeDbUniqueRecord(inUniqueRecordIdentifier); mClientSession.releaseRecord(record); } void SDDLSession::PassThrough(CSSM_DB_HANDLE inDbHandle, uint32 inPassThroughId, const void *inInputParams, void **outOutputParams) { switch (inPassThroughId) { case CSSM_APPLECSPDL_DB_LOCK: mClientSession.lock(ClientSession::toIPCHandle(inDbHandle)); break; case CSSM_APPLECSPDL_DB_UNLOCK: { TrackingAllocator track(Allocator::standard()); AutoCredentials creds(track); creds.tag("PIN1"); if (inInputParams) creds += TypedList(track, CSSM_SAMPLE_TYPE_PASSWORD, new (track) ListElement(track, *reinterpret_cast(inInputParams))); else creds += TypedList(track, CSSM_SAMPLE_TYPE_PROMPTED_PASSWORD, new (track) ListElement(track, CssmData())); Authenticate(inDbHandle, CSSM_DB_ACCESS_READ, creds); break; } case CSSM_APPLECSPDL_DB_IS_LOCKED: { if (!outOutputParams) CssmError::throwMe(CSSM_ERRCODE_INVALID_OUTPUT_POINTER); bool isLocked = mClientSession.isLocked(ClientSession::toIPCHandle(inDbHandle)); CSSM_APPLECSPDL_DB_IS_LOCKED_PARAMETERS_PTR params = DatabaseSession::alloc(); params->isLocked = isLocked; *reinterpret_cast (outOutputParams) = params; break; } case CSSM_APPLECSPDL_DB_CHANGE_PASSWORD: { if (!inInputParams) CssmError::throwMe(CSSM_ERRCODE_INVALID_INPUT_POINTER); const CSSM_APPLECSPDL_DB_CHANGE_PASSWORD_PARAMETERS *params = reinterpret_cast (inInputParams); AutoAclEntryInfoList acls /* (mClientSession.allocator()) */; CSSM_STRING tag = { 'P', 'I', 'N', '1' }; GetDbAcl(inDbHandle, &tag, *static_cast(acls), *static_cast(acls)); if (acls.size() == 0) CssmError::throwMe(CSSM_ERRCODE_ACL_ENTRY_TAG_NOT_FOUND); const AclEntryInfo &slot = acls.at(0); if (acls.size() > 1) secdebug("acl", "Using entry handle %ld from %d total candidates", slot.handle(), acls.size()); AclEdit edit(slot.handle(), slot.proto()); ChangeDbAcl(inDbHandle, AccessCredentials::required(params->accessCredentials), edit); break; } case CSSM_APPLECSPDL_DB_RELATION_EXISTS: { // We always return true so that the individual tokend can decide if (!outOutputParams) CssmError::throwMe(CSSM_ERRCODE_INVALID_OUTPUT_POINTER); *reinterpret_cast(outOutputParams) = true; break; } default: CssmError::throwMe(CSSM_ERRCODE_INVALID_PASSTHROUGH_ID); } } CSSM_DB_UNIQUE_RECORD_PTR SDDLSession::makeDbUniqueRecord(RecordHandle uniqueId) { CSSM_DB_UNIQUE_RECORD *aUniqueRecord = DatabaseSession::alloc(); memset(aUniqueRecord, 0, sizeof(CSSM_DB_UNIQUE_RECORD)); aUniqueRecord->RecordIdentifier.Length = sizeof(CSSM_HANDLE); try { aUniqueRecord->RecordIdentifier.Data = DatabaseSession::alloc(sizeof(CSSM_HANDLE)); *reinterpret_cast(aUniqueRecord->RecordIdentifier.Data) = uniqueId; } catch(...) { allocator().free(aUniqueRecord); throw; } return aUniqueRecord; } // formerly returned a RecordHandle, but redefining them to be 32-bit made // that untenable CSSM_HANDLE SDDLSession::findDbUniqueRecord(const CSSM_DB_UNIQUE_RECORD &inUniqueRecord) { if (inUniqueRecord.RecordIdentifier.Length != sizeof(CSSM_HANDLE)) CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID); return *reinterpret_cast(inUniqueRecord.RecordIdentifier.Data); } void SDDLSession::freeDbUniqueRecord(CSSM_DB_UNIQUE_RECORD &inUniqueRecord) { if (inUniqueRecord.RecordIdentifier.Length != 0 && inUniqueRecord.RecordIdentifier.Data != NULL) { inUniqueRecord.RecordIdentifier.Length = 0; allocator().free(inUniqueRecord.RecordIdentifier.Data); } allocator().free(&inUniqueRecord); }