Source code

Revision control

Copy as Markdown

Other Tools

/* 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
* 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;
}