Skip to content

Commit

Permalink
Bug 1379273 - make softoken resettable via PK11_ResetToken r=franzisk…
Browse files Browse the repository at this point in the history
…us,ttaubert

Summary:
Two issues prevented PK11_ResetToken from working properly:
1. The backing DB tables would be dropped and never recreated, preventing
future operations from working.
2. The needLogin property of the SFTKSlot would not be updated properly,
preventing PK11_InitPin (and thus other operations) from succeeding.

Reviewers: ttaubert, franziskus

Reviewed By: ttaubert, franziskus

Differential Revision: https://nss-review.dev.mozaws.net/D382

--HG--
rename : gtests/util_gtest/Makefile => gtests/softoken_gtest/Makefile
rename : gtests/util_gtest/manifest.mn => gtests/softoken_gtest/manifest.mn
rename : gtests/common/gtests.cc => gtests/softoken_gtest/softoken_gtest.cc
rename : gtests/util_gtest/util_gtest.gyp => gtests/softoken_gtest/softoken_gtest.gyp
extra : rebase_source : 93a0da1ae8544462d6836b4da3a7490ed4c8c36d
extra : histedit_source : 32ada4bb767cb840df79dfef051b6151b5d5e10d
  • Loading branch information
mozkeeler committed Aug 1, 2017
1 parent 18e5074 commit 2c66d71
Show file tree
Hide file tree
Showing 9 changed files with 275 additions and 22 deletions.
3 changes: 2 additions & 1 deletion gtests/manifest.mn
Expand Up @@ -23,8 +23,9 @@ NSS_SRCDIRS = \
certdb_gtest \
certhigh_gtest \
pk11_gtest \
softoken_gtest \
ssl_gtest \
nss_bogo_shim \
nss_bogo_shim \
$(NULL)
endif
endif
Expand Down
45 changes: 45 additions & 0 deletions gtests/softoken_gtest/Makefile
@@ -0,0 +1,45 @@
#! gmake
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

#######################################################################
# (1) Include initial platform-independent assignments (MANDATORY). #
#######################################################################

include manifest.mn

#######################################################################
# (2) Include "global" configuration information. (OPTIONAL) #
#######################################################################

include $(CORE_DEPTH)/coreconf/config.mk

#######################################################################
# (3) Include "component" configuration information. (OPTIONAL) #
#######################################################################


#######################################################################
# (4) Include "local" platform-dependent assignments (OPTIONAL). #
#######################################################################

include ../common/gtest.mk

CFLAGS += -I$(CORE_DEPTH)/lib/util

#######################################################################
# (5) Execute "global" rules. (OPTIONAL) #
#######################################################################

include $(CORE_DEPTH)/coreconf/rules.mk

#######################################################################
# (6) Execute "component" rules. (OPTIONAL) #
#######################################################################


#######################################################################
# (7) Execute "local" rules. (OPTIONAL). #
#######################################################################
25 changes: 25 additions & 0 deletions gtests/softoken_gtest/manifest.mn
@@ -0,0 +1,25 @@
# -*- makefile -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
CORE_DEPTH = ../..
DEPTH = ../..
MODULE = nss

CPPSRCS = \
softoken_gtest.cc \
$(NULL)

INCLUDES += \
-I$(CORE_DEPTH)/gtests/google_test/gtest/include \
-I$(CORE_DEPTH)/cpputil \
$(NULL)

REQUIRES = nspr gtest

PROGRAM = softoken_gtest

EXTRA_LIBS = \
$(DIST)/lib/$(LIB_PREFIX)gtest.$(LIB_SUFFIX) \
$(DIST)/lib/$(LIB_PREFIX)gtestutil.$(LIB_SUFFIX) \
$(NULL)
132 changes: 132 additions & 0 deletions gtests/softoken_gtest/softoken_gtest.cc
@@ -0,0 +1,132 @@
#include <cstdlib>

#include "nspr.h"
#include "nss.h"
#include "pk11pub.h"

#include "scoped_ptrs.h"

#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"

namespace nss_test {

// Given a prefix, attempts to create a unique directory that the user can do
// work in without impacting other tests. For example, if given the prefix
// "scratch", a directory like "scratch05c17b25" will be created in the current
// working directory (or the location specified by NSS_GTEST_WORKDIR, if
// defined).
// Upon destruction, the implementation will attempt to delete the directory.
// However, no attempt is made to first remove files in the directory - the
// user is responsible for this. If the directory is not empty, deleting it will
// fail.
// Statistically, it is technically possible to fail to create a unique
// directory name, but this is extremely unlikely given the expected workload of
// this implementation.
class ScopedUniqueDirectory {
public:
explicit ScopedUniqueDirectory(const std::string& prefix);

// NB: the directory must be empty upon destruction
~ScopedUniqueDirectory() {
assert(rmdir(mPath.c_str()) == 0);
}

const std::string& GetPath() { return mPath; }

private:
static const int RETRY_LIMIT = 5;
static void GenerateRandomName(/*in/out*/ std::string& prefix);
static bool TryMakingDirectory(/*in/out*/ std::string& prefix);

std::string mPath;
};

ScopedUniqueDirectory::ScopedUniqueDirectory(const std::string& prefix)
{
std::string path;
const char* workingDirectory = PR_GetEnvSecure("NSS_GTEST_WORKDIR");
if (workingDirectory) {
path.assign(workingDirectory);
}
path.append(prefix);
for (int i = 0; i < RETRY_LIMIT; i++) {
std::string pathCopy(path);
// TryMakingDirectory will modify its input. If it fails, we want to throw
// away the modified result.
if (TryMakingDirectory(pathCopy)) {
mPath.assign(pathCopy);
break;
}
}
assert(mPath.length() > 0);
}

void
ScopedUniqueDirectory::GenerateRandomName(std::string& prefix)
{
std::stringstream ss;
ss << prefix;
// RAND_MAX is at least 32767.
ss << std::setfill('0') << std::setw(4) << std::hex << rand() << rand();
// This will overwrite the value of prefix. This is a little inefficient, but
// at least it makes the code simple.
ss >> prefix;
}

bool
ScopedUniqueDirectory::TryMakingDirectory(std::string& prefix)
{
GenerateRandomName(prefix);
#if defined(_WIN32)
return _mkdir(prefix.c_str()) == 0;
#else
return mkdir(prefix.c_str(), 0777) == 0;
#endif
}

class SoftokenTest : public ::testing::Test {
protected:
SoftokenTest() : mNSSDBDir("SoftokenTest.d-") { }

virtual void SetUp() {
std::string nssInitArg("sql:");
nssInitArg.append(mNSSDBDir.GetPath());
ASSERT_EQ(SECSuccess, NSS_Initialize(nssInitArg.c_str(), "", "", SECMOD_DB,
NSS_INIT_NOROOTINIT));
}

virtual void TearDown() {
ASSERT_EQ(SECSuccess, NSS_Shutdown());
const std::string& nssDBDirPath = mNSSDBDir.GetPath();
ASSERT_EQ(0, unlink((nssDBDirPath + "/cert9.db").c_str()));
ASSERT_EQ(0, unlink((nssDBDirPath + "/key4.db").c_str()));
ASSERT_EQ(0, unlink((nssDBDirPath + "/pkcs11.txt").c_str()));
}

ScopedUniqueDirectory mNSSDBDir;
};

TEST_F(SoftokenTest, ResetSoftokenEmptyPassword) {
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
ASSERT_TRUE(slot);
EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, nullptr));
EXPECT_EQ(SECSuccess, PK11_ResetToken(slot.get(), nullptr));
EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, nullptr));
}

TEST_F(SoftokenTest, ResetSoftokenNonEmptyPassword) {
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
ASSERT_TRUE(slot);
EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, "password"));
EXPECT_EQ(SECSuccess, PK11_ResetToken(slot.get(), nullptr));
EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, "password2"));
}

} // namespace nss_test

int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);

return RUN_ALL_TESTS();
}
51 changes: 51 additions & 0 deletions gtests/softoken_gtest/softoken_gtest.gyp
@@ -0,0 +1,51 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
{
'includes': [
'../../coreconf/config.gypi',
'../common/gtest.gypi',
],
'targets': [
{
'target_name': 'softoken_gtest',
'type': 'executable',
'sources': [
'softoken_gtest.cc',
],
'dependencies': [
'<(DEPTH)/exports.gyp:nss_exports',
'<(DEPTH)/lib/util/util.gyp:nssutil3',
'<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
],
'conditions': [
[ 'test_build==1', {
'dependencies': [
'<(DEPTH)/lib/nss/nss.gyp:nss_static',
'<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static',
'<(DEPTH)/lib/cryptohi/cryptohi.gyp:cryptohi',
'<(DEPTH)/lib/certhigh/certhigh.gyp:certhi',
'<(DEPTH)/lib/certdb/certdb.gyp:certdb',
'<(DEPTH)/lib/base/base.gyp:nssb',
'<(DEPTH)/lib/dev/dev.gyp:nssdev',
'<(DEPTH)/lib/pki/pki.gyp:nsspki',
'<(DEPTH)/lib/ssl/ssl.gyp:ssl',
],
}, {
'dependencies': [
'<(DEPTH)/lib/nss/nss.gyp:nss3',
'<(DEPTH)/lib/ssl/ssl.gyp:ssl3',
],
}],
],
}
],
'target_defaults': {
'include_dirs': [
'../../lib/util'
]
},
'variables': {
'module': 'nss'
}
}
14 changes: 5 additions & 9 deletions lib/softoken/pkcs11.c
Expand Up @@ -3566,7 +3566,6 @@ NSC_InitToken(CK_SLOT_ID slotID, CK_CHAR_PTR pPin,
{
SFTKSlot *slot = sftk_SlotFromID(slotID, PR_FALSE);
SFTKDBHandle *handle;
SFTKDBHandle *certHandle;
SECStatus rv;
unsigned int i;
SFTKObject *object;
Expand Down Expand Up @@ -3614,19 +3613,16 @@ NSC_InitToken(CK_SLOT_ID slotID, CK_CHAR_PTR pPin,
}

rv = sftkdb_ResetKeyDB(handle);
/* clear the password */
sftkdb_ClearPassword(handle);
/* update slot->needLogin (should be true now since no password is set) */
sftk_checkNeedLogin(slot, handle);
sftk_freeDB(handle);
if (rv != SECSuccess) {
return CKR_DEVICE_ERROR;
}

/* finally mark all the user certs as non-user certs */
certHandle = sftk_getCertDB(slot);
if (certHandle == NULL)
return CKR_OK;

sftk_freeDB(certHandle);

return CKR_OK; /*is this the right function for not implemented*/
return CKR_OK;
}

/* NSC_InitPIN initializes the normal user's PIN. */
Expand Down
24 changes: 13 additions & 11 deletions lib/softoken/sdb.c
Expand Up @@ -1600,7 +1600,7 @@ sdb_PutMetaData(SDB *sdb, const char *id, const SECItem *item1,
return error;
}

static const char RESET_CMD[] = "DROP TABLE IF EXISTS %s;";
static const char RESET_CMD[] = "DELETE FROM %s;";
CK_RV
sdb_Reset(SDB *sdb)
{
Expand All @@ -1621,17 +1621,19 @@ sdb_Reset(SDB *sdb)
goto loser;
}

/* delete the key table */
newStr = sqlite3_mprintf(RESET_CMD, sdb_p->table);
if (newStr == NULL) {
error = CKR_HOST_MEMORY;
goto loser;
}
sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
sqlite3_free(newStr);
if (tableExists(sqlDB, sdb_p->table)) {
/* delete the contents of the key table */
newStr = sqlite3_mprintf(RESET_CMD, sdb_p->table);
if (newStr == NULL) {
error = CKR_HOST_MEMORY;
goto loser;
}
sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
sqlite3_free(newStr);

if (sqlerr != SQLITE_OK)
goto loser;
if (sqlerr != SQLITE_OK)
goto loser;
}

/* delete the password entry table */
sqlerr = sqlite3_exec(sqlDB, "DROP TABLE IF EXISTS metaData;",
Expand Down
1 change: 1 addition & 0 deletions nss.gyp
Expand Up @@ -168,6 +168,7 @@
'gtests/certdb_gtest/certdb_gtest.gyp:certdb_gtest',
'gtests/freebl_gtest/freebl_gtest.gyp:prng_gtest',
'gtests/pk11_gtest/pk11_gtest.gyp:pk11_gtest',
'gtests/softoken_gtest/softoken_gtest.gyp:softoken_gtest',
'gtests/ssl_gtest/ssl_gtest.gyp:ssl_gtest',
'gtests/util_gtest/util_gtest.gyp:util_gtest',
'gtests/nss_bogo_shim/nss_bogo_shim.gyp:nss_bogo_shim',
Expand Down
2 changes: 1 addition & 1 deletion tests/gtests/gtests.sh
Expand Up @@ -83,7 +83,7 @@ gtest_cleanup()
}

################## main #################################################
GTESTS="prng_gtest certhigh_gtest certdb_gtest der_gtest pk11_gtest util_gtest freebl_gtest"
GTESTS="prng_gtest certhigh_gtest certdb_gtest der_gtest pk11_gtest util_gtest freebl_gtest softoken_gtest"
SOURCE_DIR="$PWD"/../..
gtest_init $0
gtest_start
Expand Down

0 comments on commit 2c66d71

Please sign in to comment.