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