aboutsummaryrefslogtreecommitdiffstats
path: root/SoftHSMv2/src/lib/object_store/DBToken.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SoftHSMv2/src/lib/object_store/DBToken.cpp')
-rw-r--r--SoftHSMv2/src/lib/object_store/DBToken.cpp874
1 files changed, 874 insertions, 0 deletions
diff --git a/SoftHSMv2/src/lib/object_store/DBToken.cpp b/SoftHSMv2/src/lib/object_store/DBToken.cpp
new file mode 100644
index 0000000..e734372
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/DBToken.cpp
@@ -0,0 +1,874 @@
+/*
+ * Copyright (c) 2013 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.
+ */
+
+/*****************************************************************************
+ DBToken.cpp
+
+ The token class; a token is stored in a directory containing a single
+ database file.
+ Each object is stored in multiple tables with every attribute base type
+ stored in a different table.
+ *****************************************************************************/
+
+#include "config.h"
+#include "log.h"
+#include "OSAttributes.h"
+#include "OSAttribute.h"
+#include "OSPathSep.h"
+
+#include "cryptoki.h"
+#include "DBToken.h"
+#include "DBObject.h"
+#include "DB.h"
+
+#include "Directory.h"
+
+#include <vector>
+#include <string>
+#include <set>
+#include <map>
+#include <list>
+#include <cstdio>
+#include <sys/stat.h>
+#include <errno.h>
+
+const char * const DBTOKEN_FILE = "sqlite3.db";
+const long long DBTOKEN_OBJECT_TOKENINFO = 1;
+
+// Constructor for creating a new token.
+DBToken::DBToken(const std::string &baseDir, const std::string &tokenName, const ByteString &label, const ByteString &serial)
+ : _connection(NULL), _tokenMutex(NULL)
+{
+ std::string tokenDir = baseDir + OS_PATHSEP + tokenName;
+ std::string tokenPath = tokenDir + OS_PATHSEP + DBTOKEN_FILE;
+
+ // Refuse to open an already existing database.
+ FILE *f = fopen(tokenPath.c_str(),"r");
+ if (f)
+ {
+ fclose(f);
+ ERROR_MSG("Refusing to overwrite and existing database at \"%s\"", tokenPath.c_str());
+ return;
+ }
+
+ // First create the directory for the token, we expect basePath to already exist
+ if (mkdir(tokenDir.c_str(), S_IFDIR | S_IRWXU))
+ {
+ // Allow the directory to exists already.
+ if (errno != EEXIST)
+ {
+ ERROR_MSG("Unable to create directory \"%s\"", tokenDir.c_str());
+ return;
+ }
+ }
+
+ // Create
+ _connection = DB::Connection::Create(tokenDir, DBTOKEN_FILE);
+ if (_connection == NULL)
+ {
+ ERROR_MSG("Failed to create a database connection for \"%s\"", tokenPath.c_str());
+ return;
+ }
+
+ if (!_connection->connect())
+ {
+ delete _connection;
+ _connection = NULL;
+
+ ERROR_MSG("Failed to connect to the database at \"%s\"", tokenPath.c_str());
+
+ // Now remove the token directory
+ if (remove(tokenDir.c_str()))
+ {
+ ERROR_MSG("Failed to remove the token directory \"%s\"", tokenDir.c_str());
+ }
+
+ return;
+ }
+
+ // Create a DBObject for the established connection to the database.
+ DBObject tokenObject(_connection);
+
+ // First create the tables that support storage of object attributes and then insert the object containing
+ // the token info into the database.
+ if (!tokenObject.createTables() || !tokenObject.insert() || tokenObject.objectId()!=DBTOKEN_OBJECT_TOKENINFO)
+ {
+ tokenObject.dropConnection();
+
+ _connection->close();
+ delete _connection;
+ _connection = NULL;
+
+ ERROR_MSG("Failed to create tables for storing objects in database at \"%s\"", tokenPath.c_str());
+ return;
+ }
+
+ // 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))
+ {
+ _connection->close();
+ delete _connection;
+ _connection = NULL;
+
+ // Now remove the token file
+ if (remove(tokenPath.c_str()))
+ {
+ ERROR_MSG("Failed to remove the token file at \"%s\"", tokenPath.c_str());
+ }
+
+ // Now remove the token directory
+ if (remove(tokenDir.c_str()))
+ {
+ ERROR_MSG("Failed to remove the token directory at \"%s\"", tokenDir.c_str());
+ }
+ return;
+ }
+
+ _tokenMutex = MutexFactory::i()->getMutex();
+ // Success!
+}
+
+// Constructor for accessing an existing token.
+DBToken::DBToken(const std::string &baseDir, const std::string &tokenName)
+ : _connection(NULL), _tokenMutex(NULL)
+{
+ std::string tokenDir = baseDir + OS_PATHSEP + tokenName;
+ std::string tokenPath = tokenDir + OS_PATHSEP + DBTOKEN_FILE;
+
+ // Refuse to open an already existing database.
+ FILE *f = fopen(tokenPath.c_str(),"r");
+ if (f == NULL)
+ {
+ ERROR_MSG("Refusing to open a non-existant database at \"%s\"", tokenPath.c_str());
+ return;
+ }
+ fclose(f);
+
+ // Create a database connection.
+ _connection = DB::Connection::Create(tokenDir, DBTOKEN_FILE);
+ if (_connection == NULL)
+ {
+ ERROR_MSG("Failed to create a database connection for \"%s\"", tokenPath.c_str());
+ return;
+ }
+
+ if (!_connection->connect())
+ {
+ delete _connection;
+ _connection = NULL;
+
+ ERROR_MSG("Failed to connect to the database at \"%s\"", tokenPath.c_str());
+
+ return;
+ }
+
+ // Find the DBObject for the established connection to the database.
+ DBObject tokenObject(_connection);
+
+ // First find the token obect that indicates the token is properly initialized.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ tokenObject.dropConnection();
+
+ _connection->close();
+ delete _connection;
+ _connection = NULL;
+
+ ERROR_MSG("Failed to open token object in the token database at \"%s\"", tokenPath.c_str());
+ return;
+ }
+
+ _tokenMutex = MutexFactory::i()->getMutex();
+
+ // Success!
+}
+
+DBToken *DBToken::createToken(const std::string basePath, const std::string tokenDir, const ByteString &label, const ByteString &serial)
+{
+ Directory baseDir(basePath);
+
+ if (!baseDir.isValid())
+ {
+ return NULL;
+ }
+
+ // Create the token directory
+ if (!baseDir.mkdir(tokenDir))
+ {
+ return NULL;
+ }
+
+ DBToken *token = new DBToken(basePath, tokenDir, label, serial);
+ if (!token->isValid())
+ {
+ baseDir.rmdir(tokenDir);
+
+ delete token;
+ return NULL;
+ }
+
+ DEBUG_MSG("Created new token %s", tokenDir.c_str());
+
+ return token;
+}
+
+DBToken *DBToken::accessToken(const std::string &basePath, const std::string &tokenDir)
+{
+ return new DBToken(basePath, tokenDir);
+}
+
+// Destructor
+DBToken::~DBToken()
+{
+ if (_tokenMutex)
+ {
+ MutexFactory::i()->recycleMutex(_tokenMutex);
+ _tokenMutex = NULL;
+ }
+
+ std::map<long long, OSObject*> cleanUp = _allObjects;
+ _allObjects.clear();
+ for (std::map<long long, OSObject*>::iterator i = cleanUp.begin(); i != cleanUp.end(); ++i)
+ {
+ delete i->second;
+ }
+
+ if (_connection)
+ {
+ delete _connection;
+ _connection = NULL;
+ }
+}
+
+// Set the SO PIN
+bool DBToken::setSOPIN(const ByteString& soPINBlob)
+{
+ if (_connection == NULL) return false;
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadWrite))
+ {
+ ERROR_MSG("Unable to start a transaction for updating the SOPIN and TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ OSAttribute soPIN(soPINBlob);
+ if (!tokenObject.setAttribute(CKA_OS_SOPIN, soPIN))
+ {
+ ERROR_MSG("Error while setting SOPIN in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.attributeExists(CKA_OS_TOKENFLAGS))
+ {
+ ERROR_MSG("Error while getting TOKENFLAGS from token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ // Retrieve flags from the database and reset flags related to tries and expiration of the SOPIN.
+ CK_ULONG flags = tokenObject.getAttribute(CKA_OS_TOKENFLAGS).getUnsignedLongValue()
+ & ~(CKF_SO_PIN_COUNT_LOW | CKF_SO_PIN_FINAL_TRY | CKF_SO_PIN_LOCKED | CKF_SO_PIN_TO_BE_CHANGED);
+
+ OSAttribute changedTokenFlags(flags);
+ if (!tokenObject.setAttribute(CKA_OS_TOKENFLAGS, changedTokenFlags))
+ {
+ ERROR_MSG("Error while setting TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.commitTransaction())
+ {
+ ERROR_MSG("Error while committing SOPIN and TOKENFLAGS changes to token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ return true;
+}
+
+// Get the SO PIN
+bool DBToken::getSOPIN(ByteString& soPINBlob)
+{
+ if (_connection == NULL) return false;
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadOnly))
+ {
+ ERROR_MSG("Unable to start a transaction for getting the SOPIN from token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.attributeExists(CKA_OS_SOPIN))
+ {
+ ERROR_MSG("Error while getting SOPIN from token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ tokenObject.commitTransaction();
+ soPINBlob = tokenObject.getAttribute(CKA_OS_SOPIN).getByteStringValue();
+ return true;
+}
+
+// Set the user PIN
+bool DBToken::setUserPIN(ByteString userPINBlob)
+{
+ if (_connection == NULL) return false;
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadWrite))
+ {
+ ERROR_MSG("Unable to start a transaction for updating the USERPIN and TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ OSAttribute userPIN(userPINBlob);
+ if (!tokenObject.setAttribute(CKA_OS_USERPIN, userPIN))
+ {
+ ERROR_MSG("Error while setting USERPIN in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.attributeExists(CKA_OS_TOKENFLAGS))
+ {
+ ERROR_MSG("Error while getting TOKENFLAGS from token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ // Retrieve flags from the database and reset flags related to tries and expiration of the SOPIN.
+ CK_ULONG flags = tokenObject.getAttribute(CKA_OS_TOKENFLAGS).getUnsignedLongValue()
+ | (CKF_USER_PIN_INITIALIZED & ~(CKF_USER_PIN_COUNT_LOW | CKF_USER_PIN_FINAL_TRY | CKF_USER_PIN_LOCKED | CKF_USER_PIN_TO_BE_CHANGED));
+
+ OSAttribute changedTokenFlags(flags);
+ if (!tokenObject.setAttribute(CKA_OS_TOKENFLAGS, changedTokenFlags))
+ {
+ ERROR_MSG("Error while setting TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.commitTransaction())
+ {
+ ERROR_MSG("Error while committing USERPIN and TOKENFLAGS changes to token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ return true;
+}
+
+// Get the user PIN
+bool DBToken::getUserPIN(ByteString& userPINBlob)
+{
+ if (_connection == NULL) return false;
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadOnly))
+ {
+ ERROR_MSG("Unable to start a transaction for getting the USERPIN from token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.attributeExists(CKA_OS_USERPIN))
+ {
+ ERROR_MSG("Error while getting USERPIN from token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ tokenObject.commitTransaction();
+ userPINBlob = tokenObject.getAttribute(CKA_OS_USERPIN).getByteStringValue();
+ return true;
+}
+
+// Retrieve the token label
+bool DBToken::getTokenLabel(ByteString& label)
+{
+ if (_connection == NULL) return false;
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadOnly))
+ {
+ ERROR_MSG("Unable to start a transaction for getting the TOKENLABEL from token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.attributeExists(CKA_OS_TOKENLABEL))
+ {
+ ERROR_MSG("Error while getting TOKENLABEL from token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ tokenObject.commitTransaction();
+ label = tokenObject.getAttribute(CKA_OS_TOKENLABEL).getByteStringValue();
+ return true;
+}
+
+// Retrieve the token serial
+bool DBToken::getTokenSerial(ByteString& serial)
+{
+ if (_connection == NULL) return false;
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadOnly))
+ {
+ ERROR_MSG("Unable to start a transaction for getting the TOKENSERIAL from token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.attributeExists(CKA_OS_TOKENSERIAL))
+ {
+ ERROR_MSG("Error while getting TOKENSERIAL from token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ tokenObject.commitTransaction();
+ serial = tokenObject.getAttribute(CKA_OS_TOKENSERIAL).getByteStringValue();
+ return true;
+}
+
+// Get the token flags
+bool DBToken::getTokenFlags(CK_ULONG& flags)
+{
+ if (_connection == NULL) return false;
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadOnly))
+ {
+ ERROR_MSG("Unable to start a transaction for updating the SOPIN and TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.attributeExists(CKA_OS_TOKENFLAGS))
+ {
+ ERROR_MSG("Error while getting TOKENFLAGS from token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ tokenObject.commitTransaction();
+ flags = tokenObject.getAttribute(CKA_OS_TOKENFLAGS).getUnsignedLongValue();
+ return true;
+}
+
+// Set the token flags
+bool DBToken::setTokenFlags(const CK_ULONG flags)
+{
+ if (_connection == NULL) return false;
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadWrite))
+ {
+ ERROR_MSG("Unable to start a transaction for setting the TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ OSAttribute tokenFlags(flags);
+ if (!tokenObject.setAttribute(CKA_OS_TOKENFLAGS, tokenFlags))
+ {
+ ERROR_MSG("Error while setting TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.commitTransaction())
+ {
+ ERROR_MSG("Error while committing TOKENFLAGS changes to token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ return true;
+}
+
+// Retrieve objects
+std::set<OSObject *> DBToken::getObjects()
+{
+ std::set<OSObject*> objects;
+ getObjects(objects);
+ return objects;
+}
+
+void DBToken::getObjects(std::set<OSObject*> &objects)
+{
+ if (_connection == NULL) return;
+
+ if (!_connection->beginTransactionRO()) return;
+
+ DB::Statement statement = _connection->prepare("select id from object limit -1 offset 1");
+
+ DB::Result result = _connection->perform(statement);
+
+ if (result.isValid())
+ {
+ do {
+ long long objectId = result.getLongLong(1);
+ {
+ MutexLocker lock(_tokenMutex);
+ std::map<long long, OSObject*>::iterator it = _allObjects.find(objectId);
+ if (it == _allObjects.end())
+ {
+ DBObject *object = new DBObject(_connection, this, objectId);
+ _allObjects[objectId] = object;
+ objects.insert(object);
+ }
+ else
+ {
+ objects.insert(it->second);
+ }
+ }
+ } while (result.nextRow());
+ }
+
+ _connection->endTransactionRO();
+}
+
+// Create a new object
+OSObject *DBToken::createObject()
+{
+ if (_connection == NULL) return NULL;
+
+ DBObject *newObject = new DBObject(_connection, this);
+ if (newObject == NULL)
+ {
+ ERROR_MSG("Failed to create an object: out of memory");
+ return NULL;
+ }
+
+ if (!newObject->startTransaction(DBObject::ReadWrite))
+ {
+ delete newObject;
+ ERROR_MSG("Unable to start a transaction in token database at \"%s\"", _connection->dbpath().c_str());
+ return NULL;
+ }
+
+ if (!newObject->insert())
+ {
+ newObject->abortTransaction();
+ delete newObject;
+ ERROR_MSG("Unable to insert an object into token database at \"%s\"", _connection->dbpath().c_str());
+ return NULL;
+ }
+
+ if (!newObject->isValid())
+ {
+ newObject->abortTransaction();
+ delete newObject;
+ ERROR_MSG("Object that was inserted in not valid");
+ return NULL;
+ }
+
+ if (!newObject->commitTransaction())
+ {
+ newObject->abortTransaction();
+ delete newObject;
+ ERROR_MSG("Unable to commit a created object to token database at \"%s\"", _connection->dbpath().c_str());
+ return NULL;
+ }
+
+ // Now add the new object to the list of existing objects.
+ {
+ MutexLocker lock(_tokenMutex);
+ _allObjects[newObject->objectId()] = newObject;
+ }
+
+ return newObject;
+}
+
+bool DBToken::deleteObject(OSObject *object)
+{
+ if (_connection == NULL) return false;
+
+ if (object == NULL)
+ {
+ ERROR_MSG("Object passed in as a parameter is NULL");
+ return false;
+ }
+
+ if (!object->startTransaction(DBObject::ReadWrite))
+ {
+ ERROR_MSG("Unable to start a transaction for deleting an object in token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ if (!static_cast<DBObject *>(object)->remove())
+ {
+ ERROR_MSG("Error while deleting an existing object from the token database at \"%s\"", _connection->dbpath().c_str());
+ object->abortTransaction();
+ return false;
+ }
+
+ if (!object->commitTransaction())
+ {
+ ERROR_MSG("Error while committing the deletion of an existing object in token database at \"%s\"", _connection->dbpath().c_str());
+ object->abortTransaction();
+ return false;
+ }
+
+ return true;
+}
+
+// Checks if the token is consistent
+bool DBToken::isValid()
+{
+ return _connection != NULL && _connection->tableExists("object");
+}
+
+// Invalidate the token (for instance if it is deleted)
+void DBToken::invalidate()
+{
+}
+
+// Delete the token.
+bool DBToken::clearToken()
+{
+ if (_connection == NULL) return false;
+
+ std::string tokenDir = _connection->dbdir();
+ std::string tokenPath = _connection->dbpath();
+
+ if (!DBObject(_connection).dropTables())
+ {
+ ERROR_MSG("Failed to drop all tables in the token database at \"%s\"", tokenPath.c_str());
+ return false;
+ }
+
+ _connection->close();
+ delete _connection;
+ _connection = NULL;
+
+ // Remove all files from the token directory, even ones not placed there by us.
+ Directory dir(tokenDir);
+ std::vector<std::string> tokenFiles = dir.getFiles();
+
+ for (std::vector<std::string>::iterator i = tokenFiles.begin(); i != tokenFiles.end(); i++)
+ {
+ if (!dir.remove(*i))
+ {
+ ERROR_MSG("Failed to remove \"%s\" from token directory \"%s\"", i->c_str(), tokenDir.c_str());
+
+ return false;
+ }
+ }
+
+ // Now remove the token directory
+ if (!dir.rmdir(""))
+ {
+ ERROR_MSG("Failed to remove the token directory \"%s\"", tokenDir.c_str());
+
+ return false;
+ }
+
+ DEBUG_MSG("Token instance %s was succesfully cleared", tokenDir.c_str());
+
+ return true;
+}
+
+// Reset the token
+bool DBToken::resetToken(const ByteString& label)
+{
+ if (_connection == NULL) return false;
+
+ std::string tokenDir = _connection->dbdir();
+
+ // Clean up
+ std::set<OSObject*> cleanUp = getObjects();
+
+ for (std::set<OSObject*>::iterator i = cleanUp.begin(); i != cleanUp.end(); i++)
+ {
+ if (!deleteObject(*i))
+ {
+ ERROR_MSG("Unable to delete all objects in token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+ }
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadWrite))
+ {
+ ERROR_MSG("Unable to start a transaction for setting the TOKENLABEL in token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (tokenObject.attributeExists(CKA_OS_USERPIN))
+ {
+ if (!tokenObject.deleteAttribute(CKA_OS_USERPIN))
+ {
+ ERROR_MSG("Error while deleting USERPIN in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+ }
+
+ if (!tokenObject.attributeExists(CKA_OS_TOKENFLAGS))
+ {
+ ERROR_MSG("Error while getting TOKENFLAGS from token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ // Retrieve flags from the database and reset flags related to tries and expiration of the SOPIN.
+ CK_ULONG flags = tokenObject.getAttribute(CKA_OS_TOKENFLAGS).getUnsignedLongValue()
+ & ~(CKF_USER_PIN_INITIALIZED | CKF_USER_PIN_COUNT_LOW | CKF_USER_PIN_FINAL_TRY | CKF_USER_PIN_LOCKED | CKF_USER_PIN_TO_BE_CHANGED);
+
+ OSAttribute changedTokenFlags(flags);
+ if (!tokenObject.setAttribute(CKA_OS_TOKENFLAGS, changedTokenFlags))
+ {
+ ERROR_MSG("Error while setting TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ OSAttribute tokenLabel(label);
+ if (!tokenObject.setAttribute(CKA_OS_TOKENLABEL, tokenLabel))
+ {
+ ERROR_MSG("Error while setting TOKENLABEL in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.commitTransaction())
+ {
+ ERROR_MSG("Error while committing TOKENLABEL changes to token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ DEBUG_MSG("Token instance %s was succesfully reset", tokenDir.c_str());
+
+ return true;
+}