aboutsummaryrefslogtreecommitdiffstats
path: root/SoftHSMv2/src/lib/object_store/OSToken.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SoftHSMv2/src/lib/object_store/OSToken.cpp')
-rw-r--r--SoftHSMv2/src/lib/object_store/OSToken.cpp722
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;
+}
+