diff options
author | David Keeler <dkeeler@mozilla.com> | 2017-08-01 16:45:07 +0200 |
---|---|---|
committer | David Keeler <dkeeler@mozilla.com> | 2017-08-01 16:45:07 +0200 |
commit | abe0fcdc657265eafe226c96c629bc716ac85b77 (patch) | |
tree | 7c038b73963c07b5c515da1e24e29d29dec2b575 | |
parent | 36b1a750357ba5d8cf9eb2b887e92c880535790a (diff) | |
download | nss-hg-abe0fcdc657265eafe226c96c629bc716ac85b77.tar.gz |
Bug 1379273 - make softoken resettable via PK11_ResetToken r=franziskus,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
-rw-r--r-- | gtests/manifest.mn | 3 | ||||
-rw-r--r-- | gtests/softoken_gtest/Makefile | 45 | ||||
-rw-r--r-- | gtests/softoken_gtest/manifest.mn | 25 | ||||
-rw-r--r-- | gtests/softoken_gtest/softoken_gtest.cc | 132 | ||||
-rw-r--r-- | gtests/softoken_gtest/softoken_gtest.gyp | 51 | ||||
-rw-r--r-- | lib/softoken/pkcs11.c | 14 | ||||
-rw-r--r-- | lib/softoken/sdb.c | 24 | ||||
-rw-r--r-- | nss.gyp | 1 | ||||
-rwxr-xr-x | tests/gtests/gtests.sh | 2 |
9 files changed, 275 insertions, 22 deletions
diff --git a/gtests/manifest.mn b/gtests/manifest.mn index 1ae4cab77..3bc366432 100644 --- a/gtests/manifest.mn +++ b/gtests/manifest.mn @@ -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 diff --git a/gtests/softoken_gtest/Makefile b/gtests/softoken_gtest/Makefile new file mode 100644 index 000000000..996669782 --- /dev/null +++ b/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). # +####################################################################### diff --git a/gtests/softoken_gtest/manifest.mn b/gtests/softoken_gtest/manifest.mn new file mode 100644 index 000000000..4b34c099f --- /dev/null +++ b/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) diff --git a/gtests/softoken_gtest/softoken_gtest.cc b/gtests/softoken_gtest/softoken_gtest.cc new file mode 100644 index 000000000..f0e4be9f9 --- /dev/null +++ b/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(); +} diff --git a/gtests/softoken_gtest/softoken_gtest.gyp b/gtests/softoken_gtest/softoken_gtest.gyp new file mode 100644 index 000000000..cff0ea414 --- /dev/null +++ b/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' + } +} diff --git a/lib/softoken/pkcs11.c b/lib/softoken/pkcs11.c index a594fd501..4e940990e 100644 --- a/lib/softoken/pkcs11.c +++ b/lib/softoken/pkcs11.c @@ -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; @@ -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. */ diff --git a/lib/softoken/sdb.c b/lib/softoken/sdb.c index eb625b6cb..57337e334 100644 --- a/lib/softoken/sdb.c +++ b/lib/softoken/sdb.c @@ -1600,7 +1600,7 @@ loser: 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) { @@ -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;", @@ -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', diff --git a/tests/gtests/gtests.sh b/tests/gtests/gtests.sh index c785241c4..6de1ee4b7 100755 --- a/tests/gtests/gtests.sh +++ b/tests/gtests/gtests.sh @@ -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 |