diff --git a/automation/abi-check/expected-report-libnsssysinit.so.txt b/automation/abi-check/expected-report-libnsssysinit.so.txt index e69de29bb2..cf59071a28 100644 --- a/automation/abi-check/expected-report-libnsssysinit.so.txt +++ b/automation/abi-check/expected-report-libnsssysinit.so.txt @@ -0,0 +1,8 @@ +Function symbols changes summary: 2 Removed, 0 Added function symbols not referenced by debug info +Variable symbols changes summary: 0 Removed, 0 Added variable symbol not referenced by debug info + +2 Removed function symbols not referenced by debug info: + + _fini + _init + diff --git a/gtests/Makefile b/gtests/Makefile index 2b5492506c..718850c3a2 100644 --- a/gtests/Makefile +++ b/gtests/Makefile @@ -26,6 +26,16 @@ include $(CORE_DEPTH)/coreconf/config.mk # (4) Include "local" platform-dependent assignments (OPTIONAL). # ####################################################################### +# Don't build sysinit gtests unless we are also building libnsssysinit. +# See lib/Makefile for the corresponding rules. +ifndef MOZILLA_CLIENT +ifeq ($(OS_ARCH),Linux) +ifneq ($(NSS_BUILD_UTIL_ONLY),1) +SYSINIT_GTEST=sysinit_gtest +endif +endif +endif + ####################################################################### # (5) Execute "global" rules. (OPTIONAL) # diff --git a/gtests/manifest.mn b/gtests/manifest.mn index 13048f037c..18adb3d7db 100644 --- a/gtests/manifest.mn +++ b/gtests/manifest.mn @@ -26,6 +26,7 @@ NSS_SRCDIRS = \ pk11_gtest \ softoken_gtest \ ssl_gtest \ + $(SYSINIT_GTEST) \ nss_bogo_shim \ $(NULL) endif diff --git a/gtests/sysinit_gtest/Makefile b/gtests/sysinit_gtest/Makefile new file mode 100644 index 0000000000..0d547e0803 --- /dev/null +++ b/gtests/sysinit_gtest/Makefile @@ -0,0 +1,43 @@ +#! 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 + +####################################################################### +# (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/sysinit_gtest/getUserDB_unittest.cc b/gtests/sysinit_gtest/getUserDB_unittest.cc new file mode 100644 index 0000000000..845b6e36e5 --- /dev/null +++ b/gtests/sysinit_gtest/getUserDB_unittest.cc @@ -0,0 +1,164 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ +#include "gtest/gtest.h" +#include "prenv.h" +#include "seccomon.h" + +#include +#include +#include +#include + +namespace nss_test { + +// Return the path to user's NSS database. +extern "C" char *getUserDB(void); + +class Sysinit : public ::testing::Test { + protected: + void SetUp() { + home_var_ = PR_GetEnvSecure("HOME"); + if (home_var_) { + old_home_dir_ = home_var_; + } + xdg_data_home_var_ = PR_GetEnvSecure("XDG_DATA_HOME"); + if (xdg_data_home_var_) { + old_xdg_data_home_ = xdg_data_home_var_; + ASSERT_EQ(0, unsetenv("XDG_DATA_HOME")); + } + char tmp[] = "/tmp/nss-tmp.XXXXXX"; + tmp_home_ = mkdtemp(tmp); + ASSERT_EQ(0, setenv("HOME", tmp_home_.c_str(), 1)); + } + + void TearDown() { + // Set HOME back to original + if (home_var_) { + ASSERT_EQ(0, setenv("HOME", old_home_dir_.c_str(), 1)); + } else { + ASSERT_EQ(0, unsetenv("HOME")); + } + // Set XDG_DATA_HOME back to original + if (xdg_data_home_var_) { + ASSERT_EQ(0, setenv("XDG_DATA_HOME", old_xdg_data_home_.c_str(), 1)); + } + // Remove test dirs. + if (!nssdir_.empty()) { + ASSERT_EQ(0, RemoveEmptyDirsFromStart(nssdir_, tmp_home_)); + } + } + + // Remove all dirs within @start from @path containing only empty dirs. + // Assumes @start already exists. + // Upon successful completion, return 0. Otherwise, -1. + static int RemoveEmptyDirsFromStart(std::string path, std::string start) { + if (path.find(start) == std::string::npos) { + return -1; + } + std::string temp = path; + if (rmdir(temp.c_str())) { + return -1; + } + for (size_t i = temp.length() - 1; i > start.length(); --i) { + if (temp[i] == '/') { + temp[i] = '\0'; + if (rmdir(temp.c_str())) { + return -1; + } + } + } + if (rmdir(start.c_str())) { + return -1; + } + return 0; + } + + // Create empty dirs appending @path to @start with mode @mode. + // Assumes @start already exists. + // Upon successful completion, return the string @start + @path. + static std::string CreateEmptyDirsFromStart(std::string start, + std::string path, mode_t mode) { + std::string temp = start + "/"; + for (size_t i = 1; i < path.length(); ++i) { + if (path[i] == '/') { + EXPECT_EQ(0, mkdir(temp.c_str(), mode)); + } + temp += path[i]; + } + // We reach the end of string before the last dir is created + EXPECT_EQ(0, mkdir(temp.c_str(), mode)); + return temp; + } + + char *home_var_; + char *xdg_data_home_var_; + std::string old_home_dir_; + std::string old_xdg_data_home_; + std::string nssdir_; + std::string tmp_home_; +}; + +class SysinitSetXdgUserDataHome : public Sysinit { + protected: + void SetUp() { + Sysinit::SetUp(); + ASSERT_EQ(0, setenv("XDG_DATA_HOME", tmp_home_.c_str(), 1)); + } +}; + +class SysinitSetTrashXdgUserDataHome : public Sysinit { + protected: + void SetUp() { + Sysinit::SetUp(); + std::string trashPath = tmp_home_ + "/this/path/does/not/exist"; + ASSERT_EQ(0, setenv("XDG_DATA_HOME", trashPath.c_str(), 1)); + } + + void TearDown() { + ASSERT_EQ(0, rmdir(tmp_home_.c_str())); + Sysinit::TearDown(); + } +}; + +// Check if $HOME/.pki/nssdb is used if it exists +TEST_F(Sysinit, LegacyPath) { + nssdir_ = CreateEmptyDirsFromStart(tmp_home_, "/.pki/nssdb", 0760); + char *nssdb = getUserDB(); + ASSERT_EQ(nssdir_, nssdb); + PORT_Free(nssdb); +} + +// Check if $HOME/.local/share/pki/nssdb is used if: +// - $HOME/.pki/nssdb does not exist; +// - XDG_DATA_HOME is not set. +TEST_F(Sysinit, XdgDefaultPath) { + nssdir_ = CreateEmptyDirsFromStart(tmp_home_, "/.local/share", 0755); + nssdir_ = CreateEmptyDirsFromStart(nssdir_, "/pki/nssdb", 0760); + char *nssdb = getUserDB(); + ASSERT_EQ(nssdir_, nssdb); + PORT_Free(nssdb); +} + +// Check if ${XDG_DATA_HOME}/pki/nssdb is used if: +// - $HOME/.pki/nssdb does not exist; +// - XDG_DATA_HOME is set and the path exists. +TEST_F(SysinitSetXdgUserDataHome, XdgSetPath) { + // XDG_DATA_HOME is set to HOME + nssdir_ = CreateEmptyDirsFromStart(tmp_home_, "/pki/nssdb", 0760); + char *nssdb = getUserDB(); + ASSERT_EQ(nssdir_, nssdb); + PORT_Free(nssdb); +} + +// Check if it fails when: +// - XDG_DATA_HOME is set to a path that does not exist; +// - $HOME/.pki/nssdb also does not exist. */ +TEST_F(SysinitSetTrashXdgUserDataHome, XdgSetToTrashPath) { + char *nssdb = getUserDB(); + ASSERT_EQ(nullptr, nssdb); +} + +} // namespace nss_test diff --git a/gtests/sysinit_gtest/manifest.mn b/gtests/sysinit_gtest/manifest.mn new file mode 100644 index 0000000000..2fb8167aa3 --- /dev/null +++ b/gtests/sysinit_gtest/manifest.mn @@ -0,0 +1,27 @@ +# +# 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 = \ + getUserDB_unittest.cc \ + sysinit_gtest.cc \ + $(NULL) + +INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \ + -I$(CORE_DEPTH)/gtests/common + +REQUIRES = nspr nss libdbm gtest + +PROGRAM = sysinit_gtest + +EXTRA_LIBS = \ + $(DIST)/lib/$(LIB_PREFIX)gtest.$(LIB_SUFFIX) $(EXTRA_OBJS) \ + $(DIST)/lib/$(LIB_PREFIX)nsssysinit.$(LIB_SUFFIX) \ + $(NULL) + +USE_STATIC_LIBS = 1 diff --git a/gtests/sysinit_gtest/sysinit_gtest.cc b/gtests/sysinit_gtest/sysinit_gtest.cc new file mode 100644 index 0000000000..9f99e366d9 --- /dev/null +++ b/gtests/sysinit_gtest/sysinit_gtest.cc @@ -0,0 +1,9 @@ +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" + +int main(int argc, char** argv) { + // Start the tests + ::testing::InitGoogleTest(&argc, argv); + int rv = RUN_ALL_TESTS(); + return rv; +} diff --git a/gtests/sysinit_gtest/sysinit_gtest.gyp b/gtests/sysinit_gtest/sysinit_gtest.gyp new file mode 100644 index 0000000000..1fcacfe7f8 --- /dev/null +++ b/gtests/sysinit_gtest/sysinit_gtest.gyp @@ -0,0 +1,35 @@ +# 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': 'sysinit_gtest', + 'type': 'executable', + 'sources': [ + 'sysinit_gtest.cc', + 'getUserDB_unittest.cc', + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports', + '<(DEPTH)/gtests/google_test/google_test.gyp:gtest', + '<(DEPTH)/lib/sysinit/sysinit.gyp:nsssysinit_static' + ] + } + ], + 'target_defaults': { + 'include_dirs': [ + '../../lib/sysinit' + ], + 'defines': [ + 'NSS_USE_STATIC_LIBS' + ] + }, + 'variables': { + 'module': 'nss' + } +} diff --git a/lib/sysinit/manifest.mn b/lib/sysinit/manifest.mn index 822f4fcbdc..40a119e994 100644 --- a/lib/sysinit/manifest.mn +++ b/lib/sysinit/manifest.mn @@ -2,14 +2,16 @@ # 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 = ../.. -CORE_DEPTH = ../.. - -# MODULE public and private header directories are implicitly REQUIRED. MODULE = nss -CSRCS = nsssysinit.c +CSRCS = \ + nsssysinit.c \ + $(NULL) LIBRARY_NAME = nsssysinit -#LIBRARY_VERSION = 3 +MAPFILE = $(OBJDIR)/nsssysinit.def +# This part of the code, including all sub-dirs, can be optimized for size +export ALLOW_OPT_CODE_SIZE = 1 diff --git a/lib/sysinit/nsssysinit.c b/lib/sysinit/nsssysinit.c index 39e2ad7a17..2572141a39 100644 --- a/lib/sysinit/nsssysinit.c +++ b/lib/sysinit/nsssysinit.c @@ -37,9 +37,41 @@ testdir(char *dir) return S_ISDIR(buf.st_mode); } +/** + * Append given @dir to @path and creates the directory with mode @mode. + * Returns 0 if successful, -1 otherwise. + * Assumes that the allocation for @path has sufficient space for @dir + * to be added. + */ +static int +appendDirAndCreate(char *path, char *dir, mode_t mode) +{ + PORT_Strcat(path, dir); + if (!testdir(path)) { + if (mkdir(path, mode)) { + return -1; + } + } + return 0; +} + +#define XDG_NSS_USER_PATH1 "/.local" +#define XDG_NSS_USER_PATH2 "/share" +#define XDG_NSS_USER_PATH3 "/pki" + #define NSS_USER_PATH1 "/.pki" #define NSS_USER_PATH2 "/nssdb" -static char * + +/** + * Return the path to user's NSS database. + * We search in the following dirs in order: + * (1) $HOME/.pki/nssdb; + * (2) $XDG_DATA_HOME/pki/nssdb if XDG_DATA_HOME is set; + * (3) $HOME/.local/share/pki/nssdb (default XDG_DATA_HOME value). + * If (1) does not exist, then the returned dir will be set to either + * (2) or (3), depending if XDG_DATA_HOME is set. + */ +char * getUserDB(void) { char *userdir = PR_GetEnvSecure("HOME"); @@ -50,22 +82,47 @@ getUserDB(void) } nssdir = PORT_Alloc(strlen(userdir) + sizeof(NSS_USER_PATH1) + sizeof(NSS_USER_PATH2)); - if (nssdir == NULL) { - return NULL; - } PORT_Strcpy(nssdir, userdir); - /* verify it exists */ - if (!testdir(nssdir)) { + PORT_Strcat(nssdir, NSS_USER_PATH1 NSS_USER_PATH2); + if (testdir(nssdir)) { + /* $HOME/.pki/nssdb exists */ + return nssdir; + } else { + /* either $HOME/.pki or $HOME/.pki/nssdb does not exist */ PORT_Free(nssdir); - return NULL; } - PORT_Strcat(nssdir, NSS_USER_PATH1); - if (!testdir(nssdir) && mkdir(nssdir, 0760)) { - PORT_Free(nssdir); + int size = 0; + char *xdguserdatadir = PR_GetEnvSecure("XDG_DATA_HOME"); + if (xdguserdatadir) { + size = strlen(xdguserdatadir); + } else { + size = strlen(userdir) + sizeof(XDG_NSS_USER_PATH1) + sizeof(XDG_NSS_USER_PATH2); + } + size += sizeof(XDG_NSS_USER_PATH3) + sizeof(NSS_USER_PATH2); + + nssdir = PORT_Alloc(size); + if (nssdir == NULL) { return NULL; } - PORT_Strcat(nssdir, NSS_USER_PATH2); - if (!testdir(nssdir) && mkdir(nssdir, 0760)) { + + if (xdguserdatadir) { + PORT_Strcpy(nssdir, xdguserdatadir); + if (!testdir(nssdir)) { + PORT_Free(nssdir); + return NULL; + } + + } else { + PORT_Strcpy(nssdir, userdir); + if (appendDirAndCreate(nssdir, XDG_NSS_USER_PATH1, 0755) || + appendDirAndCreate(nssdir, XDG_NSS_USER_PATH2, 0755)) { + PORT_Free(nssdir); + return NULL; + } + } + /* ${XDG_DATA_HOME:-$HOME/.local/share}/pki/nssdb */ + if (appendDirAndCreate(nssdir, XDG_NSS_USER_PATH3, 0760) || + appendDirAndCreate(nssdir, NSS_USER_PATH2, 0760)) { PORT_Free(nssdir); return NULL; } diff --git a/lib/sysinit/nsssysinit.def b/lib/sysinit/nsssysinit.def new file mode 100644 index 0000000000..2e272be064 --- /dev/null +++ b/lib/sysinit/nsssysinit.def @@ -0,0 +1,26 @@ +;+# +;+# 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/. +;+# +;+# OK, this file is meant to support SUN, LINUX, AIX and WINDOWS +;+# 1. For all unix platforms, the string ";-" means "remove this line" +;+# 2. For all unix platforms, the string " DATA " will be removed from any +;+# line on which it occurs. +;+# 3. Lines containing ";+" will have ";+" removed on SUN and LINUX. +;+# On AIX, lines containing ";+" will be removed. +;+# 4. For all unix platforms, the string ";;" will thave the ";;" removed. +;+# 5. For all unix platforms, after the above processing has taken place, +;+# all characters after the first ";" on the line will be removed. +;+# And for AIX, the first ";" will also be removed. +;+# This file is passed directly to windows. Since ';' is a comment, all UNIX +;+# directives are hidden behind ";", ";+", and ";-" +;+ +;+NSS_3.15 { # NSS 3.15 release +;+ global: +LIBRARY nsssysiniit ;- +EXPORTS ;- +NSS_ReturnModuleSpecData; +;+ local: +;+*; +;+}; diff --git a/lib/sysinit/sysinit.gyp b/lib/sysinit/sysinit.gyp index e961325f6c..d76c27598c 100644 --- a/lib/sysinit/sysinit.gyp +++ b/lib/sysinit/sysinit.gyp @@ -3,29 +3,32 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. { 'includes': [ - '../../coreconf/config.gypi' + '../../coreconf/config.gypi', ], 'targets': [ { 'target_name': 'nsssysinit_static', 'type': 'static_library', 'sources': [ - 'nsssysinit.c' + 'nsssysinit.c', ], 'dependencies': [ '<(DEPTH)/exports.gyp:nss_exports', '<(DEPTH)/lib/util/util.gyp:nssutil3' - ] + ], }, { 'target_name': 'nsssysinit', 'type': 'shared_library', 'dependencies': [ - 'nsssysinit_static' - ] + 'nsssysinit_static', + ], + 'variables': { + 'mapfile': 'nsssysinit.def', + }, } ], 'variables': { - 'module': 'nss' + 'module': 'nss', } -} \ No newline at end of file +} diff --git a/nss.gyp b/nss.gyp index 18fa92f125..ba48c4bbfb 100644 --- a/nss.gyp +++ b/nss.gyp @@ -199,6 +199,7 @@ [ 'OS=="linux"', { 'dependencies': [ 'cmd/lowhashtest/lowhashtest.gyp:lowhashtest', + 'gtests/sysinit_gtest/sysinit_gtest.gyp:sysinit_gtest', ], }], [ 'disable_libpkix==0', { diff --git a/tests/gtests/gtests.sh b/tests/gtests/gtests.sh index 6606b59e77..583ecec62c 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 softoken_gtest blake2b_gtest" +GTESTS="prng_gtest certhigh_gtest certdb_gtest der_gtest pk11_gtest util_gtest freebl_gtest softoken_gtest sysinit_gtest blake2b_gtest" SOURCE_DIR="$PWD"/../.. gtest_init $0 gtest_start