security/nss/lib/sysinit/nsssysinit.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     4 #include "seccomon.h"
     5 #include "prio.h"
     6 #include "prprf.h"
     7 #include "plhash.h"
     9 /*
    10  * The following provides a default example for operating systems to set up
    11  * and manage applications loading NSS on their OS globally.
    12  *
    13  * This code hooks in to the system pkcs11.txt, which controls all the loading
    14  * of pkcs11 modules common to all applications.
    15  */
    17 /*
    18  * OS Specific function to get where the NSS user database should reside.
    19  */
    21 #ifdef XP_UNIX
    22 #include <unistd.h>
    23 #include <sys/stat.h>
    24 #include <sys/types.h>
    26 static int 
    27 testdir(char *dir)
    28 {
    29    struct stat buf;
    30    memset(&buf, 0, sizeof(buf));
    32    if (stat(dir,&buf) < 0) {
    33 	return 0;
    34    }
    36    return S_ISDIR(buf.st_mode);
    37 }
    39 #define NSS_USER_PATH1 "/.pki"
    40 #define NSS_USER_PATH2 "/nssdb"
    41 static char *
    42 getUserDB(void)
    43 {
    44    char *userdir = getenv("HOME");
    45    char *nssdir = NULL;
    47    if (userdir == NULL) {
    48 	return NULL;
    49    }
    51    nssdir = PORT_Alloc(strlen(userdir)
    52 		+sizeof(NSS_USER_PATH1)+sizeof(NSS_USER_PATH2));
    53    if (nssdir == NULL) {
    54 	return NULL;
    55    }
    56    PORT_Strcpy(nssdir, userdir);
    57    /* verify it exists */
    58    if (!testdir(nssdir)) {
    59 	PORT_Free(nssdir);
    60 	return NULL;
    61    }
    62    PORT_Strcat(nssdir, NSS_USER_PATH1);
    63    if (!testdir(nssdir) && mkdir(nssdir, 0760)) {
    64 	PORT_Free(nssdir);
    65 	return NULL;
    66    }
    67    PORT_Strcat(nssdir, NSS_USER_PATH2);
    68    if (!testdir(nssdir) && mkdir(nssdir, 0760)) {
    69 	PORT_Free(nssdir);
    70 	return NULL;
    71    }
    72    return nssdir;
    73 }
    75 #define NSS_DEFAULT_SYSTEM "/etc/pki/nssdb"
    76 static char *
    77 getSystemDB(void) {
    78    return PORT_Strdup(NSS_DEFAULT_SYSTEM);
    79 }
    81 static PRBool
    82 userIsRoot()
    83 {
    84    /* this works for linux and all unixes that we know off
    85 	  though it isn't stated as such in POSIX documentation */
    86    return getuid() == 0;
    87 }
    89 static PRBool
    90 userCanModifySystemDB()
    91 {
    92    return (access(NSS_DEFAULT_SYSTEM, W_OK) == 0);
    93 }
    95 #else
    96 #ifdef XP_WIN
    97 static char *
    98 getUserDB(void)
    99 {
   100    /* use the registry to find the user's NSS_DIR. if no entry exists, create
   101     * one in the users Appdir location */
   102    return NULL;
   103 }
   105 static char *
   106 getSystemDB(void)
   107 {
   108    /* use the registry to find the system's NSS_DIR. if no entry exists, create
   109     * one based on the windows system data area */
   110    return NULL;
   111 }
   113 static PRBool
   114 userIsRoot()
   115 {
   116    /* use the registry to find if the user is the system administrator. */
   117    return PR_FALSE;
   118 }
   120 static PRBool
   121 userCanModifySystemDB()
   122 {
   123    /* use the registry to find if the user has administrative privilege 
   124     * to modify the system's nss database. */
   125    return PR_FALSE;
   126 }
   128 #else
   129 #error "Need to write getUserDB, SystemDB, userIsRoot, and userCanModifySystemDB functions"
   130 #endif
   131 #endif
   133 static PRBool 
   134 getFIPSEnv(void)
   135 {
   136     char *fipsEnv = getenv("NSS_FIPS");
   137     if (!fipsEnv) {
   138 	return PR_FALSE;
   139     }
   140     if ((strcasecmp(fipsEnv,"fips") == 0) ||
   141 	(strcasecmp(fipsEnv,"true") == 0) ||
   142 	(strcasecmp(fipsEnv,"on") == 0) ||
   143 	(strcasecmp(fipsEnv,"1") == 0)) {
   144 	 return PR_TRUE;
   145     }
   146     return PR_FALSE;
   147 }
   148 #ifdef XP_LINUX
   150 static PRBool 
   151 getFIPSMode(void)
   152 {
   153     FILE *f;
   154     char d;
   155     size_t size;
   157     f = fopen("/proc/sys/crypto/fips_enabled", "r");
   158     if (!f) {
   159 	/* if we don't have a proc flag, fall back to the 
   160 	 * environment variable */
   161 	return getFIPSEnv();
   162     }
   164     size = fread(&d, 1, 1, f);
   165     fclose(f);
   166     if (size != 1)
   167         return PR_FALSE;
   168     if (d != '1')
   169         return PR_FALSE;
   170     return PR_TRUE;
   171 }
   173 #else
   174 static PRBool 
   175 getFIPSMode(void)
   176 {
   177     return getFIPSEnv();
   178 }
   179 #endif
   182 #define NSS_DEFAULT_FLAGS "flags=readonly"
   184 /* configuration flags according to
   185  * https://developer.mozilla.org/en/PKCS11_Module_Specs
   186  * As stated there the slotParams start with a slot name which is a slotID
   187  * Slots 1 through 3 are reserved for the nss internal modules as follows:
   188  * 1 for crypto operations slot non-fips,
   189  * 2 for the key slot, and
   190  * 3 for the crypto operations slot fips
   191  */
   192 #define CIPHER_ORDER_FLAGS "cipherOrder=100"
   193 #define SLOT_FLAGS \
   194 	"[slotFlags=RSA,RC4,RC2,DES,DH,SHA1,MD5,MD2,SSL,TLS,AES,RANDOM" \
   195 	" askpw=any timeout=30 ]"
   197 static const char *nssDefaultFlags =
   198 	CIPHER_ORDER_FLAGS " slotParams={0x00000001=" SLOT_FLAGS " }  ";
   200 static const char *nssDefaultFIPSFlags =
   201 	CIPHER_ORDER_FLAGS " slotParams={0x00000003=" SLOT_FLAGS " }  ";
   203 /*
   204  * This function builds the list of databases and modules to load, and sets
   205  * their configuration. For the sample we have a fixed set.
   206  *  1. We load the user's home nss database.
   207  *  2. We load the user's custom PKCS #11 modules.
   208  *  3. We load the system nss database readonly.
   209  *
   210  * Any space allocated in get_list must be freed in release_list.
   211  * This function can use whatever information is available to the application.
   212  * it is running in the process of the application for which it is making 
   213  * decisions, so it's possible to acquire the application name as part of
   214  * the decision making process.
   215  *
   216  */
   217 static char **
   218 get_list(char *filename, char *stripped_parameters)
   219 {
   220     char **module_list = PORT_ZNewArray(char *, 5);
   221     char *userdb, *sysdb;
   222     int isFIPS = getFIPSMode();
   223     const char *nssflags = isFIPS ? nssDefaultFIPSFlags : nssDefaultFlags;
   224     int next = 0;
   226     /* can't get any space */
   227     if (module_list == NULL) {
   228 	return NULL;
   229     }
   231     sysdb = getSystemDB();
   232     userdb = getUserDB();
   234     /* Don't open root's user DB */
   235     if (userdb != NULL && !userIsRoot()) {
   236 	/* return a list of databases to open. First the user Database */
   237 	module_list[next++] = PR_smprintf(
   238 	    "library= "
   239 	    "module=\"NSS User database\" "
   240 	    "parameters=\"configdir='sql:%s' %s tokenDescription='NSS user database'\" "
   241         "NSS=\"trustOrder=75 %sflags=internal%s\"",
   242         userdb, stripped_parameters, nssflags,
   243         isFIPS ? ",FIPS" : "");
   245 	/* now open the user's defined PKCS #11 modules */
   246 	/* skip the local user DB entry */
   247 	module_list[next++] = PR_smprintf(
   248 	    "library= "
   249 	    "module=\"NSS User database\" "
   250 	    "parameters=\"configdir='sql:%s' %s\" "
   251 	    "NSS=\"flags=internal,moduleDBOnly,defaultModDB,skipFirst\"", 
   252 		userdb, stripped_parameters);
   253 	}
   255     /* now the system database (always read only unless it's root) */
   256     if (sysdb) {
   257 	    const char *readonly = userCanModifySystemDB() ? "" : "flags=readonly";
   258 	    module_list[next++] = PR_smprintf(
   259 	      "library= "
   260 	      "module=\"NSS system database\" "
   261 	      "parameters=\"configdir='sql:%s' tokenDescription='NSS system database' %s\" "
   262 	      "NSS=\"trustOrder=80 %sflags=internal,critical\"",sysdb, readonly, nssflags);
   263     }
   265     /* that was the last module */
   266     module_list[next] = 0;
   268     PORT_Free(userdb);
   269     PORT_Free(sysdb);
   271     return module_list;
   272 }
   274 static char **
   275 release_list(char **arg)
   276 {
   277     static char *success = "Success";
   278     int next;
   280     for (next = 0; arg[next]; next++) {
   281 	free(arg[next]);
   282     }
   283     PORT_Free(arg);
   284     return &success;
   285 }
   288 #include "utilpars.h"
   290 #define TARGET_SPEC_COPY(new, start, end)    \
   291   if (end > start) {                         \
   292         int _cnt = end - start;              \
   293         PORT_Memcpy(new, start, _cnt);       \
   294         new += _cnt;                         \
   295   }
   297 /*
   298  * According the strcpy man page:
   299  *
   300  * The strings  may  not overlap, and the destination string dest must be
   301  * large enough to receive the copy.
   302  * 
   303  * This implementation allows target to overlap with src.
   304  * It does not allow the src to overlap the target.
   305  *  example: overlapstrcpy(string, string+4) is fine
   306  *           overlapstrcpy(string+4, string) is not.
   307  */
   308 static void
   309 overlapstrcpy(char *target, char *src)
   310 {
   311     while (*src) {
   312 	*target++ = *src++;
   313     }
   314     *target = 0;
   315 }
   317 /* determine what options the user was trying to open this database with */
   318 /* filename is the directory pointed to by configdir= */
   319 /* stripped is the rest of the parameters with configdir= stripped out */
   320 static SECStatus
   321 parse_parameters(char *parameters, char **filename, char **stripped)
   322 {
   323     char *sourcePrev;
   324     char *sourceCurr;
   325     char *targetCurr;
   326     char *newStripped;
   327     *filename = NULL;
   328     *stripped = NULL;
   330     newStripped = PORT_Alloc(PORT_Strlen(parameters)+2);
   331     targetCurr = newStripped;
   332     sourcePrev = parameters;
   333     sourceCurr = NSSUTIL_ArgStrip(parameters);
   334     TARGET_SPEC_COPY(targetCurr, sourcePrev, sourceCurr);
   336     while (*sourceCurr) {
   337 	int next;
   338 	sourcePrev = sourceCurr;
   339 	NSSUTIL_HANDLE_STRING_ARG(sourceCurr, *filename, "configdir=",
   340 		sourcePrev = sourceCurr; )
   341 	NSSUTIL_HANDLE_FINAL_ARG(sourceCurr);
   342 	TARGET_SPEC_COPY(targetCurr, sourcePrev, sourceCurr);
   343     }
   344     *targetCurr = 0;
   345     if (*filename == NULL) {
   346 	PORT_Free(newStripped);
   347 	return SECFailure;
   348     }
   349     /* strip off any directives from the filename */
   350     if (strncmp("sql:", *filename, 4) == 0) {
   351 	overlapstrcpy(*filename, (*filename)+4);
   352     } else if (strncmp("dbm:", *filename, 4) == 0) {
   353 	overlapstrcpy(*filename, (*filename)+4);
   354     } else if (strncmp("extern:", *filename, 7) == 0) {
   355 	overlapstrcpy(*filename, (*filename)+7);
   356     }
   357     *stripped = newStripped;
   358     return SECSuccess;
   359 }
   361 /* entry point */
   362 char **
   363 NSS_ReturnModuleSpecData(unsigned long function, char *parameters, void *args)
   364 {
   365     char *filename = NULL;
   366     char *stripped = NULL;
   367     char **retString = NULL;
   368     SECStatus rv;
   370     rv = parse_parameters(parameters, &filename, &stripped);
   371     if (rv != SECSuccess) {
   372 	/* use defaults */
   373 	filename = getSystemDB();
   374 	if (!filename) {
   375 	    return NULL;
   376 	}
   377 	stripped = PORT_Strdup(NSS_DEFAULT_FLAGS);
   378 	if (!stripped) {
   379 	    free(filename);
   380 	    return NULL;
   381 	}
   382     }
   383     switch (function) {
   384     case SECMOD_MODULE_DB_FUNCTION_FIND:
   385 	retString = get_list(filename, stripped);
   386 	break;
   387     case SECMOD_MODULE_DB_FUNCTION_RELEASE:
   388 	retString = release_list((char **)args);
   389 	break;
   390     /* can't add or delete from this module DB */
   391     case SECMOD_MODULE_DB_FUNCTION_ADD:
   392     case SECMOD_MODULE_DB_FUNCTION_DEL:
   393 	retString = NULL;
   394 	break;
   395     default:
   396 	retString = NULL;
   397 	break;
   398     }
   400     PORT_Free(filename);
   401     PORT_Free(stripped);
   402     return retString;
   403 }

mercurial