123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- /* 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 "seccomon.h"
- #include "prio.h"
- #include "prprf.h"
- #include "plhash.h"
- #include "prenv.h"
- /*
- * The following provides a default example for operating systems to set up
- * and manage applications loading NSS on their OS globally.
- *
- * This code hooks in to the system pkcs11.txt, which controls all the loading
- * of pkcs11 modules common to all applications.
- */
- #ifndef LINUX
- #error __FILE__ only builds on Linux.
- #endif
- #include <unistd.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- static int
- testdir(char *dir)
- {
- struct stat buf;
- memset(&buf, 0, sizeof(buf));
- if (stat(dir, &buf) < 0) {
- return 0;
- }
- 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"
- /**
- * 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");
- char *nssdir = NULL;
- if (userdir == NULL) {
- return NULL;
- }
- nssdir = PORT_Alloc(strlen(userdir) + sizeof(NSS_USER_PATH1) + sizeof(NSS_USER_PATH2));
- PORT_Strcpy(nssdir, userdir);
- 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);
- }
- 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;
- }
- 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;
- }
- return nssdir;
- }
- #define NSS_DEFAULT_SYSTEM "/etc/pki/nssdb"
- static char *
- getSystemDB(void)
- {
- return PORT_Strdup(NSS_DEFAULT_SYSTEM);
- }
- static PRBool
- userIsRoot()
- {
- /* this works for linux and all unixes that we know off
- though it isn't stated as such in POSIX documentation */
- return getuid() == 0;
- }
- static PRBool
- userCanModifySystemDB()
- {
- return (access(NSS_DEFAULT_SYSTEM, W_OK) == 0);
- }
- #ifndef NSS_FIPS_DISABLED
- static PRBool
- getFIPSEnv(void)
- {
- char *fipsEnv = PR_GetEnvSecure("NSS_FIPS");
- if (!fipsEnv) {
- return PR_FALSE;
- }
- if ((strcasecmp(fipsEnv, "fips") == 0) ||
- (strcasecmp(fipsEnv, "true") == 0) ||
- (strcasecmp(fipsEnv, "on") == 0) ||
- (strcasecmp(fipsEnv, "1") == 0)) {
- return PR_TRUE;
- }
- return PR_FALSE;
- }
- #endif /* NSS_FIPS_DISABLED */
- static PRBool
- getFIPSMode(void)
- {
- #ifndef NSS_FIPS_DISABLED
- FILE *f;
- char d;
- size_t size;
- f = fopen("/proc/sys/crypto/fips_enabled", "r");
- if (!f) {
- /* if we don't have a proc flag, fall back to the
- * environment variable */
- return getFIPSEnv();
- }
- size = fread(&d, 1, 1, f);
- fclose(f);
- if (size != 1)
- return PR_FALSE;
- if (d != '1')
- return PR_FALSE;
- return PR_TRUE;
- #else
- return PR_FALSE;
- #endif /* NSS_FIPS_DISABLED */
- }
- #define NSS_DEFAULT_FLAGS "flags=readonly"
- /* configuration flags according to
- * https://developer.mozilla.org/en/PKCS11_Module_Specs
- * As stated there the slotParams start with a slot name which is a slotID
- * Slots 1 through 3 are reserved for the nss internal modules as follows:
- * 1 for crypto operations slot non-fips,
- * 2 for the key slot, and
- * 3 for the crypto operations slot fips
- */
- #define CIPHER_ORDER_FLAGS "cipherOrder=100"
- #define SLOT_FLAGS \
- "[slotFlags=RSA,RC4,RC2,DES,DH,SHA1,MD5,MD2,SSL,TLS,AES,RANDOM" \
- " askpw=any timeout=30 ]"
- static const char *nssDefaultFlags =
- CIPHER_ORDER_FLAGS " slotParams={0x00000001=" SLOT_FLAGS " } ";
- static const char *nssDefaultFIPSFlags =
- CIPHER_ORDER_FLAGS " slotParams={0x00000003=" SLOT_FLAGS " } ";
- /*
- * This function builds the list of databases and modules to load, and sets
- * their configuration. For the sample we have a fixed set.
- * 1. We load the user's home nss database.
- * 2. We load the user's custom PKCS #11 modules.
- * 3. We load the system nss database readonly.
- *
- * Any space allocated in get_list must be freed in release_list.
- * This function can use whatever information is available to the application.
- * it is running in the process of the application for which it is making
- * decisions, so it's possible to acquire the application name as part of
- * the decision making process.
- *
- */
- static char **
- get_list(char *filename, char *stripped_parameters)
- {
- char **module_list = PORT_ZNewArray(char *, 5);
- char *userdb, *sysdb;
- int isFIPS = getFIPSMode();
- const char *nssflags = isFIPS ? nssDefaultFIPSFlags : nssDefaultFlags;
- int next = 0;
- /* can't get any space */
- if (module_list == NULL) {
- return NULL;
- }
- sysdb = getSystemDB();
- userdb = getUserDB();
- /* Don't open root's user DB */
- if (userdb != NULL && !userIsRoot()) {
- /* return a list of databases to open. First the user Database */
- module_list[next++] = PR_smprintf(
- "library= "
- "module=\"NSS User database\" "
- "parameters=\"configdir='sql:%s' %s tokenDescription='NSS user database'\" "
- "NSS=\"trustOrder=75 %sflags=internal%s\"",
- userdb, stripped_parameters, nssflags,
- isFIPS ? ",FIPS" : "");
- /* now open the user's defined PKCS #11 modules */
- /* skip the local user DB entry */
- module_list[next++] = PR_smprintf(
- "library= "
- "module=\"NSS User database\" "
- "parameters=\"configdir='sql:%s' %s\" "
- "NSS=\"flags=internal,moduleDBOnly,defaultModDB,skipFirst\"",
- userdb, stripped_parameters);
- }
- /* now the system database (always read only unless it's root) */
- if (sysdb) {
- const char *readonly = userCanModifySystemDB() ? "" : "flags=readonly";
- module_list[next++] = PR_smprintf(
- "library= "
- "module=\"NSS system database\" "
- "parameters=\"configdir='sql:%s' tokenDescription='NSS system database' %s\" "
- "NSS=\"trustOrder=80 %sflags=internal,critical\"",
- sysdb, readonly, nssflags);
- }
- /* that was the last module */
- module_list[next] = 0;
- PORT_Free(userdb);
- PORT_Free(sysdb);
- return module_list;
- }
- static char **
- release_list(char **arg)
- {
- static char *success = "Success";
- int next;
- for (next = 0; arg[next]; next++) {
- free(arg[next]);
- }
- PORT_Free(arg);
- return &success;
- }
- #include "utilpars.h"
- #define TARGET_SPEC_COPY(new, start, end) \
- if (end > start) { \
- int _cnt = end - start; \
- PORT_Memcpy(new, start, _cnt); \
- new += _cnt; \
- }
- /*
- * According the strcpy man page:
- *
- * The strings may not overlap, and the destination string dest must be
- * large enough to receive the copy.
- *
- * This implementation allows target to overlap with src.
- * It does not allow the src to overlap the target.
- * example: overlapstrcpy(string, string+4) is fine
- * overlapstrcpy(string+4, string) is not.
- */
- static void
- overlapstrcpy(char *target, char *src)
- {
- while (*src) {
- *target++ = *src++;
- }
- *target = 0;
- }
- /* determine what options the user was trying to open this database with */
- /* filename is the directory pointed to by configdir= */
- /* stripped is the rest of the parameters with configdir= stripped out */
- static SECStatus
- parse_parameters(const char *parameters, char **filename, char **stripped)
- {
- const char *sourcePrev;
- const char *sourceCurr;
- char *targetCurr;
- char *newStripped;
- *filename = NULL;
- *stripped = NULL;
- newStripped = PORT_Alloc(PORT_Strlen(parameters) + 2);
- targetCurr = newStripped;
- sourcePrev = parameters;
- sourceCurr = NSSUTIL_ArgStrip(parameters);
- TARGET_SPEC_COPY(targetCurr, sourcePrev, sourceCurr);
- while (*sourceCurr) {
- int next;
- sourcePrev = sourceCurr;
- NSSUTIL_HANDLE_STRING_ARG(sourceCurr, *filename, "configdir=",
- sourcePrev = sourceCurr;)
- NSSUTIL_HANDLE_FINAL_ARG(sourceCurr);
- TARGET_SPEC_COPY(targetCurr, sourcePrev, sourceCurr);
- }
- *targetCurr = 0;
- if (*filename == NULL) {
- PORT_Free(newStripped);
- return SECFailure;
- }
- /* strip off any directives from the filename */
- if (strncmp("sql:", *filename, 4) == 0) {
- overlapstrcpy(*filename, (*filename) + 4);
- } else if (strncmp("dbm:", *filename, 4) == 0) {
- overlapstrcpy(*filename, (*filename) + 4);
- } else if (strncmp("extern:", *filename, 7) == 0) {
- overlapstrcpy(*filename, (*filename) + 7);
- }
- *stripped = newStripped;
- return SECSuccess;
- }
- /* entry point */
- char **
- NSS_ReturnModuleSpecData(unsigned long function, char *parameters, void *args)
- {
- char *filename = NULL;
- char *stripped = NULL;
- char **retString = NULL;
- SECStatus rv;
- rv = parse_parameters(parameters, &filename, &stripped);
- if (rv != SECSuccess) {
- /* use defaults */
- filename = getSystemDB();
- if (!filename) {
- return NULL;
- }
- stripped = PORT_Strdup(NSS_DEFAULT_FLAGS);
- if (!stripped) {
- free(filename);
- return NULL;
- }
- }
- switch (function) {
- case SECMOD_MODULE_DB_FUNCTION_FIND:
- retString = get_list(filename, stripped);
- break;
- case SECMOD_MODULE_DB_FUNCTION_RELEASE:
- retString = release_list((char **)args);
- break;
- /* can't add or delete from this module DB */
- case SECMOD_MODULE_DB_FUNCTION_ADD:
- case SECMOD_MODULE_DB_FUNCTION_DEL:
- retString = NULL;
- break;
- default:
- retString = NULL;
- break;
- }
- PORT_Free(filename);
- PORT_Free(stripped);
- return retString;
- }
|