/src/nss-nspr/nss/lib/nss/nssinit.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * NSS utility functions |
3 | | * |
4 | | * This Source Code Form is subject to the terms of the Mozilla Public |
5 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
6 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
7 | | |
8 | | #include <ctype.h> |
9 | | #include <string.h> |
10 | | #include "seccomon.h" |
11 | | #include "prinit.h" |
12 | | #include "prprf.h" |
13 | | #include "prmem.h" |
14 | | #include "cert.h" |
15 | | #include "keyhi.h" |
16 | | #include "secmod.h" |
17 | | #include "secoid.h" |
18 | | #include "nss.h" |
19 | | #include "pk11func.h" |
20 | | #include "secerr.h" |
21 | | #include "nssbase.h" |
22 | | #include "nssutil.h" |
23 | | |
24 | | #ifndef NSS_DISABLE_LIBPKIX |
25 | | #include "pkixt.h" |
26 | | #include "pkix.h" |
27 | | #include "pkix_tools.h" |
28 | | #endif /* NSS_DISABLE_LIBPKIX */ |
29 | | |
30 | | #include "pki3hack.h" |
31 | | #include "certi.h" |
32 | | #include "secmodi.h" |
33 | | #include "ocspti.h" |
34 | | #include "ocspi.h" |
35 | | #include "utilpars.h" |
36 | | |
37 | | /* |
38 | | * On Windows nss3.dll needs to export the symbol 'mktemp' to be |
39 | | * fully backward compatible with the nss3.dll in NSS 3.2.x and |
40 | | * 3.3.x. This symbol was unintentionally exported and its |
41 | | * definition (in DBM) was moved from nss3.dll to softokn3.dll |
42 | | * in NSS 3.4. See bug 142575. |
43 | | */ |
44 | | #ifdef WIN32_NSS3_DLL_COMPAT |
45 | | #include <io.h> |
46 | | |
47 | | /* exported as 'mktemp' */ |
48 | | char * |
49 | | nss_mktemp(char *path) |
50 | | { |
51 | | return _mktemp(path); |
52 | | } |
53 | | #endif |
54 | | |
55 | 4 | #define NSS_MAX_FLAG_SIZE sizeof("readOnly") + sizeof("noCertDB") + \ |
56 | 4 | sizeof("noModDB") + sizeof("forceOpen") + sizeof("passwordRequired") + \ |
57 | 4 | sizeof("optimizeSpace") + sizeof("printPolicyFeedback") |
58 | 2 | #define NSS_DEFAULT_MOD_NAME "NSS Internal Module" |
59 | | |
60 | | static char * |
61 | | nss_makeFlags(PRBool readOnly, PRBool noCertDB, |
62 | | PRBool noModDB, PRBool forceOpen, |
63 | | PRBool passwordRequired, PRBool optimizeSpace) |
64 | 2 | { |
65 | 2 | char *flags = (char *)PORT_Alloc(NSS_MAX_FLAG_SIZE); |
66 | 2 | PRBool first = PR_TRUE; |
67 | | |
68 | 2 | PORT_Memset(flags, 0, NSS_MAX_FLAG_SIZE); |
69 | 2 | if (readOnly) { |
70 | 2 | PORT_Strcat(flags, "readOnly"); |
71 | 2 | first = PR_FALSE; |
72 | 2 | } |
73 | 2 | if (noCertDB) { |
74 | 2 | if (!first) |
75 | 2 | PORT_Strcat(flags, ","); |
76 | 2 | PORT_Strcat(flags, "noCertDB"); |
77 | 2 | first = PR_FALSE; |
78 | 2 | } |
79 | 2 | if (noModDB) { |
80 | 2 | if (!first) |
81 | 2 | PORT_Strcat(flags, ","); |
82 | 2 | PORT_Strcat(flags, "noModDB"); |
83 | 2 | first = PR_FALSE; |
84 | 2 | } |
85 | 2 | if (forceOpen) { |
86 | 2 | if (!first) |
87 | 2 | PORT_Strcat(flags, ","); |
88 | 2 | PORT_Strcat(flags, "forceOpen"); |
89 | 2 | first = PR_FALSE; |
90 | 2 | } |
91 | 2 | if (passwordRequired) { |
92 | 0 | if (!first) |
93 | 0 | PORT_Strcat(flags, ","); |
94 | 0 | PORT_Strcat(flags, "passwordRequired"); |
95 | 0 | first = PR_FALSE; |
96 | 0 | } |
97 | 2 | if (optimizeSpace) { |
98 | 2 | if (!first) |
99 | 2 | PORT_Strcat(flags, ","); |
100 | 2 | PORT_Strcat(flags, "optimizeSpace"); |
101 | 2 | } |
102 | 2 | return flags; |
103 | 2 | } |
104 | | |
105 | | /* |
106 | | * build config string from individual internationalized strings |
107 | | */ |
108 | | char * |
109 | | nss_MkConfigString(const char *man, const char *libdesc, const char *tokdesc, |
110 | | const char *ptokdesc, const char *slotdesc, const char *pslotdesc, |
111 | | const char *fslotdesc, const char *fpslotdesc, int minPwd) |
112 | 0 | { |
113 | 0 | char *strings = NULL; |
114 | 0 | char *newStrings; |
115 | | |
116 | | /* make sure the internationalization was done correctly... */ |
117 | 0 | strings = PR_smprintf(""); |
118 | 0 | if (strings == NULL) |
119 | 0 | return NULL; |
120 | | |
121 | 0 | if (man) { |
122 | 0 | newStrings = PR_smprintf("%s manufacturerID='%s'", strings, man); |
123 | 0 | PR_smprintf_free(strings); |
124 | 0 | strings = newStrings; |
125 | 0 | } |
126 | 0 | if (strings == NULL) |
127 | 0 | return NULL; |
128 | | |
129 | 0 | if (libdesc) { |
130 | 0 | newStrings = PR_smprintf("%s libraryDescription='%s'", strings, libdesc); |
131 | 0 | PR_smprintf_free(strings); |
132 | 0 | strings = newStrings; |
133 | 0 | } |
134 | 0 | if (strings == NULL) |
135 | 0 | return NULL; |
136 | | |
137 | 0 | if (tokdesc) { |
138 | 0 | newStrings = PR_smprintf("%s cryptoTokenDescription='%s'", strings, |
139 | 0 | tokdesc); |
140 | 0 | PR_smprintf_free(strings); |
141 | 0 | strings = newStrings; |
142 | 0 | } |
143 | 0 | if (strings == NULL) |
144 | 0 | return NULL; |
145 | | |
146 | 0 | if (ptokdesc) { |
147 | 0 | newStrings = PR_smprintf("%s dbTokenDescription='%s'", strings, ptokdesc); |
148 | 0 | PR_smprintf_free(strings); |
149 | 0 | strings = newStrings; |
150 | 0 | } |
151 | 0 | if (strings == NULL) |
152 | 0 | return NULL; |
153 | | |
154 | 0 | if (slotdesc) { |
155 | 0 | newStrings = PR_smprintf("%s cryptoSlotDescription='%s'", strings, |
156 | 0 | slotdesc); |
157 | 0 | PR_smprintf_free(strings); |
158 | 0 | strings = newStrings; |
159 | 0 | } |
160 | 0 | if (strings == NULL) |
161 | 0 | return NULL; |
162 | | |
163 | 0 | if (pslotdesc) { |
164 | 0 | newStrings = PR_smprintf("%s dbSlotDescription='%s'", strings, pslotdesc); |
165 | 0 | PR_smprintf_free(strings); |
166 | 0 | strings = newStrings; |
167 | 0 | } |
168 | 0 | if (strings == NULL) |
169 | 0 | return NULL; |
170 | | |
171 | 0 | if (fslotdesc) { |
172 | 0 | newStrings = PR_smprintf("%s FIPSSlotDescription='%s'", |
173 | 0 | strings, fslotdesc); |
174 | 0 | PR_smprintf_free(strings); |
175 | 0 | strings = newStrings; |
176 | 0 | } |
177 | 0 | if (strings == NULL) |
178 | 0 | return NULL; |
179 | | |
180 | 0 | if (fpslotdesc) { |
181 | 0 | newStrings = PR_smprintf("%s FIPSTokenDescription='%s'", |
182 | 0 | strings, fpslotdesc); |
183 | 0 | PR_smprintf_free(strings); |
184 | 0 | strings = newStrings; |
185 | 0 | } |
186 | 0 | if (strings == NULL) |
187 | 0 | return NULL; |
188 | | |
189 | 0 | newStrings = PR_smprintf("%s minPS=%d", strings, minPwd); |
190 | 0 | PR_smprintf_free(strings); |
191 | 0 | strings = newStrings; |
192 | |
|
193 | 0 | return (strings); |
194 | 0 | } |
195 | | |
196 | | /* |
197 | | * statics to remember the PK11_ConfigurePKCS11() |
198 | | * info. |
199 | | */ |
200 | | static char *pk11_config_strings = NULL; |
201 | | static char *pk11_config_name = NULL; |
202 | | static PRBool pk11_password_required = PR_FALSE; |
203 | | |
204 | | /* |
205 | | * this is a legacy configuration function which used to be part of |
206 | | * the PKCS #11 internal token. |
207 | | */ |
208 | | void |
209 | | PK11_ConfigurePKCS11(const char *man, const char *libdesc, const char *tokdesc, |
210 | | const char *ptokdesc, const char *slotdesc, const char *pslotdesc, |
211 | | const char *fslotdesc, const char *fpslotdesc, int minPwd, |
212 | | int pwRequired) |
213 | 0 | { |
214 | 0 | char *strings; |
215 | |
|
216 | 0 | strings = nss_MkConfigString(man, libdesc, tokdesc, ptokdesc, slotdesc, |
217 | 0 | pslotdesc, fslotdesc, fpslotdesc, minPwd); |
218 | 0 | if (strings == NULL) { |
219 | 0 | return; |
220 | 0 | } |
221 | | |
222 | 0 | if (libdesc) { |
223 | 0 | if (pk11_config_name != NULL) { |
224 | 0 | PORT_Free(pk11_config_name); |
225 | 0 | } |
226 | 0 | pk11_config_name = PORT_Strdup(libdesc); |
227 | 0 | } |
228 | |
|
229 | 0 | if (pk11_config_strings != NULL) { |
230 | 0 | PR_smprintf_free(pk11_config_strings); |
231 | 0 | } |
232 | 0 | pk11_config_strings = strings; |
233 | 0 | pk11_password_required = pwRequired; |
234 | |
|
235 | 0 | return; |
236 | 0 | } |
237 | | |
238 | | void |
239 | | PK11_UnconfigurePKCS11(void) |
240 | 0 | { |
241 | 0 | if (pk11_config_strings != NULL) { |
242 | 0 | PR_smprintf_free(pk11_config_strings); |
243 | 0 | pk11_config_strings = NULL; |
244 | 0 | } |
245 | 0 | if (pk11_config_name) { |
246 | 0 | PORT_Free(pk11_config_name); |
247 | 0 | pk11_config_name = NULL; |
248 | 0 | } |
249 | 0 | } |
250 | | |
251 | | /* |
252 | | * The following code is an attempt to automagically find the external root |
253 | | * module. |
254 | | * Note: Keep the #if-defined chunks in order. HPUX must select before UNIX. |
255 | | */ |
256 | | |
257 | | static const char *dllname = |
258 | | #if defined(XP_WIN32) |
259 | | "nssckbi.dll"; |
260 | | #elif defined(HPUX) && !defined(__ia64) /* HP-UX PA-RISC */ |
261 | | "libnssckbi.sl"; |
262 | | #elif defined(DARWIN) |
263 | | "libnssckbi.dylib"; |
264 | | #elif defined(XP_UNIX) |
265 | | "libnssckbi.so"; |
266 | | #else |
267 | | #error "Uh! Oh! I don't know about this platform." |
268 | | #endif |
269 | | |
270 | | /* Should we have platform ifdefs here??? */ |
271 | 0 | #define FILE_SEP '/' |
272 | | |
273 | | static void |
274 | | nss_FindExternalRootPaths(const char *dbpath, |
275 | | const char *secmodprefix, |
276 | | char **retoldpath, char **retnewpath) |
277 | 0 | { |
278 | 0 | char *path, *oldpath = NULL, *lastsep; |
279 | 0 | int len, path_len, secmod_len, dll_len; |
280 | |
|
281 | 0 | path_len = PORT_Strlen(dbpath); |
282 | 0 | secmod_len = secmodprefix ? PORT_Strlen(secmodprefix) : 0; |
283 | 0 | dll_len = PORT_Strlen(dllname); |
284 | 0 | len = path_len + secmod_len + dll_len + 2; /* FILE_SEP + NULL */ |
285 | |
|
286 | 0 | path = PORT_Alloc(len); |
287 | 0 | if (path == NULL) |
288 | 0 | return; |
289 | | |
290 | | /* back up to the top of the directory */ |
291 | 0 | PORT_Memcpy(path, dbpath, path_len); |
292 | 0 | if (path[path_len - 1] != FILE_SEP) { |
293 | 0 | path[path_len++] = FILE_SEP; |
294 | 0 | } |
295 | 0 | PORT_Strcpy(&path[path_len], dllname); |
296 | 0 | if (secmod_len > 0) { |
297 | 0 | lastsep = PORT_Strrchr(secmodprefix, FILE_SEP); |
298 | 0 | if (lastsep) { |
299 | 0 | int secmoddir_len = lastsep - secmodprefix + 1; /* FILE_SEP */ |
300 | 0 | oldpath = PORT_Alloc(len); |
301 | 0 | if (oldpath == NULL) { |
302 | 0 | PORT_Free(path); |
303 | 0 | return; |
304 | 0 | } |
305 | 0 | PORT_Memcpy(oldpath, path, path_len); |
306 | 0 | PORT_Memcpy(&oldpath[path_len], secmodprefix, secmoddir_len); |
307 | 0 | PORT_Strcpy(&oldpath[path_len + secmoddir_len], dllname); |
308 | 0 | } |
309 | 0 | } |
310 | 0 | *retoldpath = oldpath; |
311 | 0 | *retnewpath = path; |
312 | 0 | return; |
313 | 0 | } |
314 | | |
315 | | static void |
316 | | nss_FreeExternalRootPaths(char *oldpath, char *path) |
317 | 0 | { |
318 | 0 | if (path) { |
319 | 0 | PORT_Free(path); |
320 | 0 | } |
321 | 0 | if (oldpath) { |
322 | 0 | PORT_Free(oldpath); |
323 | 0 | } |
324 | 0 | } |
325 | | |
326 | | static void |
327 | | nss_FindExternalRoot(const char *dbpath, const char *secmodprefix) |
328 | 0 | { |
329 | 0 | char *path = NULL; |
330 | 0 | char *oldpath = NULL; |
331 | 0 | PRBool hasrootcerts = PR_FALSE; |
332 | | |
333 | | /* |
334 | | * 'oldpath' is the external root path in NSS 3.3.x or older. |
335 | | * For backward compatibility we try to load the root certs |
336 | | * module with the old path first. |
337 | | */ |
338 | 0 | nss_FindExternalRootPaths(dbpath, secmodprefix, &oldpath, &path); |
339 | 0 | if (oldpath) { |
340 | 0 | (void)SECMOD_AddNewModule("Root Certs", oldpath, 0, 0); |
341 | 0 | hasrootcerts = SECMOD_HasRootCerts(); |
342 | 0 | } |
343 | 0 | if (path && !hasrootcerts) { |
344 | 0 | (void)SECMOD_AddNewModule("Root Certs", path, 0, 0); |
345 | 0 | } |
346 | 0 | nss_FreeExternalRootPaths(oldpath, path); |
347 | 0 | return; |
348 | 0 | } |
349 | | |
350 | | /* |
351 | | * see nss_Init for definitions of the various options. |
352 | | * |
353 | | * this function builds a moduleSpec string from the options and previously |
354 | | * set statics (from PKCS11_Configure, for instance), and uses it to kick off |
355 | | * the loading of the various PKCS #11 modules. |
356 | | */ |
357 | | static SECMODModule * |
358 | | nss_InitModules(const char *configdir, const char *certPrefix, |
359 | | const char *keyPrefix, const char *secmodName, |
360 | | const char *updateDir, const char *updCertPrefix, |
361 | | const char *updKeyPrefix, const char *updateID, |
362 | | const char *updateName, char *configName, char *configStrings, |
363 | | PRBool pwRequired, PRBool readOnly, PRBool noCertDB, |
364 | | PRBool noModDB, PRBool forceOpen, PRBool optimizeSpace, |
365 | | PRBool isContextInit) |
366 | 2 | { |
367 | 2 | SECMODModule *module = NULL; |
368 | 2 | char *moduleSpec = NULL; |
369 | 2 | char *flags = NULL; |
370 | 2 | char *lconfigdir = NULL; |
371 | 2 | char *lcertPrefix = NULL; |
372 | 2 | char *lkeyPrefix = NULL; |
373 | 2 | char *lsecmodName = NULL; |
374 | 2 | char *lupdateDir = NULL; |
375 | 2 | char *lupdCertPrefix = NULL; |
376 | 2 | char *lupdKeyPrefix = NULL; |
377 | 2 | char *lupdateID = NULL; |
378 | 2 | char *lupdateName = NULL; |
379 | | |
380 | 2 | if (NSS_InitializePRErrorTable() != SECSuccess) { |
381 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
382 | 0 | return NULL; |
383 | 0 | } |
384 | | |
385 | 2 | flags = nss_makeFlags(readOnly, noCertDB, noModDB, forceOpen, |
386 | 2 | pwRequired, optimizeSpace); |
387 | 2 | if (flags == NULL) |
388 | 0 | return NULL; |
389 | | |
390 | | /* |
391 | | * configdir is double nested, and Windows uses the same character |
392 | | * for file seps as we use for escapes! (sigh). |
393 | | */ |
394 | 2 | lconfigdir = NSSUTIL_DoubleEscape(configdir, '\'', '\"'); |
395 | 2 | if (lconfigdir == NULL) { |
396 | 0 | goto loser; |
397 | 0 | } |
398 | 2 | lcertPrefix = NSSUTIL_DoubleEscape(certPrefix, '\'', '\"'); |
399 | 2 | if (lcertPrefix == NULL) { |
400 | 0 | goto loser; |
401 | 0 | } |
402 | 2 | lkeyPrefix = NSSUTIL_DoubleEscape(keyPrefix, '\'', '\"'); |
403 | 2 | if (lkeyPrefix == NULL) { |
404 | 0 | goto loser; |
405 | 0 | } |
406 | 2 | lsecmodName = NSSUTIL_DoubleEscape(secmodName, '\'', '\"'); |
407 | 2 | if (lsecmodName == NULL) { |
408 | 0 | goto loser; |
409 | 0 | } |
410 | 2 | lupdateDir = NSSUTIL_DoubleEscape(updateDir, '\'', '\"'); |
411 | 2 | if (lupdateDir == NULL) { |
412 | 0 | goto loser; |
413 | 0 | } |
414 | 2 | lupdCertPrefix = NSSUTIL_DoubleEscape(updCertPrefix, '\'', '\"'); |
415 | 2 | if (lupdCertPrefix == NULL) { |
416 | 0 | goto loser; |
417 | 0 | } |
418 | 2 | lupdKeyPrefix = NSSUTIL_DoubleEscape(updKeyPrefix, '\'', '\"'); |
419 | 2 | if (lupdKeyPrefix == NULL) { |
420 | 0 | goto loser; |
421 | 0 | } |
422 | 2 | lupdateID = NSSUTIL_DoubleEscape(updateID, '\'', '\"'); |
423 | 2 | if (lupdateID == NULL) { |
424 | 0 | goto loser; |
425 | 0 | } |
426 | 2 | lupdateName = NSSUTIL_DoubleEscape(updateName, '\'', '\"'); |
427 | 2 | if (lupdateName == NULL) { |
428 | 0 | goto loser; |
429 | 0 | } |
430 | | |
431 | 2 | moduleSpec = PR_smprintf( |
432 | 2 | "name=\"%s\" parameters=\"configdir='%s' certPrefix='%s' keyPrefix='%s' " |
433 | 2 | "secmod='%s' flags=%s updatedir='%s' updateCertPrefix='%s' " |
434 | 2 | "updateKeyPrefix='%s' updateid='%s' updateTokenDescription='%s' %s\" " |
435 | 2 | "NSS=\"flags=internal,moduleDB,moduleDBOnly,critical%s\"", |
436 | 2 | configName ? configName : NSS_DEFAULT_MOD_NAME, |
437 | 2 | lconfigdir, lcertPrefix, lkeyPrefix, lsecmodName, flags, |
438 | 2 | lupdateDir, lupdCertPrefix, lupdKeyPrefix, lupdateID, |
439 | 2 | lupdateName, configStrings ? configStrings : "", |
440 | 2 | isContextInit ? "" : ",defaultModDB,internalKeySlot"); |
441 | | |
442 | 2 | loser: |
443 | 2 | PORT_Free(flags); |
444 | 2 | if (lconfigdir) |
445 | 2 | PORT_Free(lconfigdir); |
446 | 2 | if (lcertPrefix) |
447 | 2 | PORT_Free(lcertPrefix); |
448 | 2 | if (lkeyPrefix) |
449 | 2 | PORT_Free(lkeyPrefix); |
450 | 2 | if (lsecmodName) |
451 | 2 | PORT_Free(lsecmodName); |
452 | 2 | if (lupdateDir) |
453 | 2 | PORT_Free(lupdateDir); |
454 | 2 | if (lupdCertPrefix) |
455 | 2 | PORT_Free(lupdCertPrefix); |
456 | 2 | if (lupdKeyPrefix) |
457 | 2 | PORT_Free(lupdKeyPrefix); |
458 | 2 | if (lupdateID) |
459 | 2 | PORT_Free(lupdateID); |
460 | 2 | if (lupdateName) |
461 | 2 | PORT_Free(lupdateName); |
462 | | |
463 | 2 | if (moduleSpec) { |
464 | 2 | module = SECMOD_LoadModule(moduleSpec, NULL, PR_TRUE); |
465 | 2 | PR_smprintf_free(moduleSpec); |
466 | 2 | if (module && !module->loaded) { |
467 | 0 | SECMOD_DestroyModule(module); |
468 | 0 | return NULL; |
469 | 0 | } |
470 | 2 | } |
471 | 2 | return module; |
472 | 2 | } |
473 | | |
474 | | /* |
475 | | * OK there are now lots of options here, lets go through them all: |
476 | | * |
477 | | * configdir - base directory where all the cert, key, and module datbases live. |
478 | | * certPrefix - prefix added to the beginning of the cert database example: " |
479 | | * "https-server1-" |
480 | | * keyPrefix - prefix added to the beginning of the key database example: " |
481 | | * "https-server1-" |
482 | | * secmodName - name of the security module database (usually "secmod.db"). |
483 | | * updateDir - used in initMerge, old directory to update from. |
484 | | * updateID - used in initMerge, unique ID to represent the updated directory. |
485 | | * updateName - used in initMerge, token name when updating. |
486 | | * initContextPtr - used in initContext, pointer to return a unique context |
487 | | * value. |
488 | | * readOnly - Boolean: true if the databases are to be opened read only. |
489 | | * nocertdb - Don't open the cert DB and key DB's, just initialize the |
490 | | * Volatile certdb. |
491 | | * nomoddb - Don't open the security module DB, just initialize the |
492 | | * PKCS #11 module. |
493 | | * forceOpen - Continue to force initializations even if the databases cannot |
494 | | * be opened. |
495 | | * noRootInit - don't try to automatically load the root cert store if one is |
496 | | * not found. |
497 | | * optimizeSpace - tell NSS to use fewer hash table buckets. |
498 | | * |
499 | | * The next three options are used in an attempt to share PKCS #11 modules |
500 | | * with other loaded, running libraries. PKCS #11 was not designed with this |
501 | | * sort of sharing in mind, so use of these options may lead to questionable |
502 | | * results. These options are may be incompatible with NSS_LoadContext() calls. |
503 | | * |
504 | | * noSingleThreadedModules - don't load modules that are not thread safe (many |
505 | | * smart card tokens will not work). |
506 | | * allowAlreadyInitializedModules - if a module has already been loaded and |
507 | | * initialize try to use it. |
508 | | * don'tFinalizeModules - dont shutdown modules we may have loaded. |
509 | | */ |
510 | | |
511 | | static PRBool nssIsInitted = PR_FALSE; |
512 | | static NSSInitContext *nssInitContextList = NULL; |
513 | | |
514 | | #ifndef NSS_DISABLE_LIBPKIX |
515 | | static void *plContext = NULL; |
516 | | #endif /* NSS_DISABLE_LIBPKIX */ |
517 | | |
518 | | struct NSSInitContextStr { |
519 | | NSSInitContext *next; |
520 | | PRUint32 magic; |
521 | | }; |
522 | | |
523 | 0 | #define NSS_INIT_MAGIC 0x1413A91C |
524 | | static SECStatus nss_InitShutdownList(void); |
525 | | |
526 | | /* All initialized to zero in BSS */ |
527 | | static PRCallOnceType nssInitOnce; |
528 | | static PZLock *nssInitLock; |
529 | | static PZCondVar *nssInitCondition; |
530 | | static int nssIsInInit; |
531 | | |
532 | | static PRStatus |
533 | | nss_doLockInit(void) |
534 | 2 | { |
535 | 2 | nssInitLock = PZ_NewLock(nssILockOther); |
536 | 2 | if (nssInitLock == NULL) { |
537 | 0 | return PR_FAILURE; |
538 | 0 | } |
539 | 2 | nssInitCondition = PZ_NewCondVar(nssInitLock); |
540 | 2 | if (nssInitCondition == NULL) { |
541 | 0 | return PR_FAILURE; |
542 | 0 | } |
543 | 2 | return PR_SUCCESS; |
544 | 2 | } |
545 | | |
546 | | static SECStatus |
547 | | nss_Init(const char *configdir, const char *certPrefix, const char *keyPrefix, |
548 | | const char *secmodName, const char *updateDir, |
549 | | const char *updCertPrefix, const char *updKeyPrefix, |
550 | | const char *updateID, const char *updateName, |
551 | | NSSInitContext **initContextPtr, |
552 | | NSSInitParameters *initParams, |
553 | | PRBool readOnly, PRBool noCertDB, |
554 | | PRBool noModDB, PRBool forceOpen, PRBool noRootInit, |
555 | | PRBool optimizeSpace, PRBool noSingleThreadedModules, |
556 | | PRBool allowAlreadyInitializedModules, |
557 | | PRBool dontFinalizeModules) |
558 | 2 | { |
559 | 2 | SECMODModule *parent = NULL; |
560 | | #ifndef NSS_DISABLE_LIBPKIX |
561 | | PKIX_UInt32 actualMinorVersion = 0; |
562 | | PKIX_Error *pkixError = NULL; |
563 | | #endif /* NSS_DISABLE_LIBPKIX */ |
564 | 2 | PRBool isReallyInitted; |
565 | 2 | char *configStrings = NULL; |
566 | 2 | char *configName = NULL; |
567 | 2 | PRBool passwordRequired = PR_FALSE; |
568 | | #ifdef POLICY_FILE |
569 | | char *ignoreVar; |
570 | | #endif |
571 | | |
572 | | /* if we are trying to init with a traditional NSS_Init call, maintain |
573 | | * the traditional idempotent behavior. */ |
574 | 2 | if (!initContextPtr && nssIsInitted) { |
575 | 0 | return SECSuccess; |
576 | 0 | } |
577 | | |
578 | | /* make sure our lock and condition variable are initialized one and only |
579 | | * one time */ |
580 | 2 | if (PR_CallOnce(&nssInitOnce, nss_doLockInit) != PR_SUCCESS) { |
581 | 0 | return SECFailure; |
582 | 0 | } |
583 | | |
584 | | /* |
585 | | * if we haven't done basic initialization, single thread the |
586 | | * initializations. |
587 | | */ |
588 | 2 | PZ_Lock(nssInitLock); |
589 | 2 | isReallyInitted = NSS_IsInitialized(); |
590 | 2 | if (!isReallyInitted) { |
591 | 2 | while (!isReallyInitted && nssIsInInit) { |
592 | 0 | PZ_WaitCondVar(nssInitCondition, PR_INTERVAL_NO_TIMEOUT); |
593 | 0 | isReallyInitted = NSS_IsInitialized(); |
594 | 0 | } |
595 | | /* once we've completed basic initialization, we can allow more than |
596 | | * one process initialize NSS at a time. */ |
597 | 2 | } |
598 | 2 | nssIsInInit++; |
599 | 2 | PZ_Unlock(nssInitLock); |
600 | | |
601 | | /* this tells us whether or not some library has already initialized us. |
602 | | * if so, we don't want to double call some of the basic initialization |
603 | | * functions */ |
604 | | |
605 | 2 | if (!isReallyInitted) { |
606 | 2 | #ifdef DEBUG |
607 | 2 | CERTCertificate dummyCert; |
608 | | /* New option bits must not change the size of CERTCertificate. */ |
609 | 2 | PORT_Assert(sizeof(dummyCert.options) == sizeof(void *)); |
610 | 2 | #endif |
611 | | |
612 | 2 | if (SECSuccess != cert_InitLocks()) { |
613 | 0 | goto loser; |
614 | 0 | } |
615 | | |
616 | 2 | if (SECSuccess != InitCRLCache()) { |
617 | 0 | goto loser; |
618 | 0 | } |
619 | | |
620 | 2 | if (SECSuccess != OCSP_InitGlobal()) { |
621 | 0 | goto loser; |
622 | 0 | } |
623 | 2 | } |
624 | | |
625 | 2 | if (noSingleThreadedModules || allowAlreadyInitializedModules || |
626 | 2 | dontFinalizeModules) { |
627 | 0 | pk11_setGlobalOptions(noSingleThreadedModules, |
628 | 0 | allowAlreadyInitializedModules, |
629 | 0 | dontFinalizeModules); |
630 | 0 | } |
631 | | |
632 | 2 | if (initContextPtr) { |
633 | 0 | *initContextPtr = PORT_ZNew(NSSInitContext); |
634 | 0 | if (*initContextPtr == NULL) { |
635 | 0 | goto loser; |
636 | 0 | } |
637 | | /* |
638 | | * For traditional NSS_Init, we used the PK11_Configure() call to set |
639 | | * globals. with InitContext, we pass those strings in as parameters. |
640 | | * |
641 | | * This allows old NSS_Init calls to work as before, while at the same |
642 | | * time new calls and old calls will not interfere with each other. |
643 | | */ |
644 | 0 | if (initParams) { |
645 | 0 | if (initParams->length < sizeof(NSSInitParameters)) { |
646 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
647 | 0 | goto loser; |
648 | 0 | } |
649 | 0 | configStrings = nss_MkConfigString(initParams->manufactureID, |
650 | 0 | initParams->libraryDescription, |
651 | 0 | initParams->cryptoTokenDescription, |
652 | 0 | initParams->dbTokenDescription, |
653 | 0 | initParams->cryptoSlotDescription, |
654 | 0 | initParams->dbSlotDescription, |
655 | 0 | initParams->FIPSSlotDescription, |
656 | 0 | initParams->FIPSTokenDescription, |
657 | 0 | initParams->minPWLen); |
658 | 0 | if (configStrings == NULL) { |
659 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
660 | 0 | goto loser; |
661 | 0 | } |
662 | 0 | configName = initParams->libraryDescription; |
663 | 0 | passwordRequired = initParams->passwordRequired; |
664 | 0 | } |
665 | | |
666 | | /* If we're NSS_ContextInit, we're probably a library. It could be |
667 | | * possible that the application initialized NSS then forked(). The |
668 | | * library would have no knowledge of that. If we call |
669 | | * SECMOD_RestartModules() here, we will be able to continue on with |
670 | | * NSS as normal. SECMOD_RestartModules() does have the side affect |
671 | | * of losing all our PKCS #11 objects in the new process, but only if |
672 | | * the module needs to be reinited. If it needs to be reinit those |
673 | | * objects are inaccessible anyway, it's always save to call |
674 | | * SECMOD_RestartModules(PR_FALSE). |
675 | | */ |
676 | | /* NOTE: We could call SECMOD_Init() here, but if we aren't already |
677 | | * inited, then there's no modules to restart, so SECMOD_RestartModules |
678 | | * will return immediately */ |
679 | 0 | SECMOD_RestartModules(PR_FALSE); |
680 | 2 | } else { |
681 | 2 | configStrings = pk11_config_strings; |
682 | 2 | configName = pk11_config_name; |
683 | 2 | passwordRequired = pk11_password_required; |
684 | 2 | } |
685 | | |
686 | | /* Skip the module init if we are already initted and we are trying |
687 | | * to init with noCertDB and noModDB */ |
688 | 2 | if (!(isReallyInitted && noCertDB && noModDB)) { |
689 | 2 | parent = nss_InitModules(configdir, certPrefix, keyPrefix, secmodName, |
690 | 2 | updateDir, updCertPrefix, updKeyPrefix, updateID, |
691 | 2 | updateName, configName, configStrings, passwordRequired, |
692 | 2 | readOnly, noCertDB, noModDB, forceOpen, optimizeSpace, |
693 | 2 | (initContextPtr != NULL)); |
694 | | |
695 | 2 | if (parent == NULL) { |
696 | 0 | goto loser; |
697 | 0 | } |
698 | 2 | } |
699 | | |
700 | | /* finish up initialization */ |
701 | 2 | if (!isReallyInitted) { |
702 | 2 | if (SECOID_Init() != SECSuccess) { |
703 | 0 | goto loser; |
704 | 0 | } |
705 | | #ifdef POLICY_FILE |
706 | | /* Load the system crypto policy file if it exists, |
707 | | * unless the NSS_IGNORE_SYSTEM_POLICY environment |
708 | | * variable has been set to 1. */ |
709 | | ignoreVar = PR_GetEnvSecure("NSS_IGNORE_SYSTEM_POLICY"); |
710 | | if (ignoreVar == NULL || strncmp(ignoreVar, "1", sizeof("1")) != 0) { |
711 | | if (PR_Access(POLICY_PATH "/" POLICY_FILE, PR_ACCESS_READ_OK) == PR_SUCCESS) { |
712 | | SECMODModule *module = SECMOD_LoadModule( |
713 | | "name=\"Policy File\" " |
714 | | "parameters=\"configdir='sql:" POLICY_PATH "' " |
715 | | "secmod='" POLICY_FILE "' " |
716 | | "flags=readOnly,noCertDB,forceSecmodChoice,forceOpen\" " |
717 | | "NSS=\"flags=internal,moduleDB,skipFirst,moduleDBOnly,critical\"", |
718 | | parent, PR_TRUE); |
719 | | if (module) { |
720 | | PRBool isLoaded = module->loaded; |
721 | | SECMOD_DestroyModule(module); |
722 | | if (!isLoaded) { |
723 | | goto loser; |
724 | | } |
725 | | } |
726 | | } |
727 | | } |
728 | | #endif |
729 | 2 | if (STAN_LoadDefaultNSS3TrustDomain() != PR_SUCCESS) { |
730 | 0 | goto loser; |
731 | 0 | } |
732 | 2 | if (nss_InitShutdownList() != SECSuccess) { |
733 | 0 | goto loser; |
734 | 0 | } |
735 | 2 | CERT_SetDefaultCertDB((CERTCertDBHandle *) |
736 | 2 | STAN_GetDefaultTrustDomain()); |
737 | 2 | if ((!noModDB) && (!noCertDB) && (!noRootInit)) { |
738 | 0 | if (!SECMOD_HasRootCerts()) { |
739 | 0 | const char *dbpath = configdir; |
740 | | /* handle supported database modifiers */ |
741 | 0 | if (strncmp(dbpath, "sql:", 4) == 0) { |
742 | 0 | dbpath += 4; |
743 | 0 | } else if (strncmp(dbpath, "dbm:", 4) == 0) { |
744 | 0 | dbpath += 4; |
745 | 0 | } else if (strncmp(dbpath, "extern:", 7) == 0) { |
746 | 0 | dbpath += 7; |
747 | 0 | } else if (strncmp(dbpath, "rdb:", 4) == 0) { |
748 | | /* if rdb: is specified, the configdir isn't really a |
749 | | * path. Skip it */ |
750 | 0 | dbpath = NULL; |
751 | 0 | } |
752 | 0 | if (dbpath) { |
753 | 0 | nss_FindExternalRoot(dbpath, secmodName); |
754 | 0 | } |
755 | 0 | } |
756 | 0 | } |
757 | 2 | pk11sdr_Init(); |
758 | 2 | cert_CreateSubjectKeyIDHashTable(); |
759 | | |
760 | | #ifndef NSS_DISABLE_LIBPKIX |
761 | | pkixError = PKIX_Initialize(PKIX_FALSE, PKIX_MAJOR_VERSION, PKIX_MINOR_VERSION, |
762 | | PKIX_MINOR_VERSION, &actualMinorVersion, &plContext); |
763 | | |
764 | | if (pkixError != NULL) { |
765 | | goto loser; |
766 | | } else { |
767 | | char *ev = PR_GetEnvSecure("NSS_DISABLE_PKIX_VERIFY"); |
768 | | if (ev && ev[0]) { |
769 | | CERT_SetUsePKIXForValidation(PR_FALSE); |
770 | | } |
771 | | } |
772 | | #endif /* NSS_DISABLE_LIBPKIX */ |
773 | 2 | } |
774 | | |
775 | | /* |
776 | | * Now mark the appropriate init state. If initContextPtr was passed |
777 | | * in, then return the new context pointer and add it to the |
778 | | * nssInitContextList. Otherwise set the global nss_isInitted flag |
779 | | */ |
780 | 2 | PZ_Lock(nssInitLock); |
781 | 2 | if (!initContextPtr) { |
782 | 2 | nssIsInitted = PR_TRUE; |
783 | 2 | } else { |
784 | 0 | (*initContextPtr)->magic = NSS_INIT_MAGIC; |
785 | 0 | (*initContextPtr)->next = nssInitContextList; |
786 | 0 | nssInitContextList = (*initContextPtr); |
787 | 0 | } |
788 | 2 | nssIsInInit--; |
789 | | /* now that we are inited, all waiters can move forward */ |
790 | 2 | PZ_NotifyAllCondVar(nssInitCondition); |
791 | 2 | PZ_Unlock(nssInitLock); |
792 | | |
793 | 2 | if (initContextPtr && configStrings) { |
794 | 0 | PR_smprintf_free(configStrings); |
795 | 0 | } |
796 | 2 | if (parent) { |
797 | 2 | SECMOD_DestroyModule(parent); |
798 | 2 | } |
799 | | |
800 | 2 | return SECSuccess; |
801 | | |
802 | 0 | loser: |
803 | 0 | if (initContextPtr && *initContextPtr) { |
804 | 0 | PORT_Free(*initContextPtr); |
805 | 0 | *initContextPtr = NULL; |
806 | 0 | if (configStrings) { |
807 | 0 | PR_smprintf_free(configStrings); |
808 | 0 | } |
809 | 0 | } |
810 | 0 | PZ_Lock(nssInitLock); |
811 | 0 | nssIsInInit--; |
812 | | /* We failed to init, allow one to move forward */ |
813 | 0 | PZ_NotifyCondVar(nssInitCondition); |
814 | 0 | PZ_Unlock(nssInitLock); |
815 | 0 | if (parent) { |
816 | 0 | SECMOD_DestroyModule(parent); |
817 | 0 | } |
818 | 0 | return SECFailure; |
819 | 2 | } |
820 | | |
821 | | SECStatus |
822 | | NSS_Init(const char *configdir) |
823 | 0 | { |
824 | 0 | return nss_Init(configdir, "", "", SECMOD_DB, "", "", "", "", "", NULL, |
825 | 0 | NULL, PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE, |
826 | 0 | PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE); |
827 | 0 | } |
828 | | |
829 | | SECStatus |
830 | | NSS_InitReadWrite(const char *configdir) |
831 | 0 | { |
832 | 0 | return nss_Init(configdir, "", "", SECMOD_DB, "", "", "", "", "", NULL, |
833 | 0 | NULL, PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE, |
834 | 0 | PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE); |
835 | 0 | } |
836 | | |
837 | | /* |
838 | | * OK there are now lots of options here, lets go through them all: |
839 | | * |
840 | | * configdir - base directory where all the cert, key, and module datbases live. |
841 | | * certPrefix - prefix added to the beginning of the cert database example: " |
842 | | * "https-server1-" |
843 | | * keyPrefix - prefix added to the beginning of the key database example: " |
844 | | * "https-server1-" |
845 | | * secmodName - name of the security module database (usually "secmod.db"). |
846 | | * flags - change the open options of NSS_Initialize as follows: |
847 | | * NSS_INIT_READONLY - Open the databases read only. |
848 | | * NSS_INIT_NOCERTDB - Don't open the cert DB and key DB's, just |
849 | | * initialize the volatile certdb. |
850 | | * NSS_INIT_NOMODDB - Don't open the security module DB, just |
851 | | * initialize the PKCS #11 module. |
852 | | * NSS_INIT_FORCEOPEN - Continue to force initializations even if the |
853 | | * databases cannot be opened. |
854 | | * NSS_INIT_PK11THREADSAFE - only load PKCS#11 modules that are |
855 | | * thread-safe, ie. that support locking - either OS |
856 | | * locking or NSS-provided locks . If a PKCS#11 |
857 | | * module isn't thread-safe, don't serialize its |
858 | | * calls; just don't load it instead. This is necessary |
859 | | * if another piece of code is using the same PKCS#11 |
860 | | * modules that NSS is accessing without going through |
861 | | * NSS, for example the Java SunPKCS11 provider. |
862 | | * NSS_INIT_PK11RELOAD - ignore the CKR_CRYPTOKI_ALREADY_INITIALIZED |
863 | | * error when loading PKCS#11 modules. This is necessary |
864 | | * if another piece of code is using the same PKCS#11 |
865 | | * modules that NSS is accessing without going through |
866 | | * NSS, for example Java SunPKCS11 provider. |
867 | | * NSS_INIT_NOPK11FINALIZE - never call C_Finalize on any |
868 | | * PKCS#11 module. This may be necessary in order to |
869 | | * ensure continuous operation and proper shutdown |
870 | | * sequence if another piece of code is using the same |
871 | | * PKCS#11 modules that NSS is accessing without going |
872 | | * through NSS, for example Java SunPKCS11 provider. |
873 | | * The following limitation applies when this is set : |
874 | | * SECMOD_WaitForAnyTokenEvent will not use |
875 | | * C_WaitForSlotEvent, in order to prevent the need for |
876 | | * C_Finalize. This call will be emulated instead. |
877 | | * NSS_INIT_RESERVED - Currently has no effect, but may be used in the |
878 | | * future to trigger better cooperation between PKCS#11 |
879 | | * modules used by both NSS and the Java SunPKCS11 |
880 | | * provider. This should occur after a new flag is defined |
881 | | * for C_Initialize by the PKCS#11 working group. |
882 | | * NSS_INIT_COOPERATE - Sets 4 recommended options for applications that |
883 | | * use both NSS and the Java SunPKCS11 provider. |
884 | | */ |
885 | | SECStatus |
886 | | NSS_Initialize(const char *configdir, const char *certPrefix, |
887 | | const char *keyPrefix, const char *secmodName, PRUint32 flags) |
888 | 0 | { |
889 | 0 | return nss_Init(configdir, certPrefix, keyPrefix, secmodName, |
890 | 0 | "", "", "", "", "", NULL, NULL, |
891 | 0 | ((flags & NSS_INIT_READONLY) == NSS_INIT_READONLY), |
892 | 0 | ((flags & NSS_INIT_NOCERTDB) == NSS_INIT_NOCERTDB), |
893 | 0 | ((flags & NSS_INIT_NOMODDB) == NSS_INIT_NOMODDB), |
894 | 0 | ((flags & NSS_INIT_FORCEOPEN) == NSS_INIT_FORCEOPEN), |
895 | 0 | ((flags & NSS_INIT_NOROOTINIT) == NSS_INIT_NOROOTINIT), |
896 | 0 | ((flags & NSS_INIT_OPTIMIZESPACE) == NSS_INIT_OPTIMIZESPACE), |
897 | 0 | ((flags & NSS_INIT_PK11THREADSAFE) == NSS_INIT_PK11THREADSAFE), |
898 | 0 | ((flags & NSS_INIT_PK11RELOAD) == NSS_INIT_PK11RELOAD), |
899 | 0 | ((flags & NSS_INIT_NOPK11FINALIZE) == NSS_INIT_NOPK11FINALIZE)); |
900 | 0 | } |
901 | | |
902 | | NSSInitContext * |
903 | | NSS_InitContext(const char *configdir, const char *certPrefix, |
904 | | const char *keyPrefix, const char *secmodName, |
905 | | NSSInitParameters *initParams, PRUint32 flags) |
906 | 0 | { |
907 | 0 | SECStatus rv; |
908 | 0 | NSSInitContext *context; |
909 | |
|
910 | 0 | rv = nss_Init(configdir, certPrefix, keyPrefix, secmodName, |
911 | 0 | "", "", "", "", "", &context, initParams, |
912 | 0 | ((flags & NSS_INIT_READONLY) == NSS_INIT_READONLY), |
913 | 0 | ((flags & NSS_INIT_NOCERTDB) == NSS_INIT_NOCERTDB), |
914 | 0 | ((flags & NSS_INIT_NOMODDB) == NSS_INIT_NOMODDB), |
915 | 0 | ((flags & NSS_INIT_FORCEOPEN) == NSS_INIT_FORCEOPEN), PR_TRUE, |
916 | 0 | ((flags & NSS_INIT_OPTIMIZESPACE) == NSS_INIT_OPTIMIZESPACE), |
917 | 0 | ((flags & NSS_INIT_PK11THREADSAFE) == NSS_INIT_PK11THREADSAFE), |
918 | 0 | ((flags & NSS_INIT_PK11RELOAD) == NSS_INIT_PK11RELOAD), |
919 | 0 | ((flags & NSS_INIT_NOPK11FINALIZE) == NSS_INIT_NOPK11FINALIZE)); |
920 | 0 | return (rv == SECSuccess) ? context : NULL; |
921 | 0 | } |
922 | | |
923 | | SECStatus |
924 | | NSS_InitWithMerge(const char *configdir, const char *certPrefix, |
925 | | const char *keyPrefix, const char *secmodName, |
926 | | const char *updateDir, const char *updCertPrefix, |
927 | | const char *updKeyPrefix, const char *updateID, |
928 | | const char *updateName, PRUint32 flags) |
929 | 0 | { |
930 | 0 | return nss_Init(configdir, certPrefix, keyPrefix, secmodName, |
931 | 0 | updateDir, updCertPrefix, updKeyPrefix, updateID, updateName, |
932 | 0 | NULL, NULL, |
933 | 0 | ((flags & NSS_INIT_READONLY) == NSS_INIT_READONLY), |
934 | 0 | ((flags & NSS_INIT_NOCERTDB) == NSS_INIT_NOCERTDB), |
935 | 0 | ((flags & NSS_INIT_NOMODDB) == NSS_INIT_NOMODDB), |
936 | 0 | ((flags & NSS_INIT_FORCEOPEN) == NSS_INIT_FORCEOPEN), |
937 | 0 | ((flags & NSS_INIT_NOROOTINIT) == NSS_INIT_NOROOTINIT), |
938 | 0 | ((flags & NSS_INIT_OPTIMIZESPACE) == NSS_INIT_OPTIMIZESPACE), |
939 | 0 | ((flags & NSS_INIT_PK11THREADSAFE) == NSS_INIT_PK11THREADSAFE), |
940 | 0 | ((flags & NSS_INIT_PK11RELOAD) == NSS_INIT_PK11RELOAD), |
941 | 0 | ((flags & NSS_INIT_NOPK11FINALIZE) == NSS_INIT_NOPK11FINALIZE)); |
942 | 0 | } |
943 | | |
944 | | /* |
945 | | * initialize NSS without a creating cert db's, key db's, or secmod db's. |
946 | | */ |
947 | | SECStatus |
948 | | NSS_NoDB_Init(const char *configdir) |
949 | 2 | { |
950 | 2 | return nss_Init("", "", "", "", "", "", "", "", "", NULL, NULL, |
951 | 2 | PR_TRUE, PR_TRUE, PR_TRUE, PR_TRUE, PR_TRUE, PR_TRUE, |
952 | 2 | PR_FALSE, PR_FALSE, PR_FALSE); |
953 | 2 | } |
954 | | |
955 | 2 | #define NSS_SHUTDOWN_STEP 10 |
956 | | |
957 | | struct NSSShutdownFuncPair { |
958 | | NSS_ShutdownFunc func; |
959 | | void *appData; |
960 | | }; |
961 | | |
962 | | static struct NSSShutdownListStr { |
963 | | PZLock *lock; |
964 | | int allocatedFuncs; |
965 | | int peakFuncs; |
966 | | struct NSSShutdownFuncPair *funcs; |
967 | | } nssShutdownList = { 0 }; |
968 | | |
969 | | /* |
970 | | * find and existing shutdown function |
971 | | */ |
972 | | static int |
973 | | nss_GetShutdownEntry(NSS_ShutdownFunc sFunc, void *appData) |
974 | 0 | { |
975 | 0 | int count, i; |
976 | 0 | count = nssShutdownList.peakFuncs; |
977 | |
|
978 | 0 | for (i = 0; i < count; i++) { |
979 | 0 | if ((nssShutdownList.funcs[i].func == sFunc) && |
980 | 0 | (nssShutdownList.funcs[i].appData == appData)) { |
981 | 0 | return i; |
982 | 0 | } |
983 | 0 | } |
984 | 0 | return -1; |
985 | 0 | } |
986 | | |
987 | | /* |
988 | | * register a callback to be called when NSS shuts down |
989 | | */ |
990 | | SECStatus |
991 | | NSS_RegisterShutdown(NSS_ShutdownFunc sFunc, void *appData) |
992 | 0 | { |
993 | 0 | int i; |
994 | | |
995 | | /* make sure our lock and condition variable are initialized one and only |
996 | | * one time */ |
997 | 0 | if (PR_CallOnce(&nssInitOnce, nss_doLockInit) != PR_SUCCESS) { |
998 | 0 | return SECFailure; |
999 | 0 | } |
1000 | | |
1001 | 0 | PZ_Lock(nssInitLock); |
1002 | 0 | if (!NSS_IsInitialized()) { |
1003 | 0 | PZ_Unlock(nssInitLock); |
1004 | 0 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
1005 | 0 | return SECFailure; |
1006 | 0 | } |
1007 | 0 | PZ_Unlock(nssInitLock); |
1008 | 0 | if (sFunc == NULL) { |
1009 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
1010 | 0 | return SECFailure; |
1011 | 0 | } |
1012 | | |
1013 | 0 | PORT_Assert(nssShutdownList.lock); |
1014 | 0 | PZ_Lock(nssShutdownList.lock); |
1015 | | |
1016 | | /* make sure we don't have a duplicate */ |
1017 | 0 | i = nss_GetShutdownEntry(sFunc, appData); |
1018 | 0 | if (i >= 0) { |
1019 | 0 | PZ_Unlock(nssShutdownList.lock); |
1020 | 0 | PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
1021 | 0 | return SECFailure; |
1022 | 0 | } |
1023 | | /* find an empty slot */ |
1024 | 0 | i = nss_GetShutdownEntry(NULL, NULL); |
1025 | 0 | if (i >= 0) { |
1026 | 0 | nssShutdownList.funcs[i].func = sFunc; |
1027 | 0 | nssShutdownList.funcs[i].appData = appData; |
1028 | 0 | PZ_Unlock(nssShutdownList.lock); |
1029 | 0 | return SECSuccess; |
1030 | 0 | } |
1031 | 0 | if (nssShutdownList.allocatedFuncs == nssShutdownList.peakFuncs) { |
1032 | 0 | struct NSSShutdownFuncPair *funcs = |
1033 | 0 | (struct NSSShutdownFuncPair *)PORT_Realloc(nssShutdownList.funcs, |
1034 | 0 | (nssShutdownList.allocatedFuncs + NSS_SHUTDOWN_STEP) * sizeof(struct NSSShutdownFuncPair)); |
1035 | 0 | if (!funcs) { |
1036 | 0 | PZ_Unlock(nssShutdownList.lock); |
1037 | 0 | return SECFailure; |
1038 | 0 | } |
1039 | 0 | nssShutdownList.funcs = funcs; |
1040 | 0 | nssShutdownList.allocatedFuncs += NSS_SHUTDOWN_STEP; |
1041 | 0 | } |
1042 | 0 | nssShutdownList.funcs[nssShutdownList.peakFuncs].func = sFunc; |
1043 | 0 | nssShutdownList.funcs[nssShutdownList.peakFuncs].appData = appData; |
1044 | 0 | nssShutdownList.peakFuncs++; |
1045 | 0 | PZ_Unlock(nssShutdownList.lock); |
1046 | 0 | return SECSuccess; |
1047 | 0 | } |
1048 | | |
1049 | | /* |
1050 | | * unregister a callback so it won't get called on shutdown. |
1051 | | */ |
1052 | | SECStatus |
1053 | | NSS_UnregisterShutdown(NSS_ShutdownFunc sFunc, void *appData) |
1054 | 0 | { |
1055 | 0 | int i; |
1056 | | |
1057 | | /* make sure our lock and condition variable are initialized one and only |
1058 | | * one time */ |
1059 | 0 | if (PR_CallOnce(&nssInitOnce, nss_doLockInit) != PR_SUCCESS) { |
1060 | 0 | return SECFailure; |
1061 | 0 | } |
1062 | 0 | PZ_Lock(nssInitLock); |
1063 | 0 | if (!NSS_IsInitialized()) { |
1064 | 0 | PZ_Unlock(nssInitLock); |
1065 | 0 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
1066 | 0 | return SECFailure; |
1067 | 0 | } |
1068 | 0 | PZ_Unlock(nssInitLock); |
1069 | |
|
1070 | 0 | PORT_Assert(nssShutdownList.lock); |
1071 | 0 | PZ_Lock(nssShutdownList.lock); |
1072 | 0 | i = nss_GetShutdownEntry(sFunc, appData); |
1073 | 0 | if (i >= 0) { |
1074 | 0 | nssShutdownList.funcs[i].func = NULL; |
1075 | 0 | nssShutdownList.funcs[i].appData = NULL; |
1076 | 0 | } |
1077 | 0 | PZ_Unlock(nssShutdownList.lock); |
1078 | |
|
1079 | 0 | if (i < 0) { |
1080 | 0 | PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
1081 | 0 | return SECFailure; |
1082 | 0 | } |
1083 | 0 | return SECSuccess; |
1084 | 0 | } |
1085 | | |
1086 | | /* |
1087 | | * bring up and shutdown the shutdown list |
1088 | | */ |
1089 | | static SECStatus |
1090 | | nss_InitShutdownList(void) |
1091 | 2 | { |
1092 | 2 | if (nssShutdownList.lock != NULL) { |
1093 | 0 | return SECSuccess; |
1094 | 0 | } |
1095 | 2 | nssShutdownList.lock = PZ_NewLock(nssILockOther); |
1096 | 2 | if (nssShutdownList.lock == NULL) { |
1097 | 0 | return SECFailure; |
1098 | 0 | } |
1099 | 2 | nssShutdownList.funcs = PORT_ZNewArray(struct NSSShutdownFuncPair, |
1100 | 2 | NSS_SHUTDOWN_STEP); |
1101 | 2 | if (nssShutdownList.funcs == NULL) { |
1102 | 0 | PZ_DestroyLock(nssShutdownList.lock); |
1103 | 0 | nssShutdownList.lock = NULL; |
1104 | 0 | return SECFailure; |
1105 | 0 | } |
1106 | 2 | nssShutdownList.allocatedFuncs = NSS_SHUTDOWN_STEP; |
1107 | 2 | nssShutdownList.peakFuncs = 0; |
1108 | | |
1109 | 2 | return SECSuccess; |
1110 | 2 | } |
1111 | | |
1112 | | static SECStatus |
1113 | | nss_ShutdownShutdownList(void) |
1114 | 0 | { |
1115 | 0 | SECStatus rv = SECSuccess; |
1116 | 0 | int i; |
1117 | | |
1118 | | /* call all the registerd functions first */ |
1119 | 0 | for (i = 0; i < nssShutdownList.peakFuncs; i++) { |
1120 | 0 | struct NSSShutdownFuncPair *funcPair = &nssShutdownList.funcs[i]; |
1121 | 0 | if (funcPair->func) { |
1122 | 0 | if ((*funcPair->func)(funcPair->appData, NULL) != SECSuccess) { |
1123 | 0 | rv = SECFailure; |
1124 | 0 | } |
1125 | 0 | } |
1126 | 0 | } |
1127 | |
|
1128 | 0 | nssShutdownList.peakFuncs = 0; |
1129 | 0 | nssShutdownList.allocatedFuncs = 0; |
1130 | 0 | PORT_Free(nssShutdownList.funcs); |
1131 | 0 | nssShutdownList.funcs = NULL; |
1132 | 0 | if (nssShutdownList.lock) { |
1133 | 0 | PZ_DestroyLock(nssShutdownList.lock); |
1134 | 0 | } |
1135 | 0 | nssShutdownList.lock = NULL; |
1136 | 0 | return rv; |
1137 | 0 | } |
1138 | | |
1139 | | extern const NSSError NSS_ERROR_BUSY; |
1140 | | |
1141 | | SECStatus |
1142 | | nss_Shutdown(void) |
1143 | 0 | { |
1144 | 0 | SECStatus shutdownRV = SECSuccess; |
1145 | 0 | SECStatus rv; |
1146 | 0 | PRStatus status; |
1147 | 0 | NSSInitContext *temp; |
1148 | |
|
1149 | 0 | rv = nss_ShutdownShutdownList(); |
1150 | 0 | if (rv != SECSuccess) { |
1151 | 0 | shutdownRV = SECFailure; |
1152 | 0 | } |
1153 | 0 | cert_DestroyLocks(); |
1154 | 0 | ShutdownCRLCache(); |
1155 | 0 | OCSP_ShutdownGlobal(); |
1156 | | #ifndef NSS_DISABLE_LIBPKIX |
1157 | | PKIX_Shutdown(plContext); |
1158 | | #endif /* NSS_DISABLE_LIBPKIX */ |
1159 | 0 | SECOID_Shutdown(); |
1160 | 0 | status = STAN_Shutdown(); |
1161 | 0 | cert_DestroySubjectKeyIDHashTable(); |
1162 | 0 | pk11_SetInternalKeySlot(NULL); |
1163 | 0 | rv = SECMOD_Shutdown(); |
1164 | 0 | if (rv != SECSuccess) { |
1165 | 0 | shutdownRV = SECFailure; |
1166 | 0 | } |
1167 | 0 | pk11sdr_Shutdown(); |
1168 | 0 | nssArena_Shutdown(); |
1169 | 0 | if (status == PR_FAILURE) { |
1170 | 0 | if (NSS_GetError() == NSS_ERROR_BUSY) { |
1171 | 0 | PORT_SetError(SEC_ERROR_BUSY); |
1172 | 0 | } |
1173 | 0 | shutdownRV = SECFailure; |
1174 | 0 | } |
1175 | | /* |
1176 | | * A thread's error stack is automatically destroyed when the thread |
1177 | | * terminates, except for the primordial thread, whose error stack is |
1178 | | * destroyed by PR_Cleanup. Since NSS is usually shut down by the |
1179 | | * primordial thread and many NSS-based apps don't call PR_Cleanup, |
1180 | | * we destroy the calling thread's error stack here. This must be |
1181 | | * done after any NSS_GetError call, otherwise NSS_GetError will |
1182 | | * create the error stack again. |
1183 | | */ |
1184 | 0 | nss_DestroyErrorStack(); |
1185 | 0 | nssIsInitted = PR_FALSE; |
1186 | 0 | temp = nssInitContextList; |
1187 | 0 | nssInitContextList = NULL; |
1188 | | /* free the old list. This is necessary when we are called from |
1189 | | * NSS_Shutdown(). */ |
1190 | 0 | while (temp) { |
1191 | 0 | NSSInitContext *next = temp->next; |
1192 | 0 | temp->magic = 0; |
1193 | 0 | PORT_Free(temp); |
1194 | 0 | temp = next; |
1195 | 0 | } |
1196 | 0 | return shutdownRV; |
1197 | 0 | } |
1198 | | |
1199 | | SECStatus |
1200 | | NSS_Shutdown(void) |
1201 | 0 | { |
1202 | 0 | SECStatus rv; |
1203 | | /* make sure our lock and condition variable are initialized one and only |
1204 | | * one time */ |
1205 | 0 | if (PR_CallOnce(&nssInitOnce, nss_doLockInit) != PR_SUCCESS) { |
1206 | 0 | return SECFailure; |
1207 | 0 | } |
1208 | 0 | PZ_Lock(nssInitLock); |
1209 | |
|
1210 | 0 | if (!nssIsInitted) { |
1211 | 0 | PZ_Unlock(nssInitLock); |
1212 | 0 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
1213 | 0 | return SECFailure; |
1214 | 0 | } |
1215 | | |
1216 | | /* If one or more threads are in the middle of init, wait for them |
1217 | | * to complete */ |
1218 | 0 | while (nssIsInInit) { |
1219 | 0 | PZ_WaitCondVar(nssInitCondition, PR_INTERVAL_NO_TIMEOUT); |
1220 | 0 | } |
1221 | 0 | rv = nss_Shutdown(); |
1222 | 0 | PZ_Unlock(nssInitLock); |
1223 | 0 | return rv; |
1224 | 0 | } |
1225 | | |
1226 | | /* |
1227 | | * remove the context from a list. return true if found, false if not |
1228 | | */ |
1229 | | PRBool |
1230 | | nss_RemoveList(NSSInitContext *context) |
1231 | 0 | { |
1232 | 0 | NSSInitContext *this = nssInitContextList; |
1233 | 0 | NSSInitContext **last = &nssInitContextList; |
1234 | |
|
1235 | 0 | while (this) { |
1236 | 0 | if (this == context) { |
1237 | 0 | *last = this->next; |
1238 | 0 | this->magic = 0; |
1239 | 0 | PORT_Free(this); |
1240 | 0 | return PR_TRUE; |
1241 | 0 | } |
1242 | 0 | last = &this->next; |
1243 | 0 | this = this->next; |
1244 | 0 | } |
1245 | 0 | return PR_FALSE; |
1246 | 0 | } |
1247 | | |
1248 | | /* |
1249 | | * This form of shutdown is safe in the case where we may have multiple |
1250 | | * entities using NSS in a single process. Each entity calls shutdown with |
1251 | | * it's own context. The application (which doesn't get a context), calls |
1252 | | * shutdown with NULL. Once all users have 'checked in' NSS will shutdown. |
1253 | | * This is different than NSS_Shutdown, where calling it will shutdown NSS |
1254 | | * irreguardless of who else may have NSS open. |
1255 | | */ |
1256 | | SECStatus |
1257 | | NSS_ShutdownContext(NSSInitContext *context) |
1258 | 0 | { |
1259 | 0 | SECStatus rv = SECSuccess; |
1260 | | |
1261 | | /* make sure our lock and condition variable are initialized one and only |
1262 | | * one time */ |
1263 | 0 | if (PR_CallOnce(&nssInitOnce, nss_doLockInit) != PR_SUCCESS) { |
1264 | 0 | return SECFailure; |
1265 | 0 | } |
1266 | 0 | PZ_Lock(nssInitLock); |
1267 | | /* If one or more threads are in the middle of init, wait for them |
1268 | | * to complete */ |
1269 | 0 | while (nssIsInInit) { |
1270 | 0 | PZ_WaitCondVar(nssInitCondition, PR_INTERVAL_NO_TIMEOUT); |
1271 | 0 | } |
1272 | | |
1273 | | /* OK, we are the only thread now either initializing or shutting down */ |
1274 | |
|
1275 | 0 | if (!context) { |
1276 | 0 | if (!nssIsInitted) { |
1277 | 0 | PZ_Unlock(nssInitLock); |
1278 | 0 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
1279 | 0 | return SECFailure; |
1280 | 0 | } |
1281 | 0 | nssIsInitted = 0; |
1282 | 0 | } else if (!nss_RemoveList(context)) { |
1283 | 0 | PZ_Unlock(nssInitLock); |
1284 | | /* context was already freed or wasn't valid */ |
1285 | 0 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
1286 | 0 | return SECFailure; |
1287 | 0 | } |
1288 | 0 | if ((nssIsInitted == 0) && (nssInitContextList == NULL)) { |
1289 | 0 | rv = nss_Shutdown(); |
1290 | 0 | } |
1291 | | |
1292 | | /* NOTE: we don't try to free the nssInitLocks to prevent races against |
1293 | | * the locks. There may be a thread, right now, waiting in NSS_Init for us |
1294 | | * to free the lock below. If we delete the locks, bad things would happen |
1295 | | * to that thread */ |
1296 | 0 | PZ_Unlock(nssInitLock); |
1297 | |
|
1298 | 0 | return rv; |
1299 | 0 | } |
1300 | | |
1301 | | PRBool |
1302 | | NSS_IsInitialized(void) |
1303 | 2 | { |
1304 | 2 | return (nssIsInitted) || (nssInitContextList != NULL); |
1305 | 2 | } |
1306 | | |
1307 | | extern const char __nss_base_version[]; |
1308 | | |
1309 | | PRBool |
1310 | | NSS_VersionCheck(const char *importedVersion) |
1311 | 0 | { |
1312 | | /* |
1313 | | * This is the secret handshake algorithm. |
1314 | | * |
1315 | | * This release has a simple version compatibility |
1316 | | * check algorithm. This release is not backward |
1317 | | * compatible with previous major releases. It is |
1318 | | * not compatible with future major, minor, or |
1319 | | * patch releases or builds. |
1320 | | */ |
1321 | 0 | int vmajor = 0, vminor = 0, vpatch = 0, vbuild = 0; |
1322 | 0 | const char *ptr = importedVersion; |
1323 | 0 | #define NSS_VERSION_VARIABLE __nss_base_version |
1324 | 0 | #include "verref.h" |
1325 | |
|
1326 | 0 | while (isdigit((unsigned char)*ptr)) { |
1327 | 0 | vmajor = 10 * vmajor + *ptr - '0'; |
1328 | 0 | ptr++; |
1329 | 0 | } |
1330 | 0 | if (*ptr == '.') { |
1331 | 0 | ptr++; |
1332 | 0 | while (isdigit((unsigned char)*ptr)) { |
1333 | 0 | vminor = 10 * vminor + *ptr - '0'; |
1334 | 0 | ptr++; |
1335 | 0 | } |
1336 | 0 | if (*ptr == '.') { |
1337 | 0 | ptr++; |
1338 | 0 | while (isdigit((unsigned char)*ptr)) { |
1339 | 0 | vpatch = 10 * vpatch + *ptr - '0'; |
1340 | 0 | ptr++; |
1341 | 0 | } |
1342 | 0 | if (*ptr == '.') { |
1343 | 0 | ptr++; |
1344 | 0 | while (isdigit((unsigned char)*ptr)) { |
1345 | 0 | vbuild = 10 * vbuild + *ptr - '0'; |
1346 | 0 | ptr++; |
1347 | 0 | } |
1348 | 0 | } |
1349 | 0 | } |
1350 | 0 | } |
1351 | |
|
1352 | 0 | if (vmajor != NSS_VMAJOR) { |
1353 | 0 | return PR_FALSE; |
1354 | 0 | } |
1355 | 0 | if (vmajor == NSS_VMAJOR && vminor > NSS_VMINOR) { |
1356 | 0 | return PR_FALSE; |
1357 | 0 | } |
1358 | 0 | if (vmajor == NSS_VMAJOR && vminor == NSS_VMINOR && vpatch > NSS_VPATCH) { |
1359 | 0 | return PR_FALSE; |
1360 | 0 | } |
1361 | 0 | if (vmajor == NSS_VMAJOR && vminor == NSS_VMINOR && |
1362 | 0 | vpatch == NSS_VPATCH && vbuild > NSS_VBUILD) { |
1363 | 0 | return PR_FALSE; |
1364 | 0 | } |
1365 | 0 | return PR_TRUE; |
1366 | 0 | } |
1367 | | |
1368 | | const char * |
1369 | | NSS_GetVersion(void) |
1370 | 0 | { |
1371 | 0 | return NSS_VERSION; |
1372 | 0 | } |