diff options
author | NingSun <ning.sun@intel.com> | 2018-02-08 08:34:03 -0800 |
---|---|---|
committer | NingSun <ning.sun@intel.com> | 2018-02-08 09:14:52 -0800 |
commit | 0c89b3ccba7c9b7332ab67ae1936aff51ca62367 (patch) | |
tree | 70c1b1d160d4c6d0a83395ca9a87c1264d0d3439 /SoftHSMv2/src/lib/object_store/OSToken.cpp | |
parent | 945613b4db4e07f75d2bc7463db580ddfaa700fd (diff) |
Initial sshsm project structure
Issue-ID: AAF-94
Change-Id: I5e82fff418e7567b161acf9b98013a9b85ffc5b4
Signed-off-by: NingSun <ning.sun@intel.com>
Diffstat (limited to 'SoftHSMv2/src/lib/object_store/OSToken.cpp')
-rw-r--r-- | SoftHSMv2/src/lib/object_store/OSToken.cpp | 722 |
1 files changed, 722 insertions, 0 deletions
diff --git a/SoftHSMv2/src/lib/object_store/OSToken.cpp b/SoftHSMv2/src/lib/object_store/OSToken.cpp new file mode 100644 index 0000000..13b1eaa --- /dev/null +++ b/SoftHSMv2/src/lib/object_store/OSToken.cpp @@ -0,0 +1,722 @@ +/* + * 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. + */ + +/***************************************************************************** + OSToken.cpp + + The token class; a token is stored in a directory containing several files. + Each object is stored in a separate file and a token object is present that + has the token specific attributes + *****************************************************************************/ + +#include "config.h" +#include "log.h" +#include "OSAttributes.h" +#include "OSAttribute.h" +#include "ObjectFile.h" +#include "Directory.h" +#include "Generation.h" +#include "UUID.h" +#include "cryptoki.h" +#include "OSToken.h" +#include "OSPathSep.h" +#include <vector> +#include <string> +#include <set> +#include <map> +#include <list> +#include <stdio.h> + +// Constructor +OSToken::OSToken(const std::string inTokenPath) +{ + tokenPath = inTokenPath; + + tokenDir = new Directory(tokenPath); + gen = Generation::create(tokenPath + OS_PATHSEP + "generation", true); + tokenObject = new ObjectFile(this, tokenPath + OS_PATHSEP + "token.object", tokenPath + OS_PATHSEP + "token.lock"); + tokenMutex = MutexFactory::i()->getMutex(); + valid = (gen != NULL) && (tokenMutex != NULL) && tokenDir->isValid() && tokenObject->valid; + + DEBUG_MSG("Opened token %s", tokenPath.c_str()); + + index(true); +} + +// Create a new token +/*static*/ OSToken* OSToken::createToken(const std::string basePath, const std::string tokenDir, const ByteString& label, const ByteString& serial) +{ + Directory baseDir(basePath); + + if (!baseDir.isValid()) + { + ERROR_MSG("Could not create the Directory object"); + return NULL; + } + + // Create the token directory + if (!baseDir.mkdir(tokenDir)) + { + // Error msg is generated by mkdir + return NULL; + } + + // Create the token object + ObjectFile tokenObject(NULL, basePath + OS_PATHSEP + tokenDir + OS_PATHSEP + "token.object", basePath + OS_PATHSEP + tokenDir + OS_PATHSEP + "token.lock", true); + + if (!tokenObject.valid) + { + std::string tokenPath = basePath + OS_PATHSEP + tokenDir + OS_PATHSEP + "token.[object|lock]"; + ERROR_MSG("Failed to create the token object: %s", tokenPath.c_str()); + + baseDir.rmdir(tokenDir); + + return NULL; + } + + // Set the initial attributes + CK_ULONG flags = + CKF_RNG | + CKF_LOGIN_REQUIRED | // FIXME: check + CKF_RESTORE_KEY_NOT_NEEDED | + CKF_TOKEN_INITIALIZED | + CKF_SO_PIN_LOCKED | + CKF_SO_PIN_TO_BE_CHANGED; + + OSAttribute tokenLabel(label); + OSAttribute tokenSerial(serial); + OSAttribute tokenFlags(flags); + + if (!tokenObject.setAttribute(CKA_OS_TOKENLABEL, tokenLabel) || + !tokenObject.setAttribute(CKA_OS_TOKENSERIAL, tokenSerial) || + !tokenObject.setAttribute(CKA_OS_TOKENFLAGS, tokenFlags)) + { + ERROR_MSG("Failed to set the token attributes"); + + baseDir.remove(tokenDir + OS_PATHSEP + "token.object"); + baseDir.remove(tokenDir + OS_PATHSEP + "token.lock"); + baseDir.rmdir(tokenDir); + + return NULL; + } + + DEBUG_MSG("Created new token %s", tokenDir.c_str()); + + return new OSToken(basePath + OS_PATHSEP + tokenDir); +} + +// Access an existing token +/*static*/ OSToken *OSToken::accessToken(const std::string &basePath, const std::string &tokenDir) +{ + return new OSToken(basePath + OS_PATHSEP + tokenDir); +} + +// Destructor +OSToken::~OSToken() +{ + // Clean up + std::set<OSObject*> cleanUp = allObjects; + allObjects.clear(); + + for (std::set<OSObject*>::iterator i = cleanUp.begin(); i != cleanUp.end(); i++) + { + delete *i; + } + + delete tokenDir; + if (gen != NULL) delete gen; + MutexFactory::i()->recycleMutex(tokenMutex); + delete tokenObject; +} + +// Set the SO PIN +bool OSToken::setSOPIN(const ByteString& soPINBlob) +{ + if (!valid) return false; + + OSAttribute soPIN(soPINBlob); + + CK_ULONG flags; + + if (tokenObject->setAttribute(CKA_OS_SOPIN, soPIN) && + getTokenFlags(flags)) + { + flags &= ~CKF_SO_PIN_COUNT_LOW; + flags &= ~CKF_SO_PIN_FINAL_TRY; + flags &= ~CKF_SO_PIN_LOCKED; + flags &= ~CKF_SO_PIN_TO_BE_CHANGED; + + return setTokenFlags(flags); + } + + return false; +} + +// Get the SO PIN +bool OSToken::getSOPIN(ByteString& soPINBlob) +{ + if (!valid || !tokenObject->isValid()) + { + return false; + } + + if (tokenObject->attributeExists(CKA_OS_SOPIN)) + { + soPINBlob = tokenObject->getAttribute(CKA_OS_SOPIN).getByteStringValue(); + + return true; + } + else + { + return false; + } +} + +// Set the user PIN +bool OSToken::setUserPIN(ByteString userPINBlob) +{ + if (!valid) return false; + + OSAttribute userPIN(userPINBlob); + + CK_ULONG flags; + + if (tokenObject->setAttribute(CKA_OS_USERPIN, userPIN) && + getTokenFlags(flags)) + { + flags |= CKF_USER_PIN_INITIALIZED; + flags &= ~CKF_USER_PIN_COUNT_LOW; + flags &= ~CKF_USER_PIN_FINAL_TRY; + flags &= ~CKF_USER_PIN_LOCKED; + flags &= ~CKF_USER_PIN_TO_BE_CHANGED; + + return setTokenFlags(flags); + } + + return false; +} + +// Get the user PIN +bool OSToken::getUserPIN(ByteString& userPINBlob) +{ + if (!valid || !tokenObject->isValid()) + { + return false; + } + + if (tokenObject->attributeExists(CKA_OS_USERPIN)) + { + userPINBlob = tokenObject->getAttribute(CKA_OS_USERPIN).getByteStringValue(); + + return true; + } + else + { + return false; + } +} + +// Retrieve the token label +bool OSToken::getTokenLabel(ByteString& label) +{ + if (!valid || !tokenObject->isValid()) + { + return false; + } + + if (tokenObject->attributeExists(CKA_OS_TOKENLABEL)) + { + label = tokenObject->getAttribute(CKA_OS_TOKENLABEL).getByteStringValue(); + + return true; + } + else + { + return false; + } +} + +// Retrieve the token serial +bool OSToken::getTokenSerial(ByteString& serial) +{ + if (!valid || !tokenObject->isValid()) + { + return false; + } + + if (tokenObject->attributeExists(CKA_OS_TOKENSERIAL)) + { + serial = tokenObject->getAttribute(CKA_OS_TOKENSERIAL).getByteStringValue(); + + return true; + } + else + { + return false; + } +} + +// Get the token flags +bool OSToken::getTokenFlags(CK_ULONG& flags) +{ + if (!valid || !tokenObject->isValid()) + { + return false; + } + + if (tokenObject->attributeExists(CKA_OS_TOKENFLAGS)) + { + flags = tokenObject->getAttribute(CKA_OS_TOKENFLAGS).getUnsignedLongValue(); + + // Check if the user PIN is initialised + if (tokenObject->attributeExists(CKA_OS_USERPIN)) + { + flags |= CKF_USER_PIN_INITIALIZED; + } + + return true; + } + else + { + return false; + } +} + +// Set the token flags +bool OSToken::setTokenFlags(const CK_ULONG flags) +{ + if (!valid) return false; + + OSAttribute tokenFlags(flags); + + return tokenObject->setAttribute(CKA_OS_TOKENFLAGS, tokenFlags); +} + +// Retrieve objects +std::set<OSObject*> OSToken::getObjects() +{ + index(); + + // Make sure that no other thread is in the process of changing + // the object list when we return it + MutexLocker lock(tokenMutex); + + return objects; +} + +void OSToken::getObjects(std::set<OSObject*> &inObjects) +{ + index(); + + // Make sure that no other thread is in the process of changing + // the object list when we return it + MutexLocker lock(tokenMutex); + + inObjects.insert(objects.begin(),objects.end()); +} + +// Create a new object +OSObject* OSToken::createObject() +{ + if (!valid) return NULL; + + // Generate a name for the object + std::string objectUUID = UUID::newUUID(); + std::string objectPath = tokenPath + OS_PATHSEP + objectUUID + ".object"; + std::string lockPath = tokenPath + OS_PATHSEP + objectUUID + ".lock"; + + // Create the new object file + ObjectFile* newObject = new ObjectFile(this, objectPath, lockPath, true); + + if (!newObject->valid) + { + ERROR_MSG("Failed to create new object %s", objectPath.c_str()); + + delete newObject; + + return NULL; + } + + // Now add it to the set of objects + MutexLocker lock(tokenMutex); + + objects.insert(newObject); + allObjects.insert(newObject); + currentFiles.insert(newObject->getFilename()); + + DEBUG_MSG("(0x%08X) Created new object %s (0x%08X)", this, objectPath.c_str(), newObject); + + gen->update(); + + gen->commit(); + + return newObject; +} + +// Delete an object +bool OSToken::deleteObject(OSObject* object) +{ + if (!valid) return false; + + if (objects.find(object) == objects.end()) + { + ERROR_MSG("Cannot delete non-existent object 0x%08X", object); + + return false; + } + + MutexLocker lock(tokenMutex); + + ObjectFile* fileObject = dynamic_cast<ObjectFile*>(object); + if (fileObject == NULL) + { + ERROR_MSG("Object type not compatible with this token class 0x%08X", object); + + return false; + } + + // Invalidate the object instance + fileObject->invalidate(); + + // Retrieve the filename of the object + std::string objectFilename = fileObject->getFilename(); + + // Attempt to delete the file + if (!tokenDir->remove(objectFilename)) + { + ERROR_MSG("Failed to delete object file %s", objectFilename.c_str()); + + return false; + } + + // Retrieve the filename of the lock + std::string lockFilename = fileObject->getLockname(); + + // Attempt to delete the lock + if (!tokenDir->remove(lockFilename)) + { + ERROR_MSG("Failed to delete lock file %s", lockFilename.c_str()); + + return false; + } + + objects.erase(object); + + DEBUG_MSG("Deleted object %s", objectFilename.c_str()); + + gen->update(); + + gen->commit(); + + return true; +} + +// Checks if the token is consistent +bool OSToken::isValid() +{ + return valid; +} + +// Invalidate the token (for instance if it is deleted) +void OSToken::invalidate() +{ + valid = false; +} + +// Delete the token +bool OSToken::clearToken() +{ + MutexLocker lock(tokenMutex); + + // Invalidate the token + invalidate(); + + // First, clear out all objects + objects.clear(); + + // Now, delete all files in the token directory + if (!tokenDir->refresh()) + { + return false; + } + + std::vector<std::string> tokenFiles = tokenDir->getFiles(); + + for (std::vector<std::string>::iterator i = tokenFiles.begin(); i != tokenFiles.end(); i++) + { + if (!tokenDir->remove(*i)) + { + ERROR_MSG("Failed to remove %s from token directory %s", i->c_str(), tokenPath.c_str()); + + return false; + } + } + + // Now remove the token directory + if (!tokenDir->rmdir("")) + { + ERROR_MSG("Failed to remove the token directory %s", tokenPath.c_str()); + + return false; + } + + DEBUG_MSG("Token instance %s was succesfully cleared", tokenPath.c_str()); + + return true; +} + +// Reset the token +bool OSToken::resetToken(const ByteString& label) +{ + CK_ULONG flags; + + if (!getTokenFlags(flags)) + { + ERROR_MSG("Failed to get the token attributes"); + + return false; + } + + // Clean up + std::set<OSObject*> cleanUp = getObjects(); + + MutexLocker lock(tokenMutex); + + for (std::set<OSObject*>::iterator i = cleanUp.begin(); i != cleanUp.end(); i++) + { + ObjectFile* fileObject = dynamic_cast<ObjectFile*>(*i); + if (fileObject == NULL) + { + ERROR_MSG("Object type not compatible with this token class 0x%08X", *i); + + return false; + } + + // Invalidate the object instance + fileObject->invalidate(); + + // Retrieve the filename of the object + std::string objectFilename = fileObject->getFilename(); + + // Attempt to delete the file + if (!tokenDir->remove(objectFilename)) + { + ERROR_MSG("Failed to delete object file %s", objectFilename.c_str()); + + return false; + } + + // Retrieve the filename of the lock + std::string lockFilename = fileObject->getLockname(); + + // Attempt to delete the lock + if (!tokenDir->remove(lockFilename)) + { + ERROR_MSG("Failed to delete lock file %s", lockFilename.c_str()); + + return false; + } + + objects.erase(*i); + + DEBUG_MSG("Deleted object %s", objectFilename.c_str()); + } + + // The user PIN has been removed + flags &= ~CKF_USER_PIN_INITIALIZED; + flags &= ~CKF_USER_PIN_COUNT_LOW; + flags &= ~CKF_USER_PIN_FINAL_TRY; + flags &= ~CKF_USER_PIN_LOCKED; + flags &= ~CKF_USER_PIN_TO_BE_CHANGED; + + // Set new token attributes + OSAttribute tokenLabel(label); + OSAttribute tokenFlags(flags); + + if (!tokenObject->setAttribute(CKA_OS_TOKENLABEL, tokenLabel) || + !tokenObject->setAttribute(CKA_OS_TOKENFLAGS, tokenFlags)) + { + ERROR_MSG("Failed to set the token attributes"); + + return false; + } + + if (tokenObject->attributeExists(CKA_OS_USERPIN) && + !tokenObject->deleteAttribute(CKA_OS_USERPIN)) + { + ERROR_MSG("Failed to remove USERPIN"); + + return false; + } + + DEBUG_MSG("Token instance %s was succesfully reset", tokenPath.c_str()); + + gen->update(); + gen->commit(); + + return true; +} + +// Index the token +bool OSToken::index(bool isFirstTime /* = false */) +{ + // No access to object mutable fields before + MutexLocker lock(tokenMutex); + + // Check if re-indexing is required + if (!isFirstTime && (!valid || !gen->wasUpdated())) + { + DEBUG_MSG("No re-indexing is required"); + + return true; + } + + // Check the integrity + if (!tokenDir->refresh() || !tokenObject->valid) + { + ERROR_MSG("Token integrity check failed"); + + valid = false; + + return false; + } + + DEBUG_MSG("Token %s has changed", tokenPath.c_str()); + + // Retrieve the directory listing + std::vector<std::string> tokenFiles = tokenDir->getFiles(); + + // Filter out the objects + std::set<std::string> newSet; + + for (std::vector<std::string>::iterator i = tokenFiles.begin(); i != tokenFiles.end(); i++) + { + if ((i->size() > 7) && + (!(i->substr(i->size() - 7).compare(".object"))) && + (i->compare("token.object"))) + { + newSet.insert(*i); + } + else + { + DEBUG_MSG("Ignored file %s", i->c_str()); + } + } + + // Compute the changes compared to the last list of files + std::set<std::string> addedFiles; + std::set<std::string> removedFiles; + + if (!isFirstTime) + { + // First compute which files were added + for (std::set<std::string>::iterator i = newSet.begin(); i != newSet.end(); i++) + { + if (currentFiles.find(*i) == currentFiles.end()) + { + addedFiles.insert(*i); + } + } + + // Now compute which files were removed + for (std::set<std::string>::iterator i = currentFiles.begin(); i != currentFiles.end(); i++) + { + if (newSet.find(*i) == newSet.end()) + { + removedFiles.insert(*i); + } + } + } + else + { + addedFiles = newSet; + } + + currentFiles = newSet; + + DEBUG_MSG("%d objects were added and %d objects were removed", addedFiles.size(), removedFiles.size()); + DEBUG_MSG("Current directory set contains %d objects", currentFiles.size()); + + // Now update the set of objects + + // Add new objects + for (std::set<std::string>::iterator i = addedFiles.begin(); i != addedFiles.end(); i++) + { + if ((i->find_last_of('.') == std::string::npos) || + (i->substr(i->find_last_of('.')) != ".object")) + { + continue; + } + + std::string lockName(*i); + lockName.replace(lockName.find_last_of('.'), std::string::npos, ".lock"); + + // Create a new token object for the added file + ObjectFile* newObject = new ObjectFile(this, tokenPath + OS_PATHSEP + *i, tokenPath + OS_PATHSEP + lockName); + + // Add the object, even invalid ones. + // This is so the we can read the attributes once + // the other process has finished writing to disc. + DEBUG_MSG("(0x%08X) New object %s (0x%08X) added", this, newObject->getFilename().c_str(), newObject); + objects.insert(newObject); + allObjects.insert(newObject); + } + + // Remove deleted objects + std::set<OSObject*> newObjects; + + for (std::set<OSObject*>::iterator i = objects.begin(); i != objects.end(); i++) + { + ObjectFile* fileObject = dynamic_cast<ObjectFile*>((*i)); + if (fileObject == NULL) + { + ERROR_MSG("Object type not compatible with this token class 0x%08X", (*i)); + + return false; + } + + DEBUG_MSG("Processing %s (0x%08X)", fileObject->getFilename().c_str(), *i); + + if (removedFiles.find(fileObject->getFilename()) == removedFiles.end()) + { + DEBUG_MSG("Adding object %s", fileObject->getFilename().c_str()); + // This object gets to stay in the set + newObjects.insert(*i); + } + else + { + fileObject->invalidate(); + } + } + + // Set the new objects + objects = newObjects; + + DEBUG_MSG("The token now contains %d objects", objects.size()); + + return true; +} + |