From 0c89b3ccba7c9b7332ab67ae1936aff51ca62367 Mon Sep 17 00:00:00 2001 From: NingSun Date: Thu, 8 Feb 2018 08:34:03 -0800 Subject: Initial sshsm project structure Issue-ID: AAF-94 Change-Id: I5e82fff418e7567b161acf9b98013a9b85ffc5b4 Signed-off-by: NingSun --- SoftHSMv2/src/lib/object_store/ObjectFile.cpp | 870 ++++++++++++++++++++++++++ 1 file changed, 870 insertions(+) create mode 100644 SoftHSMv2/src/lib/object_store/ObjectFile.cpp (limited to 'SoftHSMv2/src/lib/object_store/ObjectFile.cpp') diff --git a/SoftHSMv2/src/lib/object_store/ObjectFile.cpp b/SoftHSMv2/src/lib/object_store/ObjectFile.cpp new file mode 100644 index 0000000..f3becbe --- /dev/null +++ b/SoftHSMv2/src/lib/object_store/ObjectFile.cpp @@ -0,0 +1,870 @@ +/* + * Copyright (c) 2010 SURFnet bv + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/***************************************************************************** + ObjectFile.h + + This class represents object files + *****************************************************************************/ + +#include "config.h" +#include "ObjectFile.h" +#include "OSToken.h" +#include "OSPathSep.h" +#ifndef _WIN32 +#include +#endif +#include +#include +#include + +// Attribute types +#define BOOLEAN_ATTR 0x1 +#define ULONG_ATTR 0x2 +#define BYTESTR_ATTR 0x3 +#define ATTRMAP_ATTR 0x4 +#define MECHSET_ATTR 0x5 + +// Constructor +ObjectFile::ObjectFile(OSToken* parent, std::string inPath, std::string inLockpath, bool isNew /* = false */) +{ + path = inPath; + gen = Generation::create(path); + objectMutex = MutexFactory::i()->getMutex(); + valid = (gen != NULL) && (objectMutex != NULL); + token = parent; + inTransaction = false; + transactionLockFile = NULL; + lockpath = inLockpath; + + if (!valid) return; + + if (!isNew) + { + DEBUG_MSG("Opened existing object %s", path.c_str()); + + refresh(true); + } + else + { + DEBUG_MSG("Created new object %s", path.c_str()); + + // Create an empty object file + store(); + } + +} + +// Destructor +ObjectFile::~ObjectFile() +{ + discardAttributes(); + + if (gen != NULL) + { + delete gen; + } + + MutexFactory::i()->recycleMutex(objectMutex); +} + +// Check if the specified attribute exists +bool ObjectFile::attributeExists(CK_ATTRIBUTE_TYPE type) +{ + MutexLocker lock(objectMutex); + + return valid && (attributes[type] != NULL); +} + +// Retrieve the specified attribute +OSAttribute ObjectFile::getAttribute(CK_ATTRIBUTE_TYPE type) +{ + MutexLocker lock(objectMutex); + + OSAttribute* attr = attributes[type]; + if (attr == NULL) + { + ERROR_MSG("The attribute does not exist: 0x%08X", type); + return OSAttribute((unsigned long)0); + } + + return *attr; +} + +bool ObjectFile::getBooleanValue(CK_ATTRIBUTE_TYPE type, bool val) +{ + MutexLocker lock(objectMutex); + + OSAttribute* attr = attributes[type]; + if (attr == NULL) + { + ERROR_MSG("The attribute does not exist: 0x%08X", type); + return val; + } + + if (attr->isBooleanAttribute()) + { + return attr->getBooleanValue(); + } + else + { + ERROR_MSG("The attribute is not a boolean: 0x%08X", type); + return val; + } +} + +unsigned long ObjectFile::getUnsignedLongValue(CK_ATTRIBUTE_TYPE type, unsigned long val) +{ + MutexLocker lock(objectMutex); + + OSAttribute* attr = attributes[type]; + if (attr == NULL) + { + ERROR_MSG("The attribute does not exist: 0x%08X", type); + return val; + } + + if (attr->isUnsignedLongAttribute()) + { + return attr->getUnsignedLongValue(); + } + else + { + ERROR_MSG("The attribute is not an unsigned long: 0x%08X", type); + return val; + } +} + +ByteString ObjectFile::getByteStringValue(CK_ATTRIBUTE_TYPE type) +{ + MutexLocker lock(objectMutex); + + ByteString val; + + OSAttribute* attr = attributes[type]; + if (attr == NULL) + { + ERROR_MSG("The attribute does not exist: 0x%08X", type); + return val; + } + + if (attr->isByteStringAttribute()) + { + return attr->getByteStringValue(); + } + else + { + ERROR_MSG("The attribute is not a byte string: 0x%08X", type); + return val; + } +} + +// Retrieve the next attribute type +CK_ATTRIBUTE_TYPE ObjectFile::nextAttributeType(CK_ATTRIBUTE_TYPE type) +{ + MutexLocker lock(objectMutex); + + std::map::iterator n = attributes.upper_bound(type); + + // skip null attributes + while ((n != attributes.end()) && (n->second == NULL)) + ++n; + + // return type or CKA_CLASS (= 0) + if (n == attributes.end()) + { + return CKA_CLASS; + } + else + { + return n->first; + } +} + +// Set the specified attribute +bool ObjectFile::setAttribute(CK_ATTRIBUTE_TYPE type, const OSAttribute& attribute) +{ + if (!valid) + { + DEBUG_MSG("Cannot update invalid object %s", path.c_str()); + + return false; + } + + { + MutexLocker lock(objectMutex); + + if (attributes[type] != NULL) + { + delete attributes[type]; + + attributes[type] = NULL; + } + + attributes[type] = new OSAttribute(attribute); + } + + store(); + + return valid; +} + +// Delete the specified attribute +bool ObjectFile::deleteAttribute(CK_ATTRIBUTE_TYPE type) +{ + if (!valid) + { + DEBUG_MSG("Cannot update invalid object %s", path.c_str()); + + return false; + } + + { + MutexLocker lock(objectMutex); + + if (attributes[type] == NULL) + { + DEBUG_MSG("Cannot delete attribute that doesn't exist in object %s", path.c_str()); + + return false; + } + + delete attributes[type]; + attributes.erase(type); + } + + store(); + + return valid; +} + +// The validity state of the object (refresh from disk as a side effect) +bool ObjectFile::isValid() +{ + refresh(); + + return valid; +} + +// Invalidate the object file externally; this method is normally +// only called by the OSToken class in case an object file has +// been deleted. +void ObjectFile::invalidate() +{ + valid = false; + + discardAttributes(); +} + +// Refresh the object if necessary +void ObjectFile::refresh(bool isFirstTime /* = false */) +{ + // Check if we're in the middle of a transaction + if (inTransaction) + { + DEBUG_MSG("The object is in a transaction"); + + return; + } + + // Refresh the associated token if set + if (!isFirstTime && (token != NULL)) + { + // This may cause this instance to become invalid + token->index(); + } + + // Check the generation + if (!isFirstTime && (gen == NULL || !gen->wasUpdated())) + { + DEBUG_MSG("The object generation has not been updated"); + + return; + } + + File objectFile(path); + + if (!objectFile.isValid()) + { + DEBUG_MSG("Object %s is invalid", path.c_str()); + + valid = false; + + return; + } + + objectFile.lock(); + + if (objectFile.isEmpty()) + { + DEBUG_MSG("Object %s is empty", path.c_str()); + + valid = false; + + return; + } + + DEBUG_MSG("Object %s has changed", path.c_str()); + + // Discard the existing set of attributes + discardAttributes(); + + MutexLocker lock(objectMutex); + + // Read back the generation number + unsigned long curGen; + + if (!objectFile.readULong(curGen)) + { + if (!objectFile.isEOF()) + { + DEBUG_MSG("Corrupt object file %s", path.c_str()); + + valid = false; + + objectFile.unlock(); + + return; + } + } + else + { + gen->set(curGen); + } + + // Read back the attributes + while (!objectFile.isEOF()) + { + unsigned long p11AttrType; + unsigned long osAttrType; + + if (!objectFile.readULong(p11AttrType)) + { + if (objectFile.isEOF()) + { + break; + } + + DEBUG_MSG("Corrupt object file %s", path.c_str()); + + valid = false; + + objectFile.unlock(); + + return; + } + + if (!objectFile.readULong(osAttrType)) + { + DEBUG_MSG("Corrupt object file %s", path.c_str()); + + valid = false; + + objectFile.unlock(); + + return; + } + + // Depending on the type, read back the actual value + if (osAttrType == BOOLEAN_ATTR) + { + bool value; + + if (!objectFile.readBool(value)) + { + DEBUG_MSG("Corrupt object file %s", path.c_str()); + + valid = false; + + objectFile.unlock(); + + return; + } + + if (attributes[p11AttrType] != NULL) + { + delete attributes[p11AttrType]; + } + + attributes[p11AttrType] = new OSAttribute(value); + } + else if (osAttrType == ULONG_ATTR) + { + unsigned long value; + + if (!objectFile.readULong(value)) + { + DEBUG_MSG("Corrupt object file %s", path.c_str()); + + valid = false; + + objectFile.unlock(); + + return; + } + + if (attributes[p11AttrType] != NULL) + { + delete attributes[p11AttrType]; + } + + attributes[p11AttrType] = new OSAttribute(value); + } + else if (osAttrType == BYTESTR_ATTR) + { + ByteString value; + + if (!objectFile.readByteString(value)) + { + DEBUG_MSG("Corrupt object file %s", path.c_str()); + + valid = false; + + objectFile.unlock(); + + return; + } + + if (attributes[p11AttrType] != NULL) + { + delete attributes[p11AttrType]; + } + + attributes[p11AttrType] = new OSAttribute(value); + } + else if (osAttrType == MECHSET_ATTR) + { + std::set value; + + if (!objectFile.readMechanismTypeSet(value)) + { + DEBUG_MSG("Corrupt object file %s", path.c_str()); + + valid = false; + + objectFile.unlock(); + + return; + } + + if (attributes[p11AttrType] != NULL) + { + delete attributes[p11AttrType]; + } + + attributes[p11AttrType] = new OSAttribute(value); + } + else if (osAttrType == ATTRMAP_ATTR) + { + std::map value; + + if (!objectFile.readAttributeMap(value)) + { + DEBUG_MSG("Corrupt object file %s", path.c_str()); + + valid = false; + + objectFile.unlock(); + + return; + } + + if (attributes[p11AttrType] != NULL) + { + delete attributes[p11AttrType]; + } + + attributes[p11AttrType] = new OSAttribute(value); + } + else + { + DEBUG_MSG("Corrupt object file %s with unknown attribute of type %d", path.c_str(), osAttrType); + + valid = false; + + objectFile.unlock(); + + return; + } + } + + objectFile.unlock(); + + valid = true; +} + +// Common write part in store() +// called with objectFile locked and returns with objectFile unlocked +bool ObjectFile::writeAttributes(File &objectFile) +{ + if (!gen->sync(objectFile)) + { + DEBUG_MSG("Failed to synchronize generation number from object %s", path.c_str()); + + objectFile.unlock(); + + return false; + } + + if (!objectFile.truncate()) + { + DEBUG_MSG("Failed to reset object %s", path.c_str()); + + objectFile.unlock(); + + return false; + } + + gen->update(); + + unsigned long newGen = gen->get(); + + if (!objectFile.writeULong(newGen)) + { + DEBUG_MSG("Failed to write new generation number to object %s", path.c_str()); + + gen->rollback(); + + objectFile.unlock(); + + return false; + } + + + for (std::map::iterator i = attributes.begin(); i != attributes.end(); i++) + { + if (i->second == NULL) + { + continue; + } + + unsigned long p11AttrType = i->first; + + if (!objectFile.writeULong(p11AttrType)) + { + DEBUG_MSG("Failed to write PKCS #11 attribute type to object %s", path.c_str()); + + objectFile.unlock(); + + return false; + } + + if (i->second->isBooleanAttribute()) + { + unsigned long osAttrType = BOOLEAN_ATTR; + bool value = i->second->getBooleanValue(); + + if (!objectFile.writeULong(osAttrType) || !objectFile.writeBool(value)) + { + DEBUG_MSG("Failed to write attribute to object %s", path.c_str()); + + objectFile.unlock(); + + return false; + } + } + else if (i->second->isUnsignedLongAttribute()) + { + unsigned long osAttrType = ULONG_ATTR; + unsigned long value = i->second->getUnsignedLongValue(); + + if (!objectFile.writeULong(osAttrType) || !objectFile.writeULong(value)) + { + DEBUG_MSG("Failed to write attribute to object %s", path.c_str()); + + objectFile.unlock(); + + return false; + } + } + else if (i->second->isByteStringAttribute()) + { + unsigned long osAttrType = BYTESTR_ATTR; + const ByteString& value = i->second->getByteStringValue(); + + if (!objectFile.writeULong(osAttrType) || !objectFile.writeByteString(value)) + { + DEBUG_MSG("Failed to write attribute to object %s", path.c_str()); + + objectFile.unlock(); + + return false; + } + } + else if (i->second->isMechanismTypeSetAttribute()) + { + unsigned long osAttrType = MECHSET_ATTR; + const std::set& value = i->second->getMechanismTypeSetValue(); + + if (!objectFile.writeULong(osAttrType) || !objectFile.writeMechanismTypeSet(value)) + { + DEBUG_MSG("Failed to write attribute to object %s", path.c_str()); + + objectFile.unlock(); + + return false; + } + } + else if (i->second->isAttributeMapAttribute()) + { + unsigned long osAttrType = ATTRMAP_ATTR; + const std::map& value = i->second->getAttributeMapValue(); + + if (!objectFile.writeULong(osAttrType) || !objectFile.writeAttributeMap(value)) + { + DEBUG_MSG("Failed to write attribute to object %s", path.c_str()); + + objectFile.unlock(); + + return false; + } + } + else + { + DEBUG_MSG("Unknown attribute type for object %s", path.c_str()); + + objectFile.unlock(); + + return false; + } + } + + objectFile.unlock(); + + return true; +} + +// Write the object to background storage +void ObjectFile::store(bool isCommit /* = false */) +{ + // Check if we're in the middle of a transaction + if (!isCommit && inTransaction) + { + return; + } + + if (!valid) + { + DEBUG_MSG("Cannot write back an invalid object %s", path.c_str()); + + return; + } + + File objectFile(path, true, true, true, false); + + if (!objectFile.isValid()) + { + DEBUG_MSG("Cannot open object %s for writing", path.c_str()); + + valid = false; + + return; + } + + objectFile.lock(); + + if (!isCommit) { + MutexLocker lock(objectMutex); + File lockFile(lockpath, false, true, true); + + if (!writeAttributes(objectFile)) + { + valid = false; + + return; + } + } + else + { + if (!writeAttributes(objectFile)) + { + valid = false; + + return; + } + } + + valid = true; +} + +// Discard the cached attributes +void ObjectFile::discardAttributes() +{ + MutexLocker lock(objectMutex); + + std::map cleanUp = attributes; + attributes.clear(); + + for (std::map::iterator i = cleanUp.begin(); i != cleanUp.end(); i++) + { + if (i->second == NULL) + { + continue; + } + + delete i->second; + i->second = NULL; + } +} + + +// Returns the file name of the object +std::string ObjectFile::getFilename() const +{ + if ((path.find_last_of(OS_PATHSEP) != std::string::npos) && + (path.find_last_of(OS_PATHSEP) < path.size())) + { + return path.substr(path.find_last_of(OS_PATHSEP) + 1); + } + else + { + return path; + } +} + +// Returns the file name of the lock +std::string ObjectFile::getLockname() const +{ + if ((lockpath.find_last_of(OS_PATHSEP) != std::string::npos) && + (lockpath.find_last_of(OS_PATHSEP) < lockpath.size())) + { + return lockpath.substr(lockpath.find_last_of(OS_PATHSEP) + 1); + } + else + { + return lockpath; + } +} + +// Start an attribute set transaction; this method is used when - for +// example - a key is generated and all its attributes need to be +// persisted in one go. +// +// N.B.: Starting a transaction locks the object! +bool ObjectFile::startTransaction(Access) +{ + MutexLocker lock(objectMutex); + + if (inTransaction) + { + return false; + } + + transactionLockFile = new File(lockpath, false, true, true); + + if (!transactionLockFile->isValid() || !transactionLockFile->lock()) + { + delete transactionLockFile; + transactionLockFile = NULL; + + ERROR_MSG("Failed to lock file %s for attribute transaction", lockpath.c_str()); + + return false; + } + + inTransaction = true; + + return true; +} + +// Commit an attribute transaction +bool ObjectFile::commitTransaction() +{ + MutexLocker lock(objectMutex); + + if (!inTransaction) + { + return false; + } + + if (transactionLockFile == NULL) + { + ERROR_MSG("Transaction lock file instance invalid!"); + + return false; + } + + // Special store case + store(true); + + if (!valid) + { + return false; + } + + transactionLockFile->unlock(); + + delete transactionLockFile; + transactionLockFile = NULL; + inTransaction = false; + + return true; +} + +// Abort an attribute transaction; loads back the previous version of the object from disk +bool ObjectFile::abortTransaction() +{ + { + MutexLocker lock(objectMutex); + + if (!inTransaction) + { + return false; + } + + if (transactionLockFile == NULL) + { + ERROR_MSG("Transaction lock file instance invalid!"); + + return false; + } + + transactionLockFile->unlock(); + + delete transactionLockFile; + transactionLockFile = NULL; + inTransaction = false; + } + + // Force reload from disk + refresh(true); + + return true; +} + +// Destroy the object; WARNING: pointers to the object become invalid after this call +bool ObjectFile::destroyObject() +{ + if (token == NULL) + { + ERROR_MSG("Cannot destroy an object that is not associated with a token"); + + return false; + } + + return token->deleteObject(this); +} + -- cgit 1.2.3-korg