--- /dev/null
+/*
+ * 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 <security_cdsa_utilities/cssmbridge.h>
+#include <security_utilities/trackingallocator.h>
+#include <Security/cssmapplePriv.h>
+
+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<PluginSession &>(*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<char *>();
+ 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<char *>(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<CssmKey>();
+ 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<const CssmData *>(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<CSSM_APPLECSPDL_DB_IS_LOCKED_PARAMETERS>();
+ params->isLocked = isLocked;
+ *reinterpret_cast<CSSM_APPLECSPDL_DB_IS_LOCKED_PARAMETERS_PTR *>
+ (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
+ <const CSSM_APPLECSPDL_DB_CHANGE_PASSWORD_PARAMETERS *>
+ (inInputParams);
+
+ AutoAclEntryInfoList acls /* (mClientSession.allocator()) */;
+ CSSM_STRING tag = { 'P', 'I', 'N', '1' };
+ GetDbAcl(inDbHandle, &tag,
+ *static_cast<uint32 *>(acls),
+ *static_cast<CSSM_ACL_ENTRY_INFO **>(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<CSSM_BOOL *>(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<CSSM_DB_UNIQUE_RECORD>();
+ memset(aUniqueRecord, 0, sizeof(CSSM_DB_UNIQUE_RECORD));
+ aUniqueRecord->RecordIdentifier.Length = sizeof(CSSM_HANDLE);
+ try
+ {
+ aUniqueRecord->RecordIdentifier.Data = DatabaseSession::alloc<uint8>(sizeof(CSSM_HANDLE));
+ *reinterpret_cast<CSSM_HANDLE *>(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<CSSM_HANDLE *>(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);
+}