Thu, 22 Jan 2015 13:21:57 +0100
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 }