diff options
author | wolfbeast <mcwerewolf@wolfbeast.com> | 2020-01-02 21:06:40 +0100 |
---|---|---|
committer | wolfbeast <mcwerewolf@wolfbeast.com> | 2020-01-02 21:06:40 +0100 |
commit | f4a12fc67689a830e9da1c87fd11afe5bc09deb3 (patch) | |
tree | 211ae0cd022a6c11b0026ecc7761a550c584583c /security/nss/gtests/softoken_gtest | |
parent | f7d30133221896638f7bf4f66c504255c4b14f48 (diff) | |
download | UXP-f4a12fc67689a830e9da1c87fd11afe5bc09deb3.tar UXP-f4a12fc67689a830e9da1c87fd11afe5bc09deb3.tar.gz UXP-f4a12fc67689a830e9da1c87fd11afe5bc09deb3.tar.lz UXP-f4a12fc67689a830e9da1c87fd11afe5bc09deb3.tar.xz UXP-f4a12fc67689a830e9da1c87fd11afe5bc09deb3.zip |
Issue #1338 - Part 2: Update NSS to 3.48-RTM
Diffstat (limited to 'security/nss/gtests/softoken_gtest')
4 files changed, 329 insertions, 88 deletions
diff --git a/security/nss/gtests/softoken_gtest/manifest.mn b/security/nss/gtests/softoken_gtest/manifest.mn index 4b34c099f..8a533c56b 100644 --- a/security/nss/gtests/softoken_gtest/manifest.mn +++ b/security/nss/gtests/softoken_gtest/manifest.mn @@ -6,12 +6,22 @@ CORE_DEPTH = ../.. DEPTH = ../.. MODULE = nss +DEFINES += -DDLL_SUFFIX=\"$(DLL_SUFFIX)\" -DDLL_PREFIX=\"$(DLL_PREFIX)\" + +include $(CORE_DEPTH)/coreconf/arch.mk +ifneq ($(OS_ARCH),WINNT) +DB_TESTS = \ + softoken_nssckbi_testlib_gtest.cc +endif + CPPSRCS = \ softoken_gtest.cc \ + $(DB_TESTS) \ $(NULL) INCLUDES += \ -I$(CORE_DEPTH)/gtests/google_test/gtest/include \ + -I$(CORE_DEPTH)/gtests/common \ -I$(CORE_DEPTH)/cpputil \ $(NULL) diff --git a/security/nss/gtests/softoken_gtest/softoken_gtest.cc b/security/nss/gtests/softoken_gtest/softoken_gtest.cc index 5e2a497b8..17949800a 100644 --- a/security/nss/gtests/softoken_gtest/softoken_gtest.cc +++ b/security/nss/gtests/softoken_gtest/softoken_gtest.cc @@ -1,104 +1,20 @@ -#include <cstdlib> -#if defined(_WIN32) -#include <windows.h> -#include <codecvt> -#endif - #include "cert.h" #include "certdb.h" #include "nspr.h" #include "nss.h" #include "pk11pub.h" +#include "secmod.h" #include "secerr.h" #include "nss_scoped_ptrs.h" +#include "util.h" #define GTEST_HAS_RTTI 0 #include "gtest/gtest.h" +#include <fstream> 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; } - const std::string &GetUTF8Path() { return mUTF8Path; } - - 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; - std::string mUTF8Path; -}; - -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); -#if defined(_WIN32) - // sqldb always uses UTF-8 regardless of the current system locale. - DWORD len = - MultiByteToWideChar(CP_ACP, 0, mPath.data(), mPath.size(), nullptr, 0); - std::vector<wchar_t> buf(len, L'\0'); - MultiByteToWideChar(CP_ACP, 0, mPath.data(), mPath.size(), buf.data(), - buf.size()); - std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; - mUTF8Path = converter.to_bytes(std::wstring(buf.begin(), buf.end())); -#else - mUTF8Path = mPath; -#endif -} - -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-") {} @@ -205,6 +121,27 @@ TEST_F(SoftokenTest, CreateObjectChangePassword) { EXPECT_EQ(nullptr, obj); } +// The size limit for a password is 500 characters as defined in pkcs11i.h +TEST_F(SoftokenTest, CreateObjectChangeToBigPassword) { + ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + ASSERT_TRUE(slot); + EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, nullptr)); + EXPECT_EQ( + SECSuccess, + PK11_ChangePW(slot.get(), "", + "rUIFIFr2bxKnbJbitsfkyqttpk6vCJzlYMNxcxXcaN37gSZKbLk763X7iR" + "yeVNWZHQ02lSF69HYjzTyPW3318ZD0DBFMMbALZ8ZPZP73CIo5uIQlaowV" + "IbP8eOhRYtGUqoLGlcIFNEYogV8Q3GN58VeBMs0KxrIOvPQ9s8SnYYkqvt" + "zzgntmAvCgvk64x6eQf0okHwegd5wi6m0WVJytEepWXkP9J629FSa5kNT8" + "FvL3jvslkiImzTNuTvl32fQDXXMSc8vVk5Q3mH7trMZM0VDdwHWYERjHbz" + "kGxFgp0VhediHx7p9kkz6H6ac4et9sW4UkTnN7xhYc1Zr17wRSk2heQtcX" + "oZJGwuzhiKm8A8wkuVxms6zO56P4JORIk8oaUW6lyNTLo2kWWnTA")); + EXPECT_EQ(SECSuccess, PK11_Logout(slot.get())); + ScopedPK11GenericObject obj(PK11_CreateGenericObject( + slot.get(), attributes, PR_ARRAY_SIZE(attributes), true)); + EXPECT_EQ(nullptr, obj); +} + TEST_F(SoftokenTest, CreateObjectChangeToEmptyPassword) { ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); ASSERT_TRUE(slot); @@ -221,6 +158,76 @@ TEST_F(SoftokenTest, CreateObjectChangeToEmptyPassword) { EXPECT_NE(nullptr, obj); } +// We should be able to read CRLF, LF and CR. +// During the Initialization of the NSS Database, is called a function to load +// PKCS11 modules defined in pkcs11.txt. This file is read to get the +// specifications, parse them and load the modules. Here we are ensuring that +// the parsing will work correctly, independent of the breaking line format of +// pkcs11.txt file, which could vary depending where it was created. +// If the parsing is not well interpreted, the database cannot initialize. +TEST_F(SoftokenTest, CreateObjectReadBreakLine) { + const std::string path = mNSSDBDir.GetPath(); + const std::string dbname_in = path + "/pkcs11.txt"; + const std::string dbname_out_cr = path + "/pkcs11_cr.txt"; + const std::string dbname_out_crlf = path + "/pkcs11_crlf.txt"; + const std::string dbname_out_lf = path + "/pkcs11_lf.txt"; + + std::ifstream in(dbname_in); + ASSERT_TRUE(in); + std::ofstream out_cr(dbname_out_cr); + ASSERT_TRUE(out_cr); + std::ofstream out_crlf(dbname_out_crlf); + ASSERT_TRUE(out_crlf); + std::ofstream out_lf(dbname_out_lf); + ASSERT_TRUE(out_lf); + + // Database should be correctly initialized by Setup() + ASSERT_TRUE(NSS_IsInitialized()); + ASSERT_EQ(SECSuccess, NSS_Shutdown()); + + // Prepare the file formats with CR, CRLF and LF + for (std::string line; getline(in, line);) { + out_cr << line << "\r"; + out_crlf << line << "\r\n"; + out_lf << line << "\n"; + } + in.close(); + out_cr.close(); + out_crlf.close(); + out_lf.close(); + + // Change the pkcs11.txt to CR format. + ASSERT_TRUE(!remove(dbname_in.c_str())); + ASSERT_TRUE(!rename(dbname_out_cr.c_str(), dbname_in.c_str())); + + // Try to initialize with CR format. + std::string nssInitArg("sql:"); + nssInitArg.append(mNSSDBDir.GetUTF8Path()); + ASSERT_EQ(SECSuccess, NSS_Initialize(nssInitArg.c_str(), "", "", SECMOD_DB, + NSS_INIT_NOROOTINIT)); + ASSERT_TRUE(NSS_IsInitialized()); + ASSERT_EQ(SECSuccess, NSS_Shutdown()); + + // Change the pkcs11.txt to CRLF format. + ASSERT_TRUE(!remove(dbname_in.c_str())); + ASSERT_TRUE(!rename(dbname_out_crlf.c_str(), dbname_in.c_str())); + + // Try to initialize with CRLF format. + ASSERT_EQ(SECSuccess, NSS_Initialize(nssInitArg.c_str(), "", "", SECMOD_DB, + NSS_INIT_NOROOTINIT)); + ASSERT_TRUE(NSS_IsInitialized()); + ASSERT_EQ(SECSuccess, NSS_Shutdown()); + + // Change the pkcs11.txt to LF format. + ASSERT_TRUE(!remove(dbname_in.c_str())); + ASSERT_TRUE(!rename(dbname_out_lf.c_str(), dbname_in.c_str())); + + // Try to initialize with LF format. + ASSERT_EQ(SECSuccess, NSS_Initialize(nssInitArg.c_str(), "", "", SECMOD_DB, + NSS_INIT_NOROOTINIT)); + ASSERT_TRUE(NSS_IsInitialized()); +} + class SoftokenNonAsciiTest : public SoftokenTest { protected: SoftokenNonAsciiTest() : SoftokenTest("SoftokenTest.\xF7-") {} @@ -351,6 +358,100 @@ TEST_F(SoftokenNoDBTest, NeedUserInitNoDB) { ASSERT_EQ(SECSuccess, NSS_Shutdown()); } +#ifndef NSS_FIPS_DISABLED + +class SoftokenFipsTest : public SoftokenTest { + protected: + SoftokenFipsTest() : SoftokenTest("SoftokenFipsTest.d-") {} + + virtual void SetUp() { + SoftokenTest::SetUp(); + + // Turn on FIPS mode (code borrowed from FipsMode in modutil/pk11.c) + char *internal_name; + ASSERT_FALSE(PK11_IsFIPS()); + internal_name = PR_smprintf("%s", SECMOD_GetInternalModule()->commonName); + ASSERT_EQ(SECSuccess, SECMOD_DeleteInternalModule(internal_name)); + PR_smprintf_free(internal_name); + ASSERT_TRUE(PK11_IsFIPS()); + } +}; + +const std::vector<std::string> kFipsPasswordCases[] = { + // FIPS level1 -> level1 -> level1 + {"", "", ""}, + // FIPS level1 -> level1 -> level2 + {"", "", "strong-_123"}, + // FIXME: this should work: FIPS level1 -> level2 -> level2 + // {"", "strong-_123", "strong-_456"}, + // FIPS level2 -> level2 -> level2 + {"strong-_123", "strong-_456", "strong-_123"}}; + +const std::vector<std::string> kFipsPasswordBadCases[] = { + // FIPS level1 -> level2 -> level1 + {"", "strong-_123", ""}, + // FIPS level2 -> level1 -> level1 + {"strong-_123", ""}, + // FIPS level2 -> level2 -> level1 + {"strong-_123", "strong-_456", ""}, + // initialize with a weak password + {"weak"}, + // FIPS level1 -> weak password + {"", "weak"}, + // FIPS level2 -> weak password + {"strong-_123", "weak"}}; + +class SoftokenFipsPasswordTest + : public SoftokenFipsTest, + public ::testing::WithParamInterface<std::vector<std::string>> {}; + +class SoftokenFipsBadPasswordTest + : public SoftokenFipsTest, + public ::testing::WithParamInterface<std::vector<std::string>> {}; + +TEST_P(SoftokenFipsPasswordTest, SetPassword) { + const std::vector<std::string> &passwords = GetParam(); + ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + ASSERT_TRUE(slot); + + auto it = passwords.begin(); + auto prev_it = it; + EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, (*it).c_str())); + for (it++; it != passwords.end(); it++, prev_it++) { + EXPECT_EQ(SECSuccess, + PK11_ChangePW(slot.get(), (*prev_it).c_str(), (*it).c_str())); + } +} + +TEST_P(SoftokenFipsBadPasswordTest, SetBadPassword) { + const std::vector<std::string> &passwords = GetParam(); + ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + ASSERT_TRUE(slot); + + auto it = passwords.begin(); + auto prev_it = it; + SECStatus rv = PK11_InitPin(slot.get(), nullptr, (*it).c_str()); + if (it + 1 == passwords.end()) + EXPECT_EQ(SECFailure, rv); + else + EXPECT_EQ(SECSuccess, rv); + for (it++; it != passwords.end(); it++, prev_it++) { + rv = PK11_ChangePW(slot.get(), (*prev_it).c_str(), (*it).c_str()); + if (it + 1 == passwords.end()) + EXPECT_EQ(SECFailure, rv); + else + EXPECT_EQ(SECSuccess, rv); + } +} + +INSTANTIATE_TEST_CASE_P(FipsPasswordCases, SoftokenFipsPasswordTest, + ::testing::ValuesIn(kFipsPasswordCases)); + +INSTANTIATE_TEST_CASE_P(BadFipsPasswordCases, SoftokenFipsBadPasswordTest, + ::testing::ValuesIn(kFipsPasswordBadCases)); + +#endif + } // namespace nss_test int main(int argc, char **argv) { diff --git a/security/nss/gtests/softoken_gtest/softoken_gtest.gyp b/security/nss/gtests/softoken_gtest/softoken_gtest.gyp index cff0ea414..3d9b8dba9 100644 --- a/security/nss/gtests/softoken_gtest/softoken_gtest.gyp +++ b/security/nss/gtests/softoken_gtest/softoken_gtest.gyp @@ -12,6 +12,7 @@ 'type': 'executable', 'sources': [ 'softoken_gtest.cc', + 'softoken_nssckbi_testlib_gtest.cc', ], 'dependencies': [ '<(DEPTH)/exports.gyp:nss_exports', @@ -19,7 +20,7 @@ '<(DEPTH)/gtests/google_test/google_test.gyp:gtest', ], 'conditions': [ - [ 'test_build==1', { + [ 'static_libs==1', { 'dependencies': [ '<(DEPTH)/lib/nss/nss.gyp:nss_static', '<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static', @@ -30,6 +31,7 @@ '<(DEPTH)/lib/dev/dev.gyp:nssdev', '<(DEPTH)/lib/pki/pki.gyp:nsspki', '<(DEPTH)/lib/ssl/ssl.gyp:ssl', + '<(DEPTH)/lib/libpkix/libpkix.gyp:libpkix', ], }, { 'dependencies': [ @@ -43,6 +45,10 @@ 'target_defaults': { 'include_dirs': [ '../../lib/util' + ], + 'defines': [ + 'DLL_PREFIX=\"<(dll_prefix)\"', + 'DLL_SUFFIX=\"<(dll_suffix)\"' ] }, 'variables': { diff --git a/security/nss/gtests/softoken_gtest/softoken_nssckbi_testlib_gtest.cc b/security/nss/gtests/softoken_gtest/softoken_nssckbi_testlib_gtest.cc new file mode 100644 index 000000000..e7d6bc28b --- /dev/null +++ b/security/nss/gtests/softoken_gtest/softoken_nssckbi_testlib_gtest.cc @@ -0,0 +1,124 @@ +#include "cert.h" +#include "certdb.h" +#include "nspr.h" +#include "nss.h" +#include "pk11pub.h" +#include "secerr.h" + +#include "nss_scoped_ptrs.h" +#include "util.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" + +namespace nss_test { + +class SoftokenBuiltinsTest : public ::testing::Test { + protected: + SoftokenBuiltinsTest() : nss_db_dir_("SoftokenBuiltinsTest.d-") {} + SoftokenBuiltinsTest(const std::string &prefix) : nss_db_dir_(prefix) {} + + virtual void SetUp() { + std::string nss_init_arg("sql:"); + nss_init_arg.append(nss_db_dir_.GetUTF8Path()); + ASSERT_EQ(SECSuccess, NSS_Initialize(nss_init_arg.c_str(), "", "", + SECMOD_DB, NSS_INIT_NOROOTINIT)); + } + + virtual void TearDown() { + ASSERT_EQ(SECSuccess, NSS_Shutdown()); + const std::string &nss_db_dir_path = nss_db_dir_.GetPath(); + ASSERT_EQ(0, unlink((nss_db_dir_path + "/cert9.db").c_str())); + ASSERT_EQ(0, unlink((nss_db_dir_path + "/key4.db").c_str())); + ASSERT_EQ(0, unlink((nss_db_dir_path + "/pkcs11.txt").c_str())); + } + + virtual void LoadModule() { + ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + ASSERT_TRUE(slot); + EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, nullptr)); + SECStatus result = SECMOD_AddNewModule( + "Builtins-testlib", DLL_PREFIX "nssckbi-testlib." DLL_SUFFIX, 0, 0); + ASSERT_EQ(result, SECSuccess); + } + + ScopedUniqueDirectory nss_db_dir_; +}; + +// The next tests in this class are used to test the Distrust Fields. +// More details about these fields in lib/ckfw/builtins/README. +TEST_F(SoftokenBuiltinsTest, CheckNoDistrustFields) { + const char *kCertNickname = + "Builtin Object Token:Distrust Fields Test - no_distrust"; + LoadModule(); + + CERTCertDBHandle *cert_handle = CERT_GetDefaultCertDB(); + ASSERT_TRUE(cert_handle); + ScopedCERTCertificate cert( + CERT_FindCertByNickname(cert_handle, kCertNickname)); + ASSERT_TRUE(cert); + + EXPECT_EQ(PR_FALSE, + PK11_HasAttributeSet(cert->slot, cert->pkcs11ID, + CKA_NSS_SERVER_DISTRUST_AFTER, PR_FALSE)); + EXPECT_EQ(PR_FALSE, + PK11_HasAttributeSet(cert->slot, cert->pkcs11ID, + CKA_NSS_EMAIL_DISTRUST_AFTER, PR_FALSE)); + ASSERT_FALSE(cert->distrust); +} + +TEST_F(SoftokenBuiltinsTest, CheckOkDistrustFields) { + const char *kCertNickname = + "Builtin Object Token:Distrust Fields Test - ok_distrust"; + LoadModule(); + + CERTCertDBHandle *cert_handle = CERT_GetDefaultCertDB(); + ASSERT_TRUE(cert_handle); + ScopedCERTCertificate cert( + CERT_FindCertByNickname(cert_handle, kCertNickname)); + ASSERT_TRUE(cert); + + const char *kExpectedDERValueServer = "200617000000Z"; + const char *kExpectedDERValueEmail = "071014085320Z"; + // When a valid timestamp is encoded, the result length is exactly 13. + const unsigned int kDistrustFieldSize = 13; + + ASSERT_TRUE(cert->distrust); + ASSERT_EQ(kDistrustFieldSize, cert->distrust->serverDistrustAfter.len); + ASSERT_NE(nullptr, cert->distrust->serverDistrustAfter.data); + EXPECT_TRUE(!memcmp(kExpectedDERValueServer, + cert->distrust->serverDistrustAfter.data, + kDistrustFieldSize)); + + ASSERT_EQ(kDistrustFieldSize, cert->distrust->emailDistrustAfter.len); + ASSERT_NE(nullptr, cert->distrust->emailDistrustAfter.data); + EXPECT_TRUE(!memcmp(kExpectedDERValueEmail, + cert->distrust->emailDistrustAfter.data, + kDistrustFieldSize)); +} + +TEST_F(SoftokenBuiltinsTest, CheckInvalidDistrustFields) { + const char *kCertNickname = + "Builtin Object Token:Distrust Fields Test - err_distrust"; + LoadModule(); + + CERTCertDBHandle *cert_handle = CERT_GetDefaultCertDB(); + ASSERT_TRUE(cert_handle); + ScopedCERTCertificate cert( + CERT_FindCertByNickname(cert_handle, kCertNickname)); + ASSERT_TRUE(cert); + + // The field should never be set to TRUE in production, we are just + // testing if this field is readable, even if set to TRUE. + EXPECT_EQ(PR_TRUE, + PK11_HasAttributeSet(cert->slot, cert->pkcs11ID, + CKA_NSS_SERVER_DISTRUST_AFTER, PR_FALSE)); + // If something other than CK_BBOOL CK_TRUE, it will be considered FALSE + // Here, there is an OCTAL value, but with unexpected content (1 digit less). + EXPECT_EQ(PR_FALSE, + PK11_HasAttributeSet(cert->slot, cert->pkcs11ID, + CKA_NSS_EMAIL_DISTRUST_AFTER, PR_FALSE)); + ASSERT_FALSE(cert->distrust); +} + +} // namespace nss_test |