2
* NSS utility functions
4
* ***** BEGIN LICENSE BLOCK *****
5
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
7
* The contents of this file are subject to the Mozilla Public License Version
8
* 1.1 (the "License"); you may not use this file except in compliance with
9
* the License. You may obtain a copy of the License at
10
* http://www.mozilla.org/MPL/
12
* Software distributed under the License is distributed on an "AS IS" basis,
13
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14
* for the specific language governing rights and limitations under the
17
* The Original Code is the Netscape security libraries.
19
* The Initial Developer of the Original Code is
20
* Netscape Communications Corporation.
21
* Portions created by the Initial Developer are Copyright (C) 1994-2000
22
* the Initial Developer. All Rights Reserved.
26
* Alternatively, the contents of this file may be used under the terms of
27
* either the GNU General Public License Version 2 or later (the "GPL"), or
28
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29
* in which case the provisions of the GPL or the LGPL are applicable instead
30
* of those above. If you wish to allow use of your version of this file only
31
* under the terms of either the GPL or the LGPL, and not to allow others to
32
* use your version of this file under the terms of the MPL, indicate your
33
* decision by deleting the provisions above and replace them with the notice
34
* and other provisions required by the GPL or the LGPL. If you do not delete
35
* the provisions above, a recipient may use your version of this file under
36
* the terms of any one of the MPL, the GPL or the LGPL.
38
* ***** END LICENSE BLOCK ***** */
39
/* $Id: nssinit.c,v 1.69.2.6 2006/09/21 20:09:48 julien.pierre.bugs%sun.com Exp $ */
63
* On Windows nss3.dll needs to export the symbol 'mktemp' to be
64
* fully backward compatible with the nss3.dll in NSS 3.2.x and
65
* 3.3.x. This symbol was unintentionally exported and its
66
* definition (in DBM) was moved from nss3.dll to softokn3.dll
67
* in NSS 3.4. See bug 142575.
69
#ifdef WIN32_NSS3_DLL_COMPAT
72
/* exported as 'mktemp' */
74
nss_mktemp(char *path)
80
#define NSS_MAX_FLAG_SIZE sizeof("readOnly")+sizeof("noCertDB")+ \
81
sizeof("noModDB")+sizeof("forceOpen")+sizeof("passwordRequired")+ \
82
sizeof ("optimizeSpace")
83
#define NSS_DEFAULT_MOD_NAME "NSS Internal Module"
86
nss_makeFlags(PRBool readOnly, PRBool noCertDB,
87
PRBool noModDB, PRBool forceOpen,
88
PRBool passwordRequired, PRBool optimizeSpace)
90
char *flags = (char *)PORT_Alloc(NSS_MAX_FLAG_SIZE);
91
PRBool first = PR_TRUE;
93
PORT_Memset(flags,0,NSS_MAX_FLAG_SIZE);
95
PORT_Strcat(flags,"readOnly");
99
if (!first) PORT_Strcat(flags,",");
100
PORT_Strcat(flags,"noCertDB");
104
if (!first) PORT_Strcat(flags,",");
105
PORT_Strcat(flags,"noModDB");
109
if (!first) PORT_Strcat(flags,",");
110
PORT_Strcat(flags,"forceOpen");
113
if (passwordRequired) {
114
if (!first) PORT_Strcat(flags,",");
115
PORT_Strcat(flags,"passwordRequired");
119
if (!first) PORT_Strcat(flags,",");
120
PORT_Strcat(flags,"optimizeSpace");
127
* statics to remember the PKCS11_ConfigurePKCS11()
130
static char * pk11_config_strings = NULL;
131
static char * pk11_config_name = NULL;
132
static PRBool pk11_password_required = PR_FALSE;
135
* this is a legacy configuration function which used to be part of
136
* the PKCS #11 internal token.
139
PK11_ConfigurePKCS11(const char *man, const char *libdes, const char *tokdes,
140
const char *ptokdes, const char *slotdes, const char *pslotdes,
141
const char *fslotdes, const char *fpslotdes, int minPwd, int pwRequired)
143
char *strings = NULL;
146
/* make sure the internationalization was done correctly... */
147
strings = PR_smprintf("");
148
if (strings == NULL) return;
151
newStrings = PR_smprintf("%s manufacturerID='%s'",strings,man);
152
PR_smprintf_free(strings);
153
strings = newStrings;
155
if (strings == NULL) return;
158
newStrings = PR_smprintf("%s libraryDescription='%s'",strings,libdes);
159
PR_smprintf_free(strings);
160
strings = newStrings;
161
if (pk11_config_name != NULL) {
162
PORT_Free(pk11_config_name);
164
pk11_config_name = PORT_Strdup(libdes);
166
if (strings == NULL) return;
169
newStrings = PR_smprintf("%s cryptoTokenDescription='%s'",strings,
171
PR_smprintf_free(strings);
172
strings = newStrings;
174
if (strings == NULL) return;
177
newStrings = PR_smprintf("%s dbTokenDescription='%s'",strings,ptokdes);
178
PR_smprintf_free(strings);
179
strings = newStrings;
181
if (strings == NULL) return;
184
newStrings = PR_smprintf("%s cryptoSlotDescription='%s'",strings,
186
PR_smprintf_free(strings);
187
strings = newStrings;
189
if (strings == NULL) return;
192
newStrings = PR_smprintf("%s dbSlotDescription='%s'",strings,pslotdes);
193
PR_smprintf_free(strings);
194
strings = newStrings;
196
if (strings == NULL) return;
199
newStrings = PR_smprintf("%s FIPSSlotDescription='%s'",
201
PR_smprintf_free(strings);
202
strings = newStrings;
204
if (strings == NULL) return;
207
newStrings = PR_smprintf("%s FIPSTokenDescription='%s'",
209
PR_smprintf_free(strings);
210
strings = newStrings;
212
if (strings == NULL) return;
214
newStrings = PR_smprintf("%s minPS=%d", strings, minPwd);
215
PR_smprintf_free(strings);
216
strings = newStrings;
217
if (strings == NULL) return;
219
if (pk11_config_strings != NULL) {
220
PR_smprintf_free(pk11_config_strings);
222
pk11_config_strings = strings;
223
pk11_password_required = pwRequired;
229
nss_addEscape(const char *string, char quote)
232
int escapes = 0, size = 0;
236
for (src=string; *src ; src++) {
237
if ((*src == quote) || (*src == '\\')) escapes++;
241
newString = PORT_ZAlloc(escapes+size+1);
242
if (newString == NULL) {
246
for (src=string, dest=newString; *src; src++,dest++) {
247
if ((*src == '\\') || (*src == quote)) {
257
nss_doubleEscape(const char *string)
260
char *retValue = NULL;
261
if (string == NULL) {
264
round1 = nss_addEscape(string,'\'');
266
retValue = nss_addEscape(round1,'"');
271
if (retValue == NULL) {
272
retValue = PORT_Strdup("");
280
* The following code is an attempt to automagically find the external root
281
* module. NOTE: This code should be checked out on the MAC! There must be
282
* some cross platform support out there to help out with this?
283
* Note: Keep the #if-defined chunks in order. HPUX must select before UNIX.
286
static const char *dllname =
287
#if defined(XP_WIN32) || defined(XP_OS2)
289
#elif defined(HPUX) && !defined(__ia64) /* HP-UX PA-RISC */
291
#elif defined(DARWIN)
293
#elif defined(XP_UNIX) || defined(XP_BEOS)
295
#elif defined(XP_MAC)
296
"NSS Builtin Root Certs";
298
#error "Uh! Oh! I don't know about this platform."
301
/* Should we have platform ifdefs here??? */
304
static void nss_FindExternalRootPaths(const char *dbpath,
305
const char* secmodprefix,
306
char** retoldpath, char** retnewpath)
308
char *path, *oldpath = NULL, *lastsep;
309
int len, path_len, secmod_len, dll_len;
311
path_len = PORT_Strlen(dbpath);
312
secmod_len = secmodprefix ? PORT_Strlen(secmodprefix) : 0;
313
dll_len = PORT_Strlen(dllname);
314
len = path_len + secmod_len + dll_len + 2; /* FILE_SEP + NULL */
316
path = PORT_Alloc(len);
317
if (path == NULL) return;
319
/* back up to the top of the directory */
320
PORT_Memcpy(path,dbpath,path_len);
321
if (path[path_len-1] != FILE_SEP) {
322
path[path_len++] = FILE_SEP;
324
PORT_Strcpy(&path[path_len],dllname);
325
if (secmod_len > 0) {
326
lastsep = PORT_Strrchr(secmodprefix, FILE_SEP);
328
int secmoddir_len = lastsep-secmodprefix+1; /* FILE_SEP */
329
oldpath = PORT_Alloc(len);
330
if (oldpath == NULL) {
334
PORT_Memcpy(oldpath,path,path_len);
335
PORT_Memcpy(&oldpath[path_len],secmodprefix,secmoddir_len);
336
PORT_Strcpy(&oldpath[path_len+secmoddir_len],dllname);
339
*retoldpath = oldpath;
344
static void nss_FreeExternalRootPaths(char* oldpath, char* path)
355
nss_FindExternalRoot(const char *dbpath, const char* secmodprefix)
358
char *oldpath = NULL;
359
PRBool hasrootcerts = PR_FALSE;
362
* 'oldpath' is the external root path in NSS 3.3.x or older.
363
* For backward compatibility we try to load the root certs
364
* module with the old path first.
366
nss_FindExternalRootPaths(dbpath, secmodprefix, &oldpath, &path);
368
(void) SECMOD_AddNewModule("Root Certs",oldpath, 0, 0);
369
hasrootcerts = SECMOD_HasRootCerts();
371
if (path && !hasrootcerts) {
372
(void) SECMOD_AddNewModule("Root Certs",path, 0, 0);
374
nss_FreeExternalRootPaths(oldpath, path);
380
* OK there are now lots of options here, lets go through them all:
382
* configdir - base directory where all the cert, key, and module datbases live.
383
* certPrefix - prefix added to the beginning of the cert database example: "
385
* keyPrefix - prefix added to the beginning of the key database example: "
387
* secmodName - name of the security module database (usually "secmod.db").
388
* readOnly - Boolean: true if the databases are to be openned read only.
389
* nocertdb - Don't open the cert DB and key DB's, just initialize the
391
* nomoddb - Don't open the security module DB, just initialize the
393
* forceOpen - Continue to force initializations even if the databases cannot
397
static PRBool nss_IsInitted = PR_FALSE;
399
extern SECStatus secoid_Init(void);
400
static SECStatus nss_InitShutdownList(void);
403
static CERTCertificate dummyCert;
407
nss_Init(const char *configdir, const char *certPrefix, const char *keyPrefix,
408
const char *secmodName, PRBool readOnly, PRBool noCertDB,
409
PRBool noModDB, PRBool forceOpen, PRBool noRootInit,
410
PRBool optimizeSpace, PRBool noSingleThreadedModules,
411
PRBool allowAlreadyInitializedModules,
412
PRBool dontFinalizeModules)
414
char *moduleSpec = NULL;
416
SECStatus rv = SECFailure;
417
char *lconfigdir = NULL;
418
char *lcertPrefix = NULL;
419
char *lkeyPrefix = NULL;
420
char *lsecmodName = NULL;
426
/* New option bits must not change the size of CERTCertificate. */
427
PORT_Assert(sizeof(dummyCert.options) == sizeof(void *));
429
if (SECSuccess != InitCRLCache()) {
433
if (SECSuccess != InitOCSPGlobal()) {
437
flags = nss_makeFlags(readOnly,noCertDB,noModDB,forceOpen,
438
pk11_password_required, optimizeSpace);
439
if (flags == NULL) return rv;
442
* configdir is double nested, and Windows uses the same character
443
* for file seps as we use for escapes! (sigh).
445
lconfigdir = nss_doubleEscape(configdir);
446
if (lconfigdir == NULL) {
449
lcertPrefix = nss_doubleEscape(certPrefix);
450
if (lcertPrefix == NULL) {
453
lkeyPrefix = nss_doubleEscape(keyPrefix);
454
if (lkeyPrefix == NULL) {
457
lsecmodName = nss_doubleEscape(secmodName);
458
if (lsecmodName == NULL) {
461
if (noSingleThreadedModules || allowAlreadyInitializedModules ||
462
dontFinalizeModules) {
463
pk11_setGlobalOptions(noSingleThreadedModules,
464
allowAlreadyInitializedModules,
465
dontFinalizeModules);
468
moduleSpec = PR_smprintf("name=\"%s\" parameters=\"configdir='%s' certPrefix='%s' keyPrefix='%s' secmod='%s' flags=%s %s\" NSS=\"flags=internal,moduleDB,moduleDBOnly,critical\"",
469
pk11_config_name ? pk11_config_name : NSS_DEFAULT_MOD_NAME,
470
lconfigdir,lcertPrefix,lkeyPrefix,lsecmodName,flags,
471
pk11_config_strings ? pk11_config_strings : "");
475
if (lconfigdir) PORT_Free(lconfigdir);
476
if (lcertPrefix) PORT_Free(lcertPrefix);
477
if (lkeyPrefix) PORT_Free(lkeyPrefix);
478
if (lsecmodName) PORT_Free(lsecmodName);
481
SECMODModule *module = SECMOD_LoadModule(moduleSpec,NULL,PR_TRUE);
482
PR_smprintf_free(moduleSpec);
484
if (module->loaded) rv=SECSuccess;
485
SECMOD_DestroyModule(module);
489
if (rv == SECSuccess) {
490
if (secoid_Init() != SECSuccess) {
493
if (STAN_LoadDefaultNSS3TrustDomain() != PR_SUCCESS) {
496
if (nss_InitShutdownList() != SECSuccess) {
499
CERT_SetDefaultCertDB((CERTCertDBHandle *)
500
STAN_GetDefaultTrustDomain());
502
/* only servers need this. We currently do not have a mac server */
503
if ((!noModDB) && (!noCertDB) && (!noRootInit)) {
504
if (!SECMOD_HasRootCerts()) {
505
nss_FindExternalRoot(configdir, secmodName);
510
cert_CreateSubjectKeyIDHashTable();
511
nss_IsInitted = PR_TRUE;
518
NSS_Init(const char *configdir)
520
return nss_Init(configdir, "", "", SECMOD_DB, PR_TRUE,
521
PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE, PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE);
525
NSS_InitReadWrite(const char *configdir)
527
return nss_Init(configdir, "", "", SECMOD_DB, PR_FALSE,
528
PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE, PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE);
532
* OK there are now lots of options here, lets go through them all:
534
* configdir - base directory where all the cert, key, and module datbases live.
535
* certPrefix - prefix added to the beginning of the cert database example: "
537
* keyPrefix - prefix added to the beginning of the key database example: "
539
* secmodName - name of the security module database (usually "secmod.db").
540
* flags - change the open options of NSS_Initialize as follows:
541
* NSS_INIT_READONLY - Open the databases read only.
542
* NSS_INIT_NOCERTDB - Don't open the cert DB and key DB's, just
543
* initialize the volatile certdb.
544
* NSS_INIT_NOMODDB - Don't open the security module DB, just
545
* initialize the PKCS #11 module.
546
* NSS_INIT_FORCEOPEN - Continue to force initializations even if the
547
* databases cannot be opened.
548
* NSS_INIT_PK11THREADSAFE - only load PKCS#11 modules that are
549
* thread-safe, ie. that support locking - either OS
550
* locking or NSS-provided locks . If a PKCS#11
551
* module isn't thread-safe, don't serialize its
552
* calls; just don't load it instead. This is necessary
553
* if another piece of code is using the same PKCS#11
554
* modules that NSS is accessing without going through
555
* NSS, for example the Java SunPKCS11 provider.
556
* NSS_INIT_PK11RELOAD - ignore the CKR_CRYPTOKI_ALREADY_INITIALIZED
557
* error when loading PKCS#11 modules. This is necessary
558
* if another piece of code is using the same PKCS#11
559
* modules that NSS is accessing without going through
560
* NSS, for example Java SunPKCS11 provider.
561
* NSS_INIT_NOPK11FINALIZE - never call C_Finalize on any
562
* PKCS#11 module. This may be necessary in order to
563
* ensure continuous operation and proper shutdown
564
* sequence if another piece of code is using the same
565
* PKCS#11 modules that NSS is accessing without going
566
* through NSS, for example Java SunPKCS11 provider.
567
* The following limitation applies when this is set :
568
* SECMOD_WaitForAnyTokenEvent will not use
569
* C_WaitForSlotEvent, in order to prevent the need for
570
* C_Finalize. This call will be emulated instead.
571
* NSS_INIT_RESERVED - Currently has no effect, but may be used in the
572
* future to trigger better cooperation between PKCS#11
573
* modules used by both NSS and the Java SunPKCS11
574
* provider. This should occur after a new flag is defined
575
* for C_Initialize by the PKCS#11 working group.
576
* NSS_INIT_COOPERATE - Sets 4 recommended options for applications that
577
* use both NSS and the Java SunPKCS11 provider.
580
NSS_Initialize(const char *configdir, const char *certPrefix,
581
const char *keyPrefix, const char *secmodName, PRUint32 flags)
583
return nss_Init(configdir, certPrefix, keyPrefix, secmodName,
584
((flags & NSS_INIT_READONLY) == NSS_INIT_READONLY),
585
((flags & NSS_INIT_NOCERTDB) == NSS_INIT_NOCERTDB),
586
((flags & NSS_INIT_NOMODDB) == NSS_INIT_NOMODDB),
587
((flags & NSS_INIT_FORCEOPEN) == NSS_INIT_FORCEOPEN),
588
((flags & NSS_INIT_NOROOTINIT) == NSS_INIT_NOROOTINIT),
589
((flags & NSS_INIT_OPTIMIZESPACE) == NSS_INIT_OPTIMIZESPACE),
590
((flags & NSS_INIT_PK11THREADSAFE) == NSS_INIT_PK11THREADSAFE),
591
((flags & NSS_INIT_PK11RELOAD) == NSS_INIT_PK11RELOAD),
592
((flags & NSS_INIT_NOPK11FINALIZE) == NSS_INIT_NOPK11FINALIZE));
596
* initialize NSS without a creating cert db's, key db's, or secmod db's.
599
NSS_NoDB_Init(const char * configdir)
601
return nss_Init("","","","",
602
PR_TRUE,PR_TRUE,PR_TRUE,PR_TRUE,PR_TRUE,PR_TRUE,
603
PR_FALSE,PR_FALSE,PR_FALSE);
607
#define NSS_SHUTDOWN_STEP 10
609
struct NSSShutdownFuncPair {
610
NSS_ShutdownFunc func;
614
static struct NSSShutdownListStr {
618
struct NSSShutdownFuncPair *funcs;
619
} nssShutdownList = { 0 };
622
* find and existing shutdown function
625
nss_GetShutdownEntry(NSS_ShutdownFunc sFunc, void *appData)
628
count = nssShutdownList.numFuncs;
629
/* expect the list to be short, just do a linear search */
630
for (i=0; i < count; i++) {
631
if ((nssShutdownList.funcs[i].func == sFunc) &&
632
(nssShutdownList.funcs[i].appData == appData)){
640
* register a callback to be called when NSS shuts down
643
NSS_RegisterShutdown(NSS_ShutdownFunc sFunc, void *appData)
647
if (!nss_IsInitted) {
648
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
652
PORT_SetError(SEC_ERROR_INVALID_ARGS);
656
PORT_Assert(nssShutdownList.lock);
657
PZ_Lock(nssShutdownList.lock);
659
/* make sure we don't have a duplicate */
660
i = nss_GetShutdownEntry(sFunc, appData);
662
PZ_Unlock(nssShutdownList.lock);
663
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
666
/* find an empty slot */
667
i = nss_GetShutdownEntry(NULL, NULL);
669
nssShutdownList.funcs[i].func = sFunc;
670
nssShutdownList.funcs[i].appData = appData;
671
PZ_Unlock(nssShutdownList.lock);
674
if (nssShutdownList.maxFuncs == nssShutdownList.numFuncs) {
675
struct NSSShutdownFuncPair *funcs =
676
(struct NSSShutdownFuncPair *)PORT_Realloc
677
(nssShutdownList.funcs,
678
(nssShutdownList.maxFuncs + NSS_SHUTDOWN_STEP)
679
*sizeof(struct NSSShutdownFuncPair));
683
nssShutdownList.funcs = funcs;
684
nssShutdownList.maxFuncs += NSS_SHUTDOWN_STEP;
686
nssShutdownList.funcs[nssShutdownList.numFuncs].func = sFunc;
687
nssShutdownList.funcs[nssShutdownList.numFuncs].appData = appData;
688
nssShutdownList.numFuncs++;
689
PZ_Unlock(nssShutdownList.lock);
694
* unregister a callback so it won't get called on shutdown.
697
NSS_UnregisterShutdown(NSS_ShutdownFunc sFunc, void *appData)
700
if (!nss_IsInitted) {
701
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
705
PORT_Assert(nssShutdownList.lock);
706
PZ_Lock(nssShutdownList.lock);
707
i = nss_GetShutdownEntry(sFunc, appData);
709
nssShutdownList.funcs[i].func = NULL;
710
nssShutdownList.funcs[i].appData = NULL;
712
PZ_Unlock(nssShutdownList.lock);
715
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
722
* bring up and shutdown the shutdown list
725
nss_InitShutdownList(void)
727
nssShutdownList.lock = PZ_NewLock(nssILockOther);
728
if (nssShutdownList.lock == NULL) {
731
nssShutdownList.funcs = PORT_ZNewArray(struct NSSShutdownFuncPair,
733
if (nssShutdownList.funcs == NULL) {
734
PZ_DestroyLock(nssShutdownList.lock);
735
nssShutdownList.lock = NULL;
738
nssShutdownList.maxFuncs = NSS_SHUTDOWN_STEP;
739
nssShutdownList.numFuncs = 0;
745
nss_ShutdownShutdownList(void)
747
SECStatus rv = SECSuccess;
750
/* call all the registerd functions first */
751
for (i=0; i < nssShutdownList.numFuncs; i++) {
752
struct NSSShutdownFuncPair *funcPair = &nssShutdownList.funcs[i];
753
if (funcPair->func) {
754
if ((*funcPair->func)(funcPair->appData,NULL) != SECSuccess) {
760
nssShutdownList.numFuncs = 0;
761
nssShutdownList.maxFuncs = 0;
762
PORT_Free(nssShutdownList.funcs);
763
nssShutdownList.funcs = NULL;
764
if (nssShutdownList.lock) {
765
PZ_DestroyLock(nssShutdownList.lock);
767
nssShutdownList.lock = NULL;
772
extern const NSSError NSS_ERROR_BUSY;
777
SECStatus shutdownRV = SECSuccess;
781
if (!nss_IsInitted) {
782
PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
786
rv = nss_ShutdownShutdownList();
787
if (rv != SECSuccess) {
788
shutdownRV = SECFailure;
792
status = STAN_Shutdown();
793
cert_DestroySubjectKeyIDHashTable();
794
rv = SECMOD_Shutdown();
795
if (rv != SECSuccess) {
796
shutdownRV = SECFailure;
799
if (status == PR_FAILURE) {
800
if (NSS_GetError() == NSS_ERROR_BUSY) {
801
PORT_SetError(SEC_ERROR_BUSY);
803
shutdownRV = SECFailure;
805
nss_IsInitted = PR_FALSE;
810
NSS_IsInitialized(void)
812
return nss_IsInitted;
816
extern const char __nss_base_rcsid[];
817
extern const char __nss_base_sccsid[];
820
NSS_VersionCheck(const char *importedVersion)
823
* This is the secret handshake algorithm.
825
* This release has a simple version compatibility
826
* check algorithm. This release is not backward
827
* compatible with previous major releases. It is
828
* not compatible with future major, minor, or
831
int vmajor = 0, vminor = 0, vpatch = 0;
832
const char *ptr = importedVersion;
833
volatile char c; /* force a reference that won't get optimized away */
835
c = __nss_base_rcsid[0] + __nss_base_sccsid[0];
837
while (isdigit(*ptr)) {
838
vmajor = 10 * vmajor + *ptr - '0';
843
while (isdigit(*ptr)) {
844
vminor = 10 * vminor + *ptr - '0';
849
while (isdigit(*ptr)) {
850
vpatch = 10 * vpatch + *ptr - '0';
856
if (vmajor != NSS_VMAJOR) {
859
if (vmajor == NSS_VMAJOR && vminor > NSS_VMINOR) {
862
if (vmajor == NSS_VMAJOR && vminor == NSS_VMINOR && vpatch > NSS_VPATCH) {
865
/* Check dependent libraries */
866
if (PR_VersionCheck(PR_VERSION) == PR_FALSE) {