|
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" |
|
8 |
|
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 */ |
|
16 |
|
17 /* |
|
18 * OS Specific function to get where the NSS user database should reside. |
|
19 */ |
|
20 |
|
21 #ifdef XP_UNIX |
|
22 #include <unistd.h> |
|
23 #include <sys/stat.h> |
|
24 #include <sys/types.h> |
|
25 |
|
26 static int |
|
27 testdir(char *dir) |
|
28 { |
|
29 struct stat buf; |
|
30 memset(&buf, 0, sizeof(buf)); |
|
31 |
|
32 if (stat(dir,&buf) < 0) { |
|
33 return 0; |
|
34 } |
|
35 |
|
36 return S_ISDIR(buf.st_mode); |
|
37 } |
|
38 |
|
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; |
|
46 |
|
47 if (userdir == NULL) { |
|
48 return NULL; |
|
49 } |
|
50 |
|
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 } |
|
74 |
|
75 #define NSS_DEFAULT_SYSTEM "/etc/pki/nssdb" |
|
76 static char * |
|
77 getSystemDB(void) { |
|
78 return PORT_Strdup(NSS_DEFAULT_SYSTEM); |
|
79 } |
|
80 |
|
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 } |
|
88 |
|
89 static PRBool |
|
90 userCanModifySystemDB() |
|
91 { |
|
92 return (access(NSS_DEFAULT_SYSTEM, W_OK) == 0); |
|
93 } |
|
94 |
|
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 } |
|
104 |
|
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 } |
|
112 |
|
113 static PRBool |
|
114 userIsRoot() |
|
115 { |
|
116 /* use the registry to find if the user is the system administrator. */ |
|
117 return PR_FALSE; |
|
118 } |
|
119 |
|
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 } |
|
127 |
|
128 #else |
|
129 #error "Need to write getUserDB, SystemDB, userIsRoot, and userCanModifySystemDB functions" |
|
130 #endif |
|
131 #endif |
|
132 |
|
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 |
|
149 |
|
150 static PRBool |
|
151 getFIPSMode(void) |
|
152 { |
|
153 FILE *f; |
|
154 char d; |
|
155 size_t size; |
|
156 |
|
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 } |
|
163 |
|
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 } |
|
172 |
|
173 #else |
|
174 static PRBool |
|
175 getFIPSMode(void) |
|
176 { |
|
177 return getFIPSEnv(); |
|
178 } |
|
179 #endif |
|
180 |
|
181 |
|
182 #define NSS_DEFAULT_FLAGS "flags=readonly" |
|
183 |
|
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 ]" |
|
196 |
|
197 static const char *nssDefaultFlags = |
|
198 CIPHER_ORDER_FLAGS " slotParams={0x00000001=" SLOT_FLAGS " } "; |
|
199 |
|
200 static const char *nssDefaultFIPSFlags = |
|
201 CIPHER_ORDER_FLAGS " slotParams={0x00000003=" SLOT_FLAGS " } "; |
|
202 |
|
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; |
|
225 |
|
226 /* can't get any space */ |
|
227 if (module_list == NULL) { |
|
228 return NULL; |
|
229 } |
|
230 |
|
231 sysdb = getSystemDB(); |
|
232 userdb = getUserDB(); |
|
233 |
|
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" : ""); |
|
244 |
|
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 } |
|
254 |
|
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 } |
|
264 |
|
265 /* that was the last module */ |
|
266 module_list[next] = 0; |
|
267 |
|
268 PORT_Free(userdb); |
|
269 PORT_Free(sysdb); |
|
270 |
|
271 return module_list; |
|
272 } |
|
273 |
|
274 static char ** |
|
275 release_list(char **arg) |
|
276 { |
|
277 static char *success = "Success"; |
|
278 int next; |
|
279 |
|
280 for (next = 0; arg[next]; next++) { |
|
281 free(arg[next]); |
|
282 } |
|
283 PORT_Free(arg); |
|
284 return &success; |
|
285 } |
|
286 |
|
287 |
|
288 #include "utilpars.h" |
|
289 |
|
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 } |
|
296 |
|
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 } |
|
316 |
|
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; |
|
329 |
|
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); |
|
335 |
|
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 } |
|
360 |
|
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; |
|
369 |
|
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 } |
|
399 |
|
400 PORT_Free(filename); |
|
401 PORT_Free(stripped); |
|
402 return retString; |
|
403 } |