diff options
author | rrelyea%redhat.com <devnull@localhost> | 2009-10-27 23:04:48 +0000 |
---|---|---|
committer | rrelyea%redhat.com <devnull@localhost> | 2009-10-27 23:04:48 +0000 |
commit | fe21f07bc83703b442b0d91e77baf513800e27d4 (patch) | |
tree | fa80e0e267470079410c4e5efd3b31ef84dd13c8 | |
parent | a6ad5b0e66b95300ec49255465617f1ac462b38b (diff) | |
download | nss-hg-fe21f07bc83703b442b0d91e77baf513800e27d4.tar.gz |
Bug 453495 - Add new ref counted NSS_Shutdown-like function
r=nelson
-rw-r--r-- | security/nss/cmd/manifest.mn | 1 | ||||
-rw-r--r-- | security/nss/cmd/multinit/Makefile | 79 | ||||
-rw-r--r-- | security/nss/cmd/multinit/manifest.mn | 45 | ||||
-rw-r--r-- | security/nss/cmd/multinit/multinit.c | 940 | ||||
-rw-r--r-- | security/nss/lib/nss/nss.def | 3 | ||||
-rw-r--r-- | security/nss/lib/nss/nss.h | 89 | ||||
-rw-r--r-- | security/nss/lib/nss/nssinit.c | 612 | ||||
-rw-r--r-- | security/nss/lib/pk11wrap/pk11load.c | 44 | ||||
-rw-r--r-- | security/nss/lib/pk11wrap/pk11pars.c | 373 | ||||
-rw-r--r-- | security/nss/lib/pk11wrap/pk11priv.h | 1 | ||||
-rw-r--r-- | security/nss/lib/pk11wrap/pk11slot.c | 52 | ||||
-rw-r--r-- | security/nss/lib/pk11wrap/secmodi.h | 15 | ||||
-rwxr-xr-x | security/nss/tests/all.sh | 4 | ||||
-rwxr-xr-x | security/nss/tests/multinit/multinit.sh | 191 | ||||
-rw-r--r-- | security/nss/tests/multinit/multinit.txt | 79 | ||||
-rw-r--r-- | security/nss/tests/tools/tools.sh | 34 |
16 files changed, 2320 insertions, 242 deletions
diff --git a/security/nss/cmd/manifest.mn b/security/nss/cmd/manifest.mn index d1b83e595..dc7c8a777 100644 --- a/security/nss/cmd/manifest.mn +++ b/security/nss/cmd/manifest.mn @@ -56,6 +56,7 @@ DIRS = lib \ digest \ fipstest \ makepqg \ + multinit \ ocspclnt \ oidcalc \ p7content \ diff --git a/security/nss/cmd/multinit/Makefile b/security/nss/cmd/multinit/Makefile new file mode 100644 index 000000000..9ee2a8f00 --- /dev/null +++ b/security/nss/cmd/multinit/Makefile @@ -0,0 +1,79 @@ +#! gmake +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1994-2000 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### + +include ../platlibs.mk + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + + +include ../platrules.mk + diff --git a/security/nss/cmd/multinit/manifest.mn b/security/nss/cmd/multinit/manifest.mn new file mode 100644 index 000000000..78867987e --- /dev/null +++ b/security/nss/cmd/multinit/manifest.mn @@ -0,0 +1,45 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is +# Red Hat, Inc. +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +CORE_DEPTH = ../../.. + +# MODULE public and private header directories are implicitly REQUIRED. +MODULE = nss + +CSRCS = multinit.c + +PROGRAM = multinit diff --git a/security/nss/cmd/multinit/multinit.c b/security/nss/cmd/multinit/multinit.c new file mode 100644 index 000000000..c6ab216a7 --- /dev/null +++ b/security/nss/cmd/multinit/multinit.c @@ -0,0 +1,940 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Red Hat, Inc. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "nss.h" +#include "secutil.h" +#include "pk11pub.h" +#include "cert.h" + +typedef struct commandDescriptStr { + int required; + char *arg; + char *des; +} commandDescript; + +enum optionNames { + opt_liborder = 0, + opt_mainDB, + opt_lib1DB, + opt_lib2DB, + opt_mainRO, + opt_lib1RO, + opt_lib2RO, + opt_mainCMD, + opt_lib1CMD, + opt_lib2CMD, + opt_mainTokNam, + opt_lib1TokNam, + opt_lib2TokNam, + opt_oldStyle, + opt_verbose, + opt_summary, + opt_help, + opt_last +}; + + +static const +secuCommandFlag options_init[] = +{ + { /* opt_liborder */ 'o', PR_TRUE, "1M2zmi", PR_TRUE, "order" }, + { /* opt_mainDB */ 'd', PR_TRUE, 0, PR_FALSE, "main_db" }, + { /* opt_lib1DB */ '1', PR_TRUE, 0, PR_FALSE, "lib1_db" }, + { /* opt_lib2DB */ '2', PR_TRUE, 0, PR_FALSE, "lib2_db" }, + { /* opt_mainRO */ 'r', PR_FALSE, 0, PR_FALSE, "main_readonly" }, + { /* opt_lib1RO */ 0, PR_FALSE, 0, PR_FALSE, "lib1_readonly" }, + { /* opt_lib2RO */ 0, PR_FALSE, 0, PR_FALSE, "lib2_readonly" }, + { /* opt_mainCMD */ 'c', PR_TRUE, 0, PR_FALSE, "main_command" }, + { /* opt_lib1CMD */ 0, PR_TRUE, 0, PR_FALSE, "lib1_command" }, + { /* opt_lib2CMD */ 0, PR_TRUE, 0, PR_FALSE, "lib2_command" }, + { /* opt_mainTokNam */'t', PR_TRUE, 0, PR_FALSE, "main_token_name" }, + { /* opt_lib1TokNam */ 0, PR_TRUE, 0, PR_FALSE, "lib1_token_name" }, + { /* opt_lib2TokNam */ 0, PR_TRUE, 0, PR_FALSE, "lib2_token_name" }, + { /* opt_oldStype */ 's', PR_FALSE, 0, PR_FALSE, "oldStype" }, + { /* opt_verbose */ 'v', PR_FALSE, 0, PR_FALSE, "verbose" }, + { /* opt_summary */ 'z', PR_FALSE, 0, PR_FALSE, "summary" }, + { /* opt_help */ 'h', PR_FALSE, 0, PR_FALSE, "help" } +}; + +static const +commandDescript options_des[] = +{ + { /* opt_liborder */ PR_FALSE, "initOrder", + " Specifies the order of NSS initialization and shutdown. Order is\n" + " given as a string where each character represents either an init or\n" + " a shutdown of the main program or one of the 2 test libraries\n" + " (library 1 and library 2). The valid characters are as follows:\n" + " M Init the main program\n 1 Init library 1\n" + " 2 Init library 2\n" + " m Shutdown the main program\n i Shutdown library 1\n" + " z Shutdown library 2\n" }, + { /* opt_mainDB */ PR_TRUE, "nss_db", + " Specified the directory to open the nss database for the main\n" + " program. Must be specified if \"M\" is given in the order string\n"}, + { /* opt_lib1DB */ PR_FALSE, "nss_db", + " Specified the directory to open the nss database for library 1.\n" + " Must be specified if \"1\" is given in the order string\n"}, + { /* opt_lib2DB */ PR_FALSE, "nss_db", + " Specified the directory to open the nss database for library 2.\n" + " Must be specified if \"2\" is given in the order string\n"}, + { /* opt_mainRO */ PR_FALSE, NULL, + " Open the main program's database read only.\n" }, + { /* opt_lib1RO */ PR_FALSE, NULL, + " Open library 1's database read only.\n" }, + { /* opt_lib2RO */ PR_FALSE, NULL, + " Open library 2's database read only.\n" }, + { /* opt_mainCMD */ PR_FALSE, "nss_command", + " Specifies the NSS command to execute in the main program.\n" + " Valid commands are: \n" + " key_slot, list_slots, list_certs, add_cert, none.\n" + " Default is \"none\".\n" }, + { /* opt_lib1CMD */ PR_FALSE, "nss_command", + " Specifies the NSS command to execute in library 1.\n" }, + { /* opt_lib2CMD */ PR_FALSE, "nss_command", + " Specifies the NSS command to execute in library 2.\n" }, + { /* opt_mainTokNam */PR_FALSE, "token_name", + " Specifies the name of PKCS11 token for the main program's " + "database.\n" }, + { /* opt_lib1TokNam */PR_FALSE, "token_name", + " Specifies the name of PKCS11 token for library 1's database.\n" }, + { /* opt_lib2TokNam */PR_FALSE, "token_name", + " Specifies the name of PKCS11 token for library 2's database.\n" }, + { /* opt_oldStype */ PR_FALSE, NULL, + " Use NSS_Shutdown rather than NSS_ShutdownContext in the main\n" + " program.\n" }, + { /* opt_verbose */ PR_FALSE, NULL, + " Noisily output status to standard error\n" }, + { /* opt_summarize */ PR_FALSE, NULL, + "report a summary of the test results\n" }, + { /* opt_help */ PR_FALSE, NULL, " give this message\n" } +}; + +/* + * output our short help (table driven). (does not exit). + */ +static void +short_help(const char *prog) +{ + int count = opt_last; + int i,words_found; + + /* make sure all the tables are up to date before we allow compiles to + * succeed */ + PR_STATIC_ASSERT(sizeof(options_init)/sizeof(secuCommandFlag) == opt_last); + PR_STATIC_ASSERT(sizeof(options_init)/sizeof(secuCommandFlag) == + sizeof(options_des)/sizeof(commandDescript)); + + /* print the base usage */ + fprintf(stderr,"usage: %s ",prog); + for (i=0, words_found=0; i < count; i++) { + if (!options_des[i].required) { + fprintf(stderr,"["); + } + if (options_init[i].longform) { + fprintf(stderr, "--%s", options_init[i].longform); + words_found++; + } else { + fprintf(stderr, "-%c", options_init[i].flag); + } + if (options_init[i].needsArg) { + if (options_des[i].arg) { + fprintf(stderr," %s",options_des[i].arg); + } else { + fprintf(stderr," arg"); + } + words_found++; + } + if (!options_des[i].required) { + fprintf(stderr,"]"); + } + if (i < count-1 ) { + if (words_found >= 5) { + fprintf(stderr,"\n "); + words_found=0; + } else { + fprintf(stderr," "); + } + } + } + fprintf(stderr,"\n"); +} + +/* + * print out long help. like short_help, this does not exit + */ +static void +long_help(const char *prog) +{ + int i; + int count = opt_last; + + short_help(prog); + /* print the option descriptions */ + fprintf(stderr,"\n"); + for (i=0; i < count; i++) { + fprintf(stderr," "); + if (options_init[i].flag) { + fprintf(stderr, "-%c", options_init[i].flag); + if (options_init[i].longform) { + fprintf(stderr,","); + } + } + if (options_init[i].longform) { + fprintf(stderr,"--%s", options_init[i].longform); + } + if (options_init[i].needsArg) { + if (options_des[i].arg) { + fprintf(stderr," %s",options_des[i].arg); + } else { + fprintf(stderr," arg"); + } + if (options_init[i].arg) { + fprintf(stderr," (default = \"%s\")",options_init[i].arg); + } + } + fprintf(stderr,"\n%s",options_des[i].des); + } +} + +/* + * record summary data + */ +struct bufferData { + char * data; /* lowest address of the buffer */ + char * next; /* pointer to the next element on the buffer */ + int len; /* length of the buffer */ +}; + +/* our actual buffer. If data is NULL, then all append ops + * except are noops */ +static struct bufferData buffer= { NULL, NULL, 0 }; + +#define CHUNK_SIZE 1000 + +/* + * get our initial data. and set the buffer variables up. on failure, + * just don't initialize the buffer. + */ +static void +initBuffer(void) +{ + buffer.data = PORT_Alloc(CHUNK_SIZE); + if (!buffer.data) { + return; + } + buffer.next = buffer.data; + buffer.len = CHUNK_SIZE; +} + +/* + * grow the buffer. If we can't get more data, record a 'D' in the second + * to last record and allow the rest of the data to overwrite the last + * element. + */ +static void +growBuffer(void) +{ + char *new = PORT_Realloc(buffer.data, buffer.len + CHUNK_SIZE); + if (!new) { + buffer.data[buffer.len-2] = 'D'; /* signal malloc failure in summary */ + /* buffer must always point to good memory if it exists */ + buffer.next = buffer.data + (buffer.len -1); + return; + } + buffer.next = new + (buffer.next-buffer.data); + buffer.data = new; + buffer.len += CHUNK_SIZE; +} + +/* + * append a label, doubles as appending a single character. + */ +static void +appendLabel(char label) +{ + if (!buffer.data) { + return; + } + + *buffer.next++ = label; + if (buffer.data+buffer.len >= buffer.next) { + growBuffer(); + } +} + +/* + * append a string onto the buffer. The result will be <string> + */ +static void +appendString(char *string) +{ + if (!buffer.data) { + return; + } + + appendLabel('<'); + while (*string) { + appendLabel(*string++); + } + appendLabel('>'); +} + +/* + * append a bool, T= true, F=false + */ +static void +appendBool(PRBool bool) +{ + if (!buffer.data) { + return; + } + + if (bool) { + appendLabel('t'); + } else { + appendLabel('f'); + } +} + +/* + * append a single hex nibble. + */ +static void +appendHex(unsigned char nibble) +{ + if (nibble <= 9) { + appendLabel('0'+nibble); + } else { + appendLabel('a'+nibble-10); + } +} + +/* + * append a secitem as colon separated hex bytes. + */ +static void +appendItem(SECItem *item) +{ + int i; + + if (!buffer.data) { + return; + } + + appendLabel(':'); + for (i=0; i < item->len; i++) { + unsigned char byte=item->data[i]; + appendHex(byte >> 4); + appendHex(byte & 0xf); + appendLabel(':'); + } +} + +/* + * append a 32 bit integer (even on a 64 bit platform). + * for simplicity append it as a hex value, full extension with 0x prefix. + */ +static void +appendInt(unsigned int value) +{ + int i; + + if (!buffer.data) { + return; + } + + appendLabel('0'); + appendLabel('x'); + value = value & 0xffffffff; /* only look at the buttom 8 bytes */ + for (i=0; i < 8; i++) { + appendHex(value >> 28 ); + value = value << 4; + } +} + +/* append a trust flag */ +static void +appendFlags(unsigned int flag) +{ + char trust[10]; + char *cp=trust; + + trust[0] = 0; + printflags(trust, flag); + while (*cp) { + appendLabel(*cp++); + } +} + +/* + * dump our buffer out with a result= flag so we can find it easily. + * free the buffer as a side effect. + */ +static void +dumpBuffer(void) +{ + if (!buffer.data) { + return; + } + + appendLabel(0); /* terminate */ + printf("\nresult=%s\n",buffer.data); + PORT_Free(buffer.data); + buffer.data = buffer.next = NULL; + buffer.len = 0; +} + + +/* + * usage, like traditional usage, automatically exit + */ +static void +usage(const char *prog) +{ + short_help(prog); + dumpBuffer(); + exit(1); +} + +/* + * like usage, except prints the long version of help + */ +static void +usage_long(const char *prog) +{ + long_help(prog); + dumpBuffer(); + exit(1); +} + +static const char * +bool2String(PRBool bool) +{ + return bool ? "true" : "false"; +} + +/* + * print out interesting info about the given slot + */ +void +print_slot(PK11SlotInfo *slot, int log) +{ + if (log) { + fprintf(stderr, "* Name=%s Token_Name=%s present=%s, ro=%s *\n", + PK11_GetSlotName(slot), PK11_GetTokenName(slot), + bool2String(PK11_IsPresent(slot)), + bool2String(PK11_IsReadOnly(slot))); + } + appendLabel('S'); + appendString(PK11_GetTokenName(slot)); + appendBool(PK11_IsPresent(slot)); + appendBool(PK11_IsReadOnly(slot)); +} + +/* + * list all our slots + */ +void +do_list_slots(const char *progName, int log) +{ + PK11SlotList *list; + PK11SlotListElement *le; + + list= PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, NULL); + if (list == NULL) { + fprintf(stderr,"ERROR: no tokens found %s\n", + SECU_Strerror(PORT_GetError())); + appendLabel('S'); + appendString("none"); + return; + } + + for (le= PK11_GetFirstSafe(list); le; + le = PK11_GetNextSafe(list,le,PR_TRUE)) { + print_slot(le->slot, log); + } + PK11_FreeSlotList(list); +} + +static PRBool +sort_CN(CERTCertificate *certa, CERTCertificate *certb, void *arg) +{ + char *commonNameA, *commonNameB; + int ret; + + commonNameA = CERT_GetCommonName(&certa->subject); + commonNameB = CERT_GetCommonName(&certb->subject); + + if (commonNameA == NULL) { + PORT_Free(commonNameB); + return PR_TRUE; + } + if (commonNameB == NULL) { + PORT_Free(commonNameA); + return PR_FALSE; + } + ret = PORT_Strcmp(commonNameA,commonNameB); + PORT_Free(commonNameA); + PORT_Free(commonNameB); + return (ret < 0) ? PR_TRUE: PR_FALSE; +} + +/* + * list all the certs + */ +void +do_list_certs(const char *progName, int log) +{ + CERTCertList *list; + CERTCertList *sorted; + CERTCertListNode *node; + int i; + + list = PK11_ListCerts(PK11CertListUnique, NULL); + if (list == NULL) { + fprintf(stderr,"ERROR: no certs found %s\n", + SECU_Strerror(PORT_GetError())); + appendLabel('C'); + appendString("none"); + return; + } + + sorted = CERT_NewCertList(); + if (sorted == NULL) { + fprintf(stderr,"ERROR: no certs found %s\n", + SECU_Strerror(PORT_GetError())); + appendLabel('C'); + appendLabel('E'); + appendInt(PORT_GetError()); + return; + } + + /* sort the list */ + for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node,list); + node = CERT_LIST_NEXT(node)) { + CERT_AddCertToListSorted(sorted, node->cert, sort_CN, NULL); + } + + + for (node = CERT_LIST_HEAD(sorted); !CERT_LIST_END(node,sorted); + node = CERT_LIST_NEXT(node)) { + CERTCertificate *cert = node->cert; + char *commonName; + + SECU_PrintCertNickname(node, stderr); + if (log) { + fprintf(stderr, "* Slot=%s*\n", cert->slot ? + PK11_GetTokenName(cert->slot) : "none"); + fprintf(stderr, "* Nickname=%s*\n", cert->nickname); + fprintf(stderr, "* Subject=<%s>*\n", cert->subjectName); + fprintf(stderr, "* Issuer=<%s>*\n", cert->issuerName); + fprintf(stderr, "* SN="); + for (i=0; i < cert->serialNumber.len; i++) { + if (i!=0) fprintf(stderr,":"); + fprintf(stderr, "%02x",cert->serialNumber.data[0]); + } + fprintf(stderr," *\n"); + } + appendLabel('C'); + commonName = CERT_GetCommonName(&cert->subject); + appendString(commonName?commonName:"*NoName*"); + PORT_Free(commonName); + if (cert->trust) { + appendFlags(cert->trust->sslFlags); + appendFlags(cert->trust->emailFlags); + appendFlags(cert->trust->objectSigningFlags); + } + } + CERT_DestroyCertList(list); + +} + +/* + * need to implement yet... try to add a new certificate + */ +void +do_add_cert(const char *progName, int log) +{ + PORT_Assert(/* do_add_cert not implemented */ 0); +} + +/* + * display the current key slot + */ +void +do_key_slot(const char *progName, int log) +{ + PK11SlotInfo *slot = PK11_GetInternalKeySlot(); + if (!slot) { + fprintf(stderr,"ERROR: no internal key slot found %s\n", + SECU_Strerror(PORT_GetError())); + appendLabel('K'); + appendLabel('S'); + appendString("none"); + } + print_slot(slot, log); + PK11_FreeSlot(slot); +} + +/* + * execute some NSS command. + */ +void +do_command(const char *label, int initialized, secuCommandFlag *command, + const char *progName, int log) +{ + char * command_string; + if (!initialized) { + return; + } + + if (command->activated) { + command_string = command->arg; + } else { + command_string = "none"; + } + + if (log) { + fprintf(stderr, "*Executing nss command \"%s\" for %s*\n", + command_string,label); + } + + /* do something */ + if (strcasecmp(command_string, "list_slots") == 0) { + do_list_slots(progName, log); + } else if (strcasecmp(command_string, "list_certs") == 0) { + do_list_certs(progName, log); + } else if (strcasecmp(command_string, "add_cert") == 0) { + do_add_cert(progName, log); + } else if (strcasecmp(command_string, "key_slot") == 0) { + do_key_slot(progName, log); + } else if (strcasecmp(command_string, "none") != 0) { + fprintf(stderr, ">> Unknown command (%s)\n", command_string); + appendLabel('E'); + appendString("bc"); + usage_long(progName); + } + +} + + +/* + * functions do handle + * different library initializations. + */ +static int main_initialized; +static int lib1_initialized; +static int lib2_initialized; + +void +main_Init(secuCommandFlag *db, secuCommandFlag *tokNam, + int readOnly, const char *progName, int log) +{ + SECStatus rv; + if (log) { + fprintf(stderr,"*NSS_Init for the main program*\n"); + } + appendLabel('M'); + if (!db->activated) { + fprintf(stderr, ">> No main_db has been specified\n"); + usage(progName); + } + if (main_initialized) { + fprintf(stderr,"Warning: Second initialization of Main\n"); + appendLabel('E'); + appendString("2M"); + } + if (tokNam->activated) { + PK11_ConfigurePKCS11(NULL, NULL, NULL, tokNam->arg, + NULL, NULL, NULL, NULL, 0, 0); + } + rv = NSS_Initialize(db->arg, "", "", "", + NSS_INIT_NOROOTINIT|(readOnly?NSS_INIT_READONLY:0)); + if (rv != SECSuccess) { + appendLabel('E'); + appendInt(PORT_GetError()); + fprintf(stderr,">> %s\n", SECU_Strerror(PORT_GetError())); + dumpBuffer(); + exit(1); + } + main_initialized = 1; +} + +void +main_Do(secuCommandFlag *command, const char *progName, int log) +{ + do_command("main", main_initialized, command, progName, log); +} + +void +main_Shutdown(int old_style, const char *progName, int log) +{ + SECStatus rv; + appendLabel('N'); + if (log) { + fprintf(stderr,"*NSS_Shutdown for the main program*\n"); + } + if (!main_initialized) { + fprintf(stderr,"Warning: Main shutdown without corresponding init\n"); + } + if (old_style) { + rv = NSS_Shutdown(); + } else { + rv = NSS_ShutdownContext(NULL); + } + fprintf(stderr, "Shutdown main state = %d\n", rv); + if (rv != SECSuccess) { + appendLabel('E'); + appendInt(PORT_GetError()); + fprintf(stderr,"ERROR: %s\n", SECU_Strerror(PORT_GetError())); + } + main_initialized = 0; +} + +/* common library init */ +NSSInitContext * +lib_Init(const char *lableString, char label, int initialized, + secuCommandFlag *db, secuCommandFlag *tokNam, int readonly, + const char *progName, int log) +{ + NSSInitContext *ctxt; + NSSInitParameters initStrings; + NSSInitParameters *initStringPtr = NULL; + + appendLabel(label); + if (log) { + fprintf(stderr,"*NSS_Init for %s*\n", lableString); + } + + if (!db->activated) { + fprintf(stderr, ">> No %s_db has been specified\n", lableString); + usage(progName); + } + if (initialized) { + fprintf(stderr,"Warning: Second initialization of %s\n", lableString); + } + if (tokNam->activated) { + PORT_Memset(&initStrings, 0, sizeof(initStrings)); + initStrings.length = sizeof(initStrings); + initStrings.dbTokenDescription = tokNam->arg; + initStringPtr = &initStrings; + } + ctxt = NSS_InitContext(db->arg, "", "", "", initStringPtr, + NSS_INIT_NOROOTINIT|(readonly?NSS_INIT_READONLY:0)); + if (ctxt == NULL) { + appendLabel('E'); + appendInt(PORT_GetError()); + fprintf(stderr,">> %s\n",SECU_Strerror(PORT_GetError())); + dumpBuffer(); + exit(1); + } + return ctxt; +} + +/* common library shutdown */ +void +lib_Shutdown(const char *labelString, char label, NSSInitContext *ctx, + int initialize, const char *progName, int log) +{ + SECStatus rv; + appendLabel(label); + if (log) { + fprintf(stderr,"*NSS_Shutdown for %s\n*", labelString); + } + if (!initialize) { + fprintf(stderr,"Warning: %s shutdown without corresponding init\n", + labelString); + } + rv = NSS_ShutdownContext(ctx); + fprintf(stderr, "Shutdown %s state = %d\n", labelString, rv); + if (rv != SECSuccess) { + appendLabel('E'); + appendInt(PORT_GetError()); + fprintf(stderr,"ERROR: %s\n", SECU_Strerror(PORT_GetError())); + } +} + + +static NSSInitContext *lib1_context; +static NSSInitContext *lib2_context; +void +lib1_Init(secuCommandFlag *db, secuCommandFlag *tokNam, + int readOnly, const char *progName, int log) +{ + lib1_context = lib_Init("lib1", '1', lib1_initialized, db, tokNam, + readOnly, progName, log); + lib1_initialized = 1; +} + +void +lib2_Init(secuCommandFlag *db, secuCommandFlag *tokNam, + int readOnly, const char *progName, int log) +{ + lib2_context = lib_Init("lib2", '2', lib2_initialized, + db, tokNam, readOnly, progName, log); + lib2_initialized = 1; +} + +void +lib1_Do(secuCommandFlag *command, const char *progName, int log) +{ + do_command("lib1", lib1_initialized, command, progName, log); +} + +void +lib2_Do(secuCommandFlag *command, const char *progName, int log) +{ + do_command("lib2", lib2_initialized, command, progName, log); +} + +void +lib1_Shutdown(const char *progName, int log) +{ + lib_Shutdown("lib1", 'I', lib1_context, lib1_initialized, progName, log); + lib1_initialized = 0; + /* don't clear lib1_Context, so we can test multiple attempts to close + * the same context produces correct errors*/ +} + +void +lib2_Shutdown(const char *progName, int log) +{ + lib_Shutdown("lib2", 'Z', lib2_context, lib2_initialized, progName, log); + lib2_initialized = 0; + /* don't clear lib2_Context, so we can test multiple attempts to close + * the same context produces correct errors*/ +} + +int +main(int argc, char **argv) +{ + SECStatus rv; + secuCommand libinit; + char *progName; + char *order; + secuCommandFlag *options; + int log = 0; + + progName = strrchr(argv[0], '/'); + progName = progName ? progName+1 : argv[0]; + + libinit.numCommands = 0; + libinit.commands = 0; + libinit.numOptions = opt_last; + options = (secuCommandFlag *)PORT_Alloc(sizeof(options_init)); + if (options == NULL) { + fprintf(stderr, ">> %s:Not enough free memory to run command\n", + progName); + exit(1); + } + PORT_Memcpy(options, options_init, sizeof(options_init)); + libinit.options = options; + + rv = SECU_ParseCommandLine(argc, argv, progName, & libinit); + if (rv != SECSuccess) { + usage(progName); + } + + if (libinit.options[opt_help].activated) { + long_help(progName); + exit (0); + } + + log = libinit.options[opt_verbose].activated; + if (libinit.options[opt_summary].activated) { + initBuffer(); + } + + order = libinit.options[opt_liborder].arg; + if (!order) { + usage(progName); + } + + if (log) { + fprintf(stderr,"* initializing with order \"%s\"*\n", order); + } + + for (;*order; order++) { + switch (*order) { + case 'M': + main_Init(&libinit.options[opt_mainDB], + &libinit.options[opt_mainTokNam], + libinit.options[opt_mainRO].activated, + progName, log); + break; + case '1': + lib1_Init(&libinit.options[opt_lib1DB], + &libinit.options[opt_lib1TokNam], + libinit.options[opt_lib1RO].activated, + progName,log); + break; + case '2': + lib2_Init(&libinit.options[opt_lib2DB], + &libinit.options[opt_lib2TokNam], + libinit.options[opt_lib2RO].activated, + progName,log); + break; + case 'm': + main_Shutdown(libinit.options[opt_oldStyle].activated, + progName, log); + break; + case 'i': + lib1_Shutdown(progName, log); + break; + case 'z': + lib2_Shutdown(progName, log); + break; + default: + fprintf(stderr,">> Unknown init/shutdown command \"%c\"", *order); + usage_long(progName); + } + main_Do(&libinit.options[opt_mainCMD], progName, log); + lib1_Do(&libinit.options[opt_lib1CMD], progName, log); + lib2_Do(&libinit.options[opt_lib2CMD], progName, log); + } + + if (NSS_IsInitialized()) { + appendLabel('X'); + fprintf(stderr, "Warning: NSS is initialized\n"); + } + dumpBuffer(); + + exit(0); +} + diff --git a/security/nss/lib/nss/nss.def b/security/nss/lib/nss/nss.def index d8b47de8d..e058e6f11 100644 --- a/security/nss/lib/nss/nss.def +++ b/security/nss/lib/nss/nss.def @@ -981,6 +981,9 @@ SECMOD_OpenNewSlot; ;+}; ;+NSS_3.12.5 { # NSS 3.12.5 release ;+ global: +CERT_AddCertToListSorted; +NSS_InitContext; +NSS_ShutdownContext; SECMOD_GetDefaultModDBFlag; SECMOD_GetSkipFirstFlag; ;+ local: diff --git a/security/nss/lib/nss/nss.h b/security/nss/lib/nss/nss.h index e19ff707a..f136d7b8b 100644 --- a/security/nss/lib/nss/nss.h +++ b/security/nss/lib/nss/nss.h @@ -77,6 +77,80 @@ #include "seccomon.h" +typedef struct NSSInitParametersStr NSSInitParameters; + +/* + * parameters used to initialize softoken. Mostly strings used to + * internationalize softoken. Memory for the strings are owned by the caller, + * who is free to free them once NSS_ContextInit returns. If the string + * parameter is NULL (as opposed to empty, zero length), then the softoken + * default is used. These are equivalent to the parameters for + * PK11_ConfigurePKCS11(). + * + * field names match their equivalent parameter names for softoken strings + * documented at https://developer.mozilla.org/en/PKCS11_Module_Specs. + * + * minPWLen + * Minimum password length in bytes. + * manufacturerID + * Override the default manufactureID value for the module returned in + * the CK_INFO, CK_SLOT_INFO, and CK_TOKEN_INFO structures with an + * internationalize string (UTF8). This value will be truncated at 32 + * bytes (not including the trailing NULL, partial UTF8 characters will be + * dropped). + * libraryDescription + * Override the default libraryDescription value for the module returned in + * the CK_INFO structure with an internationalize string (UTF8). This value + * will be truncated at 32 bytes(not including the trailing NULL, partial + * UTF8 characters will be dropped). + * cryptoTokenDescription + * Override the default label value for the internal crypto token returned + * in the CK_TOKEN_INFO structure with an internationalize string (UTF8). + * This value will be truncated at 32 bytes (not including the trailing + * NULL, partial UTF8 characters will be dropped). + * dbTokenDescription + * Override the default label value for the internal DB token returned in + * the CK_TOKEN_INFO structure with an internationalize string (UTF8). This + * value will be truncated at 32 bytes (not including the trailing NULL, + * partial UTF8 characters will be dropped). + * FIPSTokenDescription + * Override the default label value for the internal FIPS token returned in + * the CK_TOKEN_INFO structure with an internationalize string (UTF8). This + * value will be truncated at 32 bytes (not including the trailing NULL, + * partial UTF8 characters will be dropped). + * cryptoSlotDescription + * Override the default slotDescription value for the internal crypto token + * returned in the CK_SLOT_INFO structure with an internationalize string + * (UTF8). This value will be truncated at 64 bytes (not including the + * trailing NULL, partial UTF8 characters will be dropped). + * dbSlotDescription + * Override the default slotDescription value for the internal DB token + * returned in the CK_SLOT_INFO structure with an internationalize string + * (UTF8). This value will be truncated at 64 bytes (not including the + * trailing NULL, partial UTF8 characters will be dropped). + * FIPSSlotDescription + * Override the default slotDecription value for the internal FIPS token + * returned in the CK_SLOT_INFO structure with an internationalize string + * (UTF8). This value will be truncated at 64 bytes (not including the + * trailing NULL, partial UTF8 characters will be dropped). + * + */ +struct NSSInitParametersStr { + unsigned int length; /* allow this structure to grow in the future, + * must be set */ + PRBool passwordRequired; + int minPWLen; + char * manufactureID; /* variable names for strings match the */ + char * libraryDescription; /* parameter name in softoken */ + char * cryptoTokenDescription; + char * dbTokenDescription; + char * FIPSTokenDescription; + char * cryptoSlotDescription; + char * dbSlotDescription; + char * FIPSSlotDescription; +}; + + SEC_BEGIN_PROTOS /* @@ -195,10 +269,19 @@ extern SECStatus NSS_InitReadWrite(const char *configdir); #define SECMOD_DB "secmod.db" #endif +typedef struct NSSInitContextStr NSSInitContext; + + extern SECStatus NSS_Initialize(const char *configdir, const char *certPrefix, const char *keyPrefix, const char *secmodName, PRUint32 flags); +extern NSSInitContext *NSS_InitContext(const char *configdir, + const char *certPrefix, const char *keyPrefix, + const char *secmodName, NSSInitParameters *initParams, PRUint32 flags); + +extern SECStatus NSS_ShutdownContext(NSSInitContext *); + /* * same as NSS_Init, but checks to see if we need to merge an * old database in. @@ -252,9 +335,9 @@ extern SECStatus NSS_Shutdown(void); /* * set the PKCS #11 strings for the internal token. */ -void PK11_ConfigurePKCS11(const char *man, const char *libdes, - const char *tokdes, const char *ptokdes, const char *slotdes, - const char *pslotdes, const char *fslotdes, const char *fpslotdes, +void PK11_ConfigurePKCS11(const char *man, const char *libdesc, + const char *tokdesc, const char *ptokdesc, const char *slotdesc, + const char *pslotdesc, const char *fslotdesc, const char *fpslotdesc, int minPwd, int pwRequired); /* diff --git a/security/nss/lib/nss/nssinit.c b/security/nss/lib/nss/nssinit.c index c2c1c562d..fad2fe83d 100644 --- a/security/nss/lib/nss/nssinit.c +++ b/security/nss/lib/nss/nssinit.c @@ -128,98 +128,121 @@ nss_makeFlags(PRBool readOnly, PRBool noCertDB, return flags; } -/* - * statics to remember the PK11_ConfigurePKCS11() - * info. - */ -static char * pk11_config_strings = NULL; -static char * pk11_config_name = NULL; -static PRBool pk11_password_required = PR_FALSE; /* - * this is a legacy configuration function which used to be part of - * the PKCS #11 internal token. + * build config string from individual internationalized strings */ -void -PK11_ConfigurePKCS11(const char *man, const char *libdes, const char *tokdes, - const char *ptokdes, const char *slotdes, const char *pslotdes, - const char *fslotdes, const char *fpslotdes, int minPwd, int pwRequired) +char * +nss_MkConfigString(const char *man, const char *libdesc, const char *tokdesc, + const char *ptokdesc, const char *slotdesc, const char *pslotdesc, + const char *fslotdesc, const char *fpslotdesc, int minPwd) { - char *strings = NULL; - char *newStrings; + char *strings = NULL; + char *newStrings; - /* make sure the internationalization was done correctly... */ - strings = PR_smprintf(""); - if (strings == NULL) return; + /* make sure the internationalization was done correctly... */ + strings = PR_smprintf(""); + if (strings == NULL) return NULL; if (man) { newStrings = PR_smprintf("%s manufacturerID='%s'",strings,man); PR_smprintf_free(strings); strings = newStrings; } - if (strings == NULL) return; + if (strings == NULL) return NULL; - if (libdes) { - newStrings = PR_smprintf("%s libraryDescription='%s'",strings,libdes); + if (libdesc) { + newStrings = PR_smprintf("%s libraryDescription='%s'",strings,libdesc); PR_smprintf_free(strings); strings = newStrings; - if (pk11_config_name != NULL) { - PORT_Free(pk11_config_name); - } - pk11_config_name = PORT_Strdup(libdes); } - if (strings == NULL) return; + if (strings == NULL) return NULL; - if (tokdes) { + if (tokdesc) { newStrings = PR_smprintf("%s cryptoTokenDescription='%s'",strings, - tokdes); + tokdesc); PR_smprintf_free(strings); strings = newStrings; } - if (strings == NULL) return; + if (strings == NULL) return NULL; - if (ptokdes) { - newStrings = PR_smprintf("%s dbTokenDescription='%s'",strings,ptokdes); + if (ptokdesc) { + newStrings = PR_smprintf("%s dbTokenDescription='%s'",strings,ptokdesc); PR_smprintf_free(strings); strings = newStrings; } - if (strings == NULL) return; + if (strings == NULL) return NULL; - if (slotdes) { + if (slotdesc) { newStrings = PR_smprintf("%s cryptoSlotDescription='%s'",strings, - slotdes); + slotdesc); PR_smprintf_free(strings); strings = newStrings; } - if (strings == NULL) return; + if (strings == NULL) return NULL; - if (pslotdes) { - newStrings = PR_smprintf("%s dbSlotDescription='%s'",strings,pslotdes); + if (pslotdesc) { + newStrings = PR_smprintf("%s dbSlotDescription='%s'",strings,pslotdesc); PR_smprintf_free(strings); strings = newStrings; } - if (strings == NULL) return; + if (strings == NULL) return NULL; - if (fslotdes) { + if (fslotdesc) { newStrings = PR_smprintf("%s FIPSSlotDescription='%s'", - strings,fslotdes); + strings,fslotdesc); PR_smprintf_free(strings); strings = newStrings; } - if (strings == NULL) return; + if (strings == NULL) return NULL; - if (fpslotdes) { + if (fpslotdesc) { newStrings = PR_smprintf("%s FIPSTokenDescription='%s'", - strings,fpslotdes); + strings,fpslotdesc); PR_smprintf_free(strings); strings = newStrings; } - if (strings == NULL) return; + if (strings == NULL) return NULL; newStrings = PR_smprintf("%s minPS=%d", strings, minPwd); PR_smprintf_free(strings); strings = newStrings; - if (strings == NULL) return; + + return(strings); +} + +/* + * statics to remember the PK11_ConfigurePKCS11() + * info. + */ +static char * pk11_config_strings = NULL; +static char * pk11_config_name = NULL; +static PRBool pk11_password_required = PR_FALSE; + +/* + * this is a legacy configuration function which used to be part of + * the PKCS #11 internal token. + */ +void +PK11_ConfigurePKCS11(const char *man, const char *libdesc, const char *tokdesc, + const char *ptokdesc, const char *slotdesc, const char *pslotdesc, + const char *fslotdesc, const char *fpslotdesc, int minPwd, + int pwRequired) +{ + char * strings; + + strings = nss_MkConfigString(man,libdesc,tokdesc,ptokdesc,slotdesc, + pslotdesc,fslotdesc,fpslotdesc,minPwd); + if (strings == NULL) { + return; + } + + if (libdesc) { + if (pk11_config_name != NULL) { + PORT_Free(pk11_config_name); + } + pk11_config_name = PORT_Strdup(libdesc); + } if (pk11_config_strings != NULL) { PR_smprintf_free(pk11_config_strings); @@ -242,56 +265,6 @@ void PK11_UnconfigurePKCS11(void) } } -static char * -nss_addEscape(const char *string, char quote) -{ - char *newString = 0; - int escapes = 0, size = 0; - const char *src; - char *dest; - - for (src=string; *src ; src++) { - if ((*src == quote) || (*src == '\\')) escapes++; - size++; - } - - newString = PORT_ZAlloc(escapes+size+1); - if (newString == NULL) { - return NULL; - } - - for (src=string, dest=newString; *src; src++,dest++) { - if ((*src == '\\') || (*src == quote)) { - *dest++ = '\\'; - } - *dest = *src; - } - - return newString; -} - -static char * -nss_doubleEscape(const char *string) -{ - char *round1 = NULL; - char *retValue = NULL; - if (string == NULL) { - goto done; - } - round1 = nss_addEscape(string,'\''); - if (round1) { - retValue = nss_addEscape(round1,'"'); - PORT_Free(round1); - } - -done: - if (retValue == NULL) { - retValue = PORT_Strdup(""); - } - return retValue; -} - - /* * The following code is an attempt to automagically find the external root * module. @@ -389,46 +362,25 @@ nss_FindExternalRoot(const char *dbpath, const char* secmodprefix) } /* - * OK there are now lots of options here, lets go through them all: + * see nss_Init for definitions of the various options. * - * configdir - base directory where all the cert, key, and module datbases live. - * certPrefix - prefix added to the beginning of the cert database example: " - * "https-server1-" - * keyPrefix - prefix added to the beginning of the key database example: " - * "https-server1-" - * secmodName - name of the security module database (usually "secmod.db"). - * readOnly - Boolean: true if the databases are to be opened read only. - * nocertdb - Don't open the cert DB and key DB's, just initialize the - * Volatile certdb. - * nomoddb - Don't open the security module DB, just initialize the - * PKCS #11 module. - * forceOpen - Continue to force initializations even if the databases cannot - * be opened. + * this function builds a moduleSpec string from the options and previously + * set statics (from PKCS11_Configure, for instance), and uses it to kick off + * the loading of the various PKCS #11 modules. */ - -static PRBool nss_IsInitted = PR_FALSE; -static void* plContext = NULL; - -static SECStatus nss_InitShutdownList(void); - -#ifdef DEBUG -static CERTCertificate dummyCert; -#endif - static SECStatus -nss_Init(const char *configdir, const char *certPrefix, const char *keyPrefix, - const char *secmodName, const char *updateDir, - const char *updCertPrefix, const char *updKeyPrefix, - const char *updateID, const char *updateName, - PRBool readOnly, PRBool noCertDB, - PRBool noModDB, PRBool forceOpen, PRBool noRootInit, - PRBool optimizeSpace, PRBool noSingleThreadedModules, - PRBool allowAlreadyInitializedModules, - PRBool dontFinalizeModules) +nss_InitModules(const char *configdir, const char *certPrefix, + const char *keyPrefix, const char *secmodName, + const char *updateDir, const char *updCertPrefix, + const char *updKeyPrefix, const char *updateID, + const char *updateName, char *configName, char *configStrings, + PRBool pwRequired, PRBool readOnly, PRBool noCertDB, + PRBool noModDB, PRBool forceOpen, PRBool optimizeSpace, + PRBool isContextInit) { + SECStatus rv = SECFailure; char *moduleSpec = NULL; char *flags = NULL; - SECStatus rv = SECFailure; char *lconfigdir = NULL; char *lcertPrefix = NULL; char *lkeyPrefix = NULL; @@ -438,88 +390,62 @@ nss_Init(const char *configdir, const char *certPrefix, const char *keyPrefix, char *lupdKeyPrefix = NULL; char *lupdateID = NULL; char *lupdateName = NULL; - PKIX_UInt32 actualMinorVersion = 0; - PKIX_Error *pkixError = NULL;; - - if (nss_IsInitted) { - return SECSuccess; - } - - /* New option bits must not change the size of CERTCertificate. */ - PORT_Assert(sizeof(dummyCert.options) == sizeof(void *)); - - if (SECSuccess != cert_InitLocks()) { - return SECFailure; - } - - if (SECSuccess != InitCRLCache()) { - return SECFailure; - } - - if (SECSuccess != OCSP_InitGlobal()) { - return SECFailure; - } flags = nss_makeFlags(readOnly,noCertDB,noModDB,forceOpen, - pk11_password_required, optimizeSpace); + pwRequired, optimizeSpace); if (flags == NULL) return rv; /* * configdir is double nested, and Windows uses the same character * for file seps as we use for escapes! (sigh). */ - lconfigdir = nss_doubleEscape(configdir); + lconfigdir = secmod_DoubleEscape(configdir, '\'', '\"'); if (lconfigdir == NULL) { goto loser; } - lcertPrefix = nss_doubleEscape(certPrefix); + lcertPrefix = secmod_DoubleEscape(certPrefix, '\'', '\"'); if (lcertPrefix == NULL) { goto loser; } - lkeyPrefix = nss_doubleEscape(keyPrefix); + lkeyPrefix = secmod_DoubleEscape(keyPrefix, '\'', '\"'); if (lkeyPrefix == NULL) { goto loser; } - lsecmodName = nss_doubleEscape(secmodName); + lsecmodName = secmod_DoubleEscape(secmodName, '\'', '\"'); if (lsecmodName == NULL) { goto loser; } - lupdateDir = nss_doubleEscape(updateDir); + lupdateDir = secmod_DoubleEscape(updateDir, '\'', '\"'); if (lupdateDir == NULL) { goto loser; } - lupdCertPrefix = nss_doubleEscape(updCertPrefix); + lupdCertPrefix = secmod_DoubleEscape(updCertPrefix, '\'', '\"'); if (lupdCertPrefix == NULL) { goto loser; } - lupdKeyPrefix = nss_doubleEscape(updKeyPrefix); + lupdKeyPrefix = secmod_DoubleEscape(updKeyPrefix, '\'', '\"'); if (lupdKeyPrefix == NULL) { goto loser; } - lupdateID = nss_doubleEscape(updateID); + lupdateID = secmod_DoubleEscape(updateID, '\'', '\"'); if (lupdateID == NULL) { goto loser; } - lupdateName = nss_doubleEscape(updateName); + lupdateName = secmod_DoubleEscape(updateName, '\'', '\"'); if (lupdateName == NULL) { goto loser; } - if (noSingleThreadedModules || allowAlreadyInitializedModules || - dontFinalizeModules) { - pk11_setGlobalOptions(noSingleThreadedModules, - allowAlreadyInitializedModules, - dontFinalizeModules); - } moduleSpec = PR_smprintf( "name=\"%s\" parameters=\"configdir='%s' certPrefix='%s' keyPrefix='%s' " "secmod='%s' flags=%s updatedir='%s' updateCertPrefix='%s' " "updateKeyPrefix='%s' updateid='%s' updateTokenDescription='%s' %s\" " - "NSS=\"flags=internal,moduleDB,moduleDBOnly,critical\"", - pk11_config_name ? pk11_config_name : NSS_DEFAULT_MOD_NAME, + "NSS=\"flags=internal,moduleDB,moduleDBOnly,critical%s%s\"", + configName ? configName : NSS_DEFAULT_MOD_NAME, lconfigdir,lcertPrefix,lkeyPrefix,lsecmodName,flags, lupdateDir, lupdCertPrefix, lupdKeyPrefix, lupdateID, - lupdateName, pk11_config_strings ? pk11_config_strings : ""); + lupdateName, configStrings ? configStrings : "", + isContextInit ? "" : ",defaultModDB,internalKeySlot"); loser: PORT_Free(flags); @@ -541,65 +467,262 @@ loser: SECMOD_DestroyModule(module); } } + return rv; +} - if (rv == SECSuccess) { - if (SECOID_Init() != SECSuccess) { +/* + * OK there are now lots of options here, lets go through them all: + * + * configdir - base directory where all the cert, key, and module datbases live. + * certPrefix - prefix added to the beginning of the cert database example: " + * "https-server1-" + * keyPrefix - prefix added to the beginning of the key database example: " + * "https-server1-" + * secmodName - name of the security module database (usually "secmod.db"). + * updateDir - used in initMerge, old directory to update from. + * updateID - used in initMerge, unique ID to represent the updated directory. + * updateName - used in initMerge, token name when updating. + * initContextPtr - used in initContext, pointer to return a unique context + * value. + * readOnly - Boolean: true if the databases are to be opened read only. + * nocertdb - Don't open the cert DB and key DB's, just initialize the + * Volatile certdb. + * nomoddb - Don't open the security module DB, just initialize the + * PKCS #11 module. + * forceOpen - Continue to force initializations even if the databases cannot + * be opened. + * noRootInit - don't try to automatically load the root cert store if one is + * not found. + * optimizeSpace - tell NSS to use fewer hash table buckets. + * + * The next three options are used in an attempt to share PKCS #11 modules + * with other loaded, running libraries. PKCS #11 was not designed with this + * sort of sharing in mind, so use of these options may lead to questionable + * results. These options are may be incompatible with NSS_LoadContext() calls. + * + * noSingleThreadedModules - don't load modules that are not thread safe (many + * smart card tokens will not work). + * allowAlreadyInitializedModules - if a module has already been loaded and + * initialize try to use it. + * don'tFinalizeModules - dont shutdown modules we may have loaded. + */ + +static PRBool nssIsInitted = PR_FALSE; +static NSSInitContext *nssInitContextList = NULL; +static void* plContext = NULL; + +struct NSSInitContextStr { + NSSInitContext *next; + PRUint32 magic; +}; + +#define NSS_INIT_MAGIC 0x1413A91C +static SECStatus nss_InitShutdownList(void); + +#ifdef DEBUG +static CERTCertificate dummyCert; +#endif + +static SECStatus +nss_Init(const char *configdir, const char *certPrefix, const char *keyPrefix, + const char *secmodName, const char *updateDir, + const char *updCertPrefix, const char *updKeyPrefix, + const char *updateID, const char *updateName, + NSSInitContext ** initContextPtr, + NSSInitParameters *initParams, + PRBool readOnly, PRBool noCertDB, + PRBool noModDB, PRBool forceOpen, PRBool noRootInit, + PRBool optimizeSpace, PRBool noSingleThreadedModules, + PRBool allowAlreadyInitializedModules, + PRBool dontFinalizeModules) +{ + SECStatus rv = SECFailure; + PKIX_UInt32 actualMinorVersion = 0; + PKIX_Error *pkixError = NULL;; + PRBool isReallyInitted; + char *configStrings = NULL; + char *configName = NULL; + PRBool passwordRequired = PR_FALSE; + + /* if we are trying to init with a traditional NSS_Init call, maintain + * the traditional idempotent behavior. */ + if (!initContextPtr && nssIsInitted) { + return SECSuccess; + } + + /* this tells us whether or not some library has already initialized us. + * if so, we don't want to double call some of the basic initialization + * functions */ + isReallyInitted = NSS_IsInitialized(); + + if (!isReallyInitted) { + /* New option bits must not change the size of CERTCertificate. */ + PORT_Assert(sizeof(dummyCert.options) == sizeof(void *)); + + if (SECSuccess != cert_InitLocks()) { + return SECFailure; + } + + if (SECSuccess != InitCRLCache()) { + return SECFailure; + } + + if (SECSuccess != OCSP_InitGlobal()) { + return SECFailure; + } + } + + if (noSingleThreadedModules || allowAlreadyInitializedModules || + dontFinalizeModules) { + pk11_setGlobalOptions(noSingleThreadedModules, + allowAlreadyInitializedModules, + dontFinalizeModules); + } + + if (initContextPtr) { + *initContextPtr = PORT_ZNew(NSSInitContext); + if (*initContextPtr == NULL) { return SECFailure; } + /* + * For traditional NSS_Init, we used the PK11_Configure() call to set + * globals. with InitContext, we pass those strings in as parameters. + * + * This allows old NSS_Init calls to work as before, while at the same + * time new calls and old calls will not interfere with each other. + */ + if (initParams) { + if (initParams->length < sizeof(NSSInitParameters)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + configStrings = nss_MkConfigString(initParams->manufactureID, + initParams->libraryDescription, + initParams->cryptoTokenDescription, + initParams->dbTokenDescription, + initParams->cryptoSlotDescription, + initParams->dbSlotDescription, + initParams->FIPSSlotDescription, + initParams->FIPSTokenDescription, + initParams->minPWLen); + if (configStrings == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + configName = initParams->libraryDescription; + passwordRequired = initParams->passwordRequired; + } + } else { + configStrings = pk11_config_strings; + configName = pk11_config_name; + passwordRequired = pk11_password_required; + } + + /* we always try to initialize the modules */ + rv = nss_InitModules(configdir, certPrefix, keyPrefix, secmodName, + updateDir, updCertPrefix, updKeyPrefix, updateID, + updateName, configName, configStrings, passwordRequired, + readOnly, noCertDB, noModDB, forceOpen, optimizeSpace, + (initContextPtr != NULL)); + + if (rv != SECSuccess) { + goto loser; + } + + + /* finish up initialization */ + if (!isReallyInitted) { + if (SECOID_Init() != SECSuccess) { + goto loser; + } if (STAN_LoadDefaultNSS3TrustDomain() != PR_SUCCESS) { - return SECFailure; + goto loser; } if (nss_InitShutdownList() != SECSuccess) { - return SECFailure; + goto loser; } CERT_SetDefaultCertDB((CERTCertDBHandle *) STAN_GetDefaultTrustDomain()); if ((!noModDB) && (!noCertDB) && (!noRootInit)) { if (!SECMOD_HasRootCerts()) { const char *dbpath = configdir; + /* handle supported database modifiers */ if (strncmp(dbpath, "sql:", 4) == 0) { dbpath += 4; + } else if(strncmp(dbpath, "dbm:", 4) == 0) { + dbpath += 4; + } else if(strncmp(dbpath, "extern:", 7) == 0) { + dbpath += 7; + } else if(strncmp(dbpath, "rdb:", 4) == 0) { + /* if rdb: is specified, the configdir isn't really a + * path. Skip it */ + dbpath = NULL; + } + if (dbpath) { + nss_FindExternalRoot(dbpath, secmodName); } - nss_FindExternalRoot(dbpath, secmodName); } } + pk11sdr_Init(); cert_CreateSubjectKeyIDHashTable(); - nss_IsInitted = PR_TRUE; - } - if (SECSuccess == rv) { pkixError = PKIX_Initialize (PKIX_FALSE, PKIX_MAJOR_VERSION, PKIX_MINOR_VERSION, PKIX_MINOR_VERSION, &actualMinorVersion, &plContext); if (pkixError != NULL) { - rv = SECFailure; + goto loser; } else { char *ev = getenv("NSS_ENABLE_PKIX_VERIFY"); if (ev && ev[0]) { CERT_SetUsePKIXForValidation(PR_TRUE); } } + + } - return rv; + /* + * Now mark the appropriate init state. If initContextPtr was passed + * in, then return the new context pointer and add it to the + * nssInitContextList. Otherwise set the global nss_isInitted flag + */ + if (!initContextPtr) { + nssIsInitted = PR_TRUE; + } else { + (*initContextPtr)->magic = NSS_INIT_MAGIC; + (*initContextPtr)->next = nssInitContextList; + nssInitContextList = (*initContextPtr); + } + + return SECSuccess; + +loser: + if (initContextPtr && *initContextPtr) { + PORT_Free(*initContextPtr); + *initContextPtr = NULL; + if (configStrings) { + PR_smprintf_free(configStrings); + } + } + return SECFailure; } SECStatus NSS_Init(const char *configdir) { - return nss_Init(configdir, "", "", SECMOD_DB, "", "", "", "", "", - PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE, + return nss_Init(configdir, "", "", SECMOD_DB, "", "", "", "", "", NULL, + NULL, PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE, PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE); } SECStatus NSS_InitReadWrite(const char *configdir) { - return nss_Init(configdir, "", "", SECMOD_DB, "", "", "", "", "", - PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE, + return nss_Init(configdir, "", "", SECMOD_DB, "", "", "", "", "", NULL, + NULL, PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE, PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE); } @@ -656,7 +779,7 @@ NSS_Initialize(const char *configdir, const char *certPrefix, const char *keyPrefix, const char *secmodName, PRUint32 flags) { return nss_Init(configdir, certPrefix, keyPrefix, secmodName, - "", "", "", "", "", + "", "", "", "", "", NULL, NULL, ((flags & NSS_INIT_READONLY) == NSS_INIT_READONLY), ((flags & NSS_INIT_NOCERTDB) == NSS_INIT_NOCERTDB), ((flags & NSS_INIT_NOMODDB) == NSS_INIT_NOMODDB), @@ -668,6 +791,27 @@ NSS_Initialize(const char *configdir, const char *certPrefix, ((flags & NSS_INIT_NOPK11FINALIZE) == NSS_INIT_NOPK11FINALIZE)); } +NSSInitContext * +NSS_InitContext(const char *configdir, const char *certPrefix, + const char *keyPrefix, const char *secmodName, + NSSInitParameters *initParams, PRUint32 flags) +{ + SECStatus rv; + NSSInitContext *context; + + rv = nss_Init(configdir, certPrefix, keyPrefix, secmodName, + "", "", "", "", "", &context, initParams, + ((flags & NSS_INIT_READONLY) == NSS_INIT_READONLY), + ((flags & NSS_INIT_NOCERTDB) == NSS_INIT_NOCERTDB), + ((flags & NSS_INIT_NOMODDB) == NSS_INIT_NOMODDB), + ((flags & NSS_INIT_FORCEOPEN) == NSS_INIT_FORCEOPEN), PR_TRUE, + ((flags & NSS_INIT_OPTIMIZESPACE) == NSS_INIT_OPTIMIZESPACE), + ((flags & NSS_INIT_PK11THREADSAFE) == NSS_INIT_PK11THREADSAFE), + ((flags & NSS_INIT_PK11RELOAD) == NSS_INIT_PK11RELOAD), + ((flags & NSS_INIT_NOPK11FINALIZE) == NSS_INIT_NOPK11FINALIZE)); + return (rv == SECSuccess) ? context : NULL; +} + SECStatus NSS_InitWithMerge(const char *configdir, const char *certPrefix, const char *keyPrefix, const char *secmodName, @@ -676,7 +820,8 @@ NSS_InitWithMerge(const char *configdir, const char *certPrefix, const char *updateName, PRUint32 flags) { return nss_Init(configdir, certPrefix, keyPrefix, secmodName, - updateDir, updCertPrefix, updKeyPrefix, updateID, updateName, + updateDir, updCertPrefix, updKeyPrefix, updateID, updateName, + NULL, NULL, ((flags & NSS_INIT_READONLY) == NSS_INIT_READONLY), ((flags & NSS_INIT_NOCERTDB) == NSS_INIT_NOCERTDB), ((flags & NSS_INIT_NOMODDB) == NSS_INIT_NOMODDB), @@ -694,7 +839,7 @@ NSS_InitWithMerge(const char *configdir, const char *certPrefix, SECStatus NSS_NoDB_Init(const char * configdir) { - return nss_Init("","","","", "", "", "", "", "", + return nss_Init("","","","", "", "", "", "", "", NULL, NULL, PR_TRUE,PR_TRUE,PR_TRUE,PR_TRUE,PR_TRUE,PR_TRUE, PR_FALSE,PR_FALSE,PR_FALSE); } @@ -722,7 +867,7 @@ nss_GetShutdownEntry(NSS_ShutdownFunc sFunc, void *appData) { int count, i; count = nssShutdownList.peakFuncs; - /* expect the list to be short, just do a linear search */ + for (i=0; i < count; i++) { if ((nssShutdownList.funcs[i].func == sFunc) && (nssShutdownList.funcs[i].appData == appData)){ @@ -740,7 +885,7 @@ NSS_RegisterShutdown(NSS_ShutdownFunc sFunc, void *appData) { int i; - if (!nss_IsInitted) { + if (!nssIsInitted) { PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; } @@ -794,7 +939,7 @@ SECStatus NSS_UnregisterShutdown(NSS_ShutdownFunc sFunc, void *appData) { int i; - if (!nss_IsInitted) { + if (!nssIsInitted) { PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; } @@ -821,6 +966,9 @@ NSS_UnregisterShutdown(NSS_ShutdownFunc sFunc, void *appData) static SECStatus nss_InitShutdownList(void) { + if (nssShutdownList.lock != NULL) { + return SECSuccess; + } nssShutdownList.lock = PZ_NewLock(nssILockOther); if (nssShutdownList.lock == NULL) { return SECFailure; @@ -869,16 +1017,12 @@ nss_ShutdownShutdownList(void) extern const NSSError NSS_ERROR_BUSY; SECStatus -NSS_Shutdown(void) +nss_Shutdown(void) { SECStatus shutdownRV = SECSuccess; SECStatus rv; PRStatus status; - - if (!nss_IsInitted) { - PORT_SetError(SEC_ERROR_NOT_INITIALIZED); - return SECFailure; - } + NSSInitContext *temp; rv = nss_ShutdownShutdownList(); if (rv != SECSuccess) { @@ -891,6 +1035,7 @@ NSS_Shutdown(void) SECOID_Shutdown(); status = STAN_Shutdown(); cert_DestroySubjectKeyIDHashTable(); + pk11_SetInternalKeySlot(NULL); rv = SECMOD_Shutdown(); if (rv != SECSuccess) { shutdownRV = SECFailure; @@ -911,14 +1056,87 @@ NSS_Shutdown(void) } shutdownRV = SECFailure; } - nss_IsInitted = PR_FALSE; + nssIsInitted = PR_FALSE; + temp = nssInitContextList; + nssInitContextList = NULL; + /* free the old list. This is necessary when we are called from + * NSS_Shutdown(). */ + while (temp) { + NSSInitContext *next = temp->next; + temp->magic = 0; + PORT_Free(temp); + temp = next; + } return shutdownRV; } +SECStatus +NSS_Shutdown(void) +{ + if (!nssIsInitted) { + PORT_SetError(SEC_ERROR_NOT_INITIALIZED); + return SECFailure; + } + + return nss_Shutdown(); +} + +/* + * remove the context from a list. return true if found, false if not + */ +PRBool +nss_RemoveList(NSSInitContext *context) { + NSSInitContext *this = nssInitContextList; + NSSInitContext **last = &nssInitContextList; + + while (this) { + if (this == context) { + *last = this->next; + this->magic = 0; + PORT_Free(this); + return PR_TRUE; + } + last = &this->next; + this=this->next; + } + return PR_FALSE; +} + +/* + * This form of shutdown is safe in the case where we may have multiple + * entities using NSS in a single process. Each entity calls shutdown with + * it's own context. The application (which doesn't get a context), calls + * shutdown with NULL. Once all users have 'checked in' NSS will shutdown. + * This is different than NSS_Shutdown, where calling it will shutdown NSS + * irreguardless of who else may have NSS open. + */ +SECStatus +NSS_ShutdownContext(NSSInitContext *context) +{ + if (!context) { + if (!nssIsInitted) { + PORT_SetError(SEC_ERROR_NOT_INITIALIZED); + return SECFailure; + } + nssIsInitted = 0; + } else if (! nss_RemoveList(context)) { + /* context was already freed or wasn't valid */ + PORT_SetError(SEC_ERROR_NOT_INITIALIZED); + return SECFailure; + } + if ((nssIsInitted == 0) && (nssInitContextList == NULL)) { + return nss_Shutdown(); + } + return SECSuccess; +} + + + + PRBool NSS_IsInitialized(void) { - return nss_IsInitted; + return (nssIsInitted) || (nssInitContextList != NULL); } diff --git a/security/nss/lib/pk11wrap/pk11load.c b/security/nss/lib/pk11wrap/pk11load.c index be15398ed..900be9053 100644 --- a/security/nss/lib/pk11wrap/pk11load.c +++ b/security/nss/lib/pk11wrap/pk11load.c @@ -132,19 +132,44 @@ PRBool pk11_getFinalizeModulesOption(void) * initialize. */ static SECStatus -secmod_handleReload(SECMODModule *oldModule, char *modulespec) +secmod_handleReload(SECMODModule *oldModule, SECMODModule *newModule) { PK11SlotInfo *slot; + char *modulespec; char *newModuleSpec; char **children; CK_SLOT_ID *ids; SECStatus rv; + SECMODConfigList *conflist; + int count = 0; /* first look for tokens= key words from the module spec */ - newModuleSpec = secmod_ParseModuleSpecForTokens(modulespec,&children,&ids); + modulespec = newModule->libraryParams; + newModuleSpec = secmod_ParseModuleSpecForTokens(PR_TRUE, + newModule->isFIPS, modulespec, &children, &ids); if (!newModuleSpec) { return SECFailure; } + + /* + * We are now trying to open a new slot on an already loaded module. + * If that slot represents a cert/key database, we don't want to open + * multiple copies of that same database. Unfortunately we understand + * the softoken flags well enough to be able to do this, so we can only get + * the list of already loaded databases if we are trying to open another + * internal module. + */ + if (oldModule->internal) { + conflist = secmod_GetConfigList(oldModule->isFIPS, + oldModule->libraryParams, &count); + } + + + /* don't open multiple of the same db */ + if (conflist && secmod_MatchConfigList(newModuleSpec, conflist, count)) { + rv = SECSuccess; + goto loser; + } slot = SECMOD_OpenNewSlot(oldModule, newModuleSpec); if (slot) { int newID; @@ -152,10 +177,18 @@ secmod_handleReload(SECMODModule *oldModule, char *modulespec) CK_SLOT_ID *thisID; char *oldModuleSpec; + if (secmod_IsInternalKeySlot(newModule)) { + pk11_SetInternalKeySlot(slot); + } newID = slot->slotID; PK11_FreeSlot(slot); for (thisChild=children, thisID=ids; thisChild && *thisChild; thisChild++,thisID++) { + if (conflist && + secmod_MatchConfigList(*thisChild, conflist, count)) { + *thisID = (CK_SLOT_ID) -1; + continue; + } slot = SECMOD_OpenNewSlot(oldModule, *thisChild); if (slot) { *thisID = slot->slotID; @@ -177,8 +210,13 @@ secmod_handleReload(SECMODModule *oldModule, char *modulespec) rv = SECSuccess; } + +loser: secmod_FreeChildren(children, ids); PORT_Free(newModuleSpec); + if (conflist) { + secmod_FreeConfigList(conflist, count); + } return rv; } @@ -224,7 +262,7 @@ secmod_ModuleInit(SECMODModule *mod, SECMODModule **reload, * reloading */ if (oldModule) { SECStatus rv; - rv = secmod_handleReload(oldModule, mod->libraryParams); + rv = secmod_handleReload(oldModule, mod); if (rv == SECSuccess) { /* This module should go away soon, since we've * simply expanded the slots on the old module. diff --git a/security/nss/lib/pk11wrap/pk11pars.c b/security/nss/lib/pk11wrap/pk11pars.c index 1ddcc4d04..c36cd25d8 100644 --- a/security/nss/lib/pk11wrap/pk11pars.c +++ b/security/nss/lib/pk11wrap/pk11pars.c @@ -107,28 +107,28 @@ secmod_NewModule(void) } -/* private flags. */ +/* private flags for isModuleDB (field in SECMODModule). */ /* The meaing of these flags is as follows: * - * SECMOD_FLAG_IS_MODULE_DB - This is a module that accesses the database of - * other modules to load. Module DBs are loadable modules that tells - * NSS which PKCS #11 modules to load and when. These module DBs are + * SECMOD_FLAG_MODULE_DB_IS_MODULE_DB - This is a module that accesses the + * database of other modules to load. Module DBs are loadable modules that + * tells NSS which PKCS #11 modules to load and when. These module DBs are * chainable. That is, one module DB can load another one. NSS system init * design takes advantage of this feature. In system NSS, a fixed system * module DB loads the system defined libraries, then chains out to the * traditional module DBs to load any system or user configured modules * (like smart cards). This bit is the same as the already existing meaning - * of isModuleDB = PR_TRUE. None of the other flags should be set if this - * flag isn't on. + * of isModuleDB = PR_TRUE. None of the other module db flags should be set + * if this flag isn't on. * - * SECMOD_FLAG_SKIP_FIRST - This flag tells NSS to skip the first + * SECMOD_FLAG_MODULE_DB_SKIP_FIRST - This flag tells NSS to skip the first * PKCS #11 module presented by a module DB. This allows the OS to load a * softoken from the system module, then ask the existing module DB code to * load the other PKCS #11 modules in that module DB (skipping it's request * to load softoken). This gives the system init finer control over the * configuration of that softoken module. * - * SECMOD_FLAG_DEFAULT_MODDB - This flag allows system init to mark a + * SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB - This flag allows system init to mark a * different module DB as the 'default' module DB (the one in which * 'Add module' changes will go). Without this flag NSS takes the first * module as the default Module DB, but in system NSS, that first module @@ -137,10 +137,27 @@ secmod_NewModule(void) * preserving the user's ability to load new PKCS #11 modules (which only * affect him), from existing applications like Firefox. */ -#define SECMOD_FLAG_IS_MODULE_DB 0x01 /* must be set if any of the other flags - * are set */ -#define SECMOD_FLAG_SKIP_FIRST 0x02 -#define SECMOD_FLAG_DEFAULT_MODDB 0x04 +#define SECMOD_FLAG_MODULE_DB_IS_MODULE_DB 0x01 /* must be set if any of the + *other flags are set */ +#define SECMOD_FLAG_MODULE_DB_SKIP_FIRST 0x02 +#define SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB 0x04 + + +/* private flags for internal (field in SECMODModule). */ +/* The meaing of these flags is as follows: + * + * SECMOD_FLAG_INTERNAL_IS_INTERNAL - This is a marks the the module is + * the internal module (that is, softoken). This bit is the same as the + * already existing meaning of internal = PR_TRUE. None of the other + * internal flags should be set if this flag isn't on. + * + * SECMOD_FLAG_MODULE_INTERNAL_KEY_SLOT - This flag allows system init to mark + * a different slot returned byt PK11_GetInternalKeySlot(). The 'primary' + * slot defined by this module will be the new internal key slot. + */ +#define SECMOD_FLAG_INTERNAL_IS_INTERNAL 0x01 /* must be set if any of + *the other flags are set */ +#define SECMOD_FLAG_INTERNAL_KEY_SLOT 0x02 /* * for 3.4 we continue to use the old SECMODModule structure @@ -188,17 +205,26 @@ SECMOD_CreateModule(const char *library, const char *moduleName, * all platforms. These flags are only valid if moduleDB is set, so * code checking if (mod->isModuleDB) will continue to work correctly. */ if (mod->isModuleDB) { - char flags = SECMOD_FLAG_IS_MODULE_DB; + char flags = SECMOD_FLAG_MODULE_DB_IS_MODULE_DB; if (secmod_argHasFlag("flags","skipFirst",nssc)) { - flags |= SECMOD_FLAG_SKIP_FIRST; + flags |= SECMOD_FLAG_MODULE_DB_SKIP_FIRST; } if (secmod_argHasFlag("flags","defaultModDB",nssc)) { - flags |= SECMOD_FLAG_DEFAULT_MODDB; + flags |= SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB; } /* additional moduleDB flags could be added here in the future */ mod->isModuleDB = (PRBool) flags; } + if (mod->internal) { + char flags = SECMOD_FLAG_INTERNAL_IS_INTERNAL; + + if (secmod_argHasFlag("flags", "internalKeySlot", nssc)) { + flags |= SECMOD_FLAG_INTERNAL_KEY_SLOT; + } + mod->internal = (PRBool) flags; + } + ciphers = secmod_argGetParamValue("ciphers",nssc); secmod_argSetNewCipherFlags(&mod->ssl[0],ciphers); if (ciphers) PORT_Free(ciphers); @@ -213,7 +239,7 @@ SECMOD_GetSkipFirstFlag(SECMODModule *mod) { char flags = (char) mod->isModuleDB; - return (flags & SECMOD_FLAG_SKIP_FIRST) ? PR_TRUE : PR_FALSE; + return (flags & SECMOD_FLAG_MODULE_DB_SKIP_FIRST) ? PR_TRUE : PR_FALSE; } PRBool @@ -221,7 +247,59 @@ SECMOD_GetDefaultModDBFlag(SECMODModule *mod) { char flags = (char) mod->isModuleDB; - return (flags & SECMOD_FLAG_DEFAULT_MODDB) ? PR_TRUE : PR_FALSE; + return (flags & SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB) ? PR_TRUE : PR_FALSE; +} + +PRBool +secmod_IsInternalKeySlot(SECMODModule *mod) +{ + char flags = (char) mod->internal; + + return (flags & SECMOD_FLAG_INTERNAL_KEY_SLOT) ? PR_TRUE : PR_FALSE; +} + +/* forward declarations */ +static int secmod_escapeSize(const char *string, char quote); +static char *secmod_addEscape(const char *string, char quote); + +/* + * copy desc and value into target. Target is known to be big enough to + * hold desc +2 +value, which is good because the result of this will be + * *desc"*value". We may, however, have to add some escapes for special + * characters imbedded into value (rare). This string potentially comes from + * a user, so we don't want the user overflowing the target buffer by using + * excessive escapes. To prevent this we count the escapes we need to add and + * try to expand the buffer with Realloc. + */ +static char * +secmod_doDescCopy(char *target, int *targetLen, const char *desc, + int descLen, char *value) +{ + int diff, esc_len; + + esc_len = secmod_escapeSize(value, '\"') - 1; + diff = esc_len - strlen(value); + if (diff > 0) { + /* we need to escape... expand newSpecPtr as well to make sure + * we don't overflow it */ + char *newPtr = PORT_Realloc(target, *targetLen * diff); + if (!newPtr) { + return target; /* not enough space, just drop the whole copy */ + } + *targetLen += diff; + target = newPtr; + value = secmod_addEscape(value, '\"'); + if (value == NULL) { + return target; /* couldn't escape value, just drop the copy */ + } + } + PORT_Memcpy(target, desc, descLen); + target += descLen; + *target++='\"'; + PORT_Memcpy(target, value, esc_len); + target += esc_len; + *target++='\"'; + return target; } #define SECMOD_SPEC_COPY(new, start, end) \ @@ -230,6 +308,9 @@ SECMOD_GetDefaultModDBFlag(SECMODModule *mod) PORT_Memcpy(new, start, _cnt); \ new += _cnt; \ } +#define SECMOD_TOKEN_DESCRIPTION "tokenDescription=" +#define SECMOD_SLOT_DESCRIPTION "slotDescription=" + /* * Find any tokens= values in the module spec. @@ -241,13 +322,16 @@ SECMOD_GetDefaultModDBFlag(SECMODModule *mod) * spec. */ char * -secmod_ParseModuleSpecForTokens(char *moduleSpec, char ***children, +secmod_ParseModuleSpecForTokens(PRBool convert, PRBool isFIPS, + char *moduleSpec, char ***children, CK_SLOT_ID **ids) { - char *newSpec = PORT_Alloc(PORT_Strlen(moduleSpec)+2); + int newSpecLen = PORT_Strlen(moduleSpec)+2; + char *newSpec = PORT_Alloc(newSpecLen); char *newSpecPtr = newSpec; char *modulePrev = moduleSpec; char *target = NULL; + char *tmp = NULL; char **childArray = NULL; char *tokenIndex; CK_SLOT_ID *idArray = NULL; @@ -265,6 +349,28 @@ secmod_ParseModuleSpecForTokens(char *moduleSpec, char ***children, moduleSpec = secmod_argStrip(moduleSpec); SECMOD_SPEC_COPY(newSpecPtr, modulePrev, moduleSpec); + /* Notes on 'convert' and 'isFIPS' flags: The base parameters for opening + * a new softoken module takes the following parameters to name the + * various tokens: + * + * cryptoTokenDescription: name of the non-fips crypto token. + * cryptoSlotDescription: name of the non-fips crypto slot. + * dbTokenDescription: name of the non-fips db token. + * dbSlotDescription: name of the non-fips db slot. + * FIPSTokenDescription: name of the fips db/crypto token. + * FIPSSlotDescription: name of the fips db/crypto slot. + * + * if we are opening a new slot, we need to have the following + * parameters: + * tokenDescription: name of the token. + * slotDescription: name of the slot. + * + * + * The convert flag tells us to drop the unnecessary *TokenDescription + * and *SlotDescription arguments and convert the appropriate pair + * (either db or FIPS based on the isFIPS flag) to tokenDescription and + * slotDescription). + */ /* * walk down the list. if we find a tokens= argument, save it, * otherise copy the argument. @@ -274,9 +380,53 @@ secmod_ParseModuleSpecForTokens(char *moduleSpec, char ***children, modulePrev = moduleSpec; SECMOD_HANDLE_STRING_ARG(moduleSpec, target, "tokens=", modulePrev = moduleSpec; /* skip copying */ ) + SECMOD_HANDLE_STRING_ARG(moduleSpec, tmp, "cryptoTokenDescription=", + if (convert) { modulePrev = moduleSpec; } ); + SECMOD_HANDLE_STRING_ARG(moduleSpec, tmp, "cryptoSlotDescription=", + if (convert) { modulePrev = moduleSpec; } ); + SECMOD_HANDLE_STRING_ARG(moduleSpec, tmp, "dbTokenDescription=", + if (convert) { + modulePrev = moduleSpec; + if (!isFIPS) { + newSpecPtr = secmod_doDescCopy(newSpecPtr, + &newSpecLen, SECMOD_TOKEN_DESCRIPTION, + sizeof(SECMOD_TOKEN_DESCRIPTION)-1, tmp); + } + }); + SECMOD_HANDLE_STRING_ARG(moduleSpec, tmp, "dbSlotDescription=", + if (convert) { + modulePrev = moduleSpec; /* skip copying */ + if (!isFIPS) { + newSpecPtr = secmod_doDescCopy(newSpecPtr, + &newSpecLen, SECMOD_SLOT_DESCRIPTION, + sizeof(SECMOD_SLOT_DESCRIPTION)-1, tmp); + } + } ); + SECMOD_HANDLE_STRING_ARG(moduleSpec, tmp, "FIPSTokenDescription=", + if (convert) { + modulePrev = moduleSpec; /* skip copying */ + if (isFIPS) { + newSpecPtr = secmod_doDescCopy(newSpecPtr, + &newSpecLen, SECMOD_TOKEN_DESCRIPTION, + sizeof(SECMOD_TOKEN_DESCRIPTION)-1, tmp); + } + } ); + SECMOD_HANDLE_STRING_ARG(moduleSpec, tmp, "FIPSSlotDescription=", + if (convert) { + modulePrev = moduleSpec; /* skip copying */ + if (isFIPS) { + newSpecPtr = secmod_doDescCopy(newSpecPtr, + &newSpecLen, SECMOD_SLOT_DESCRIPTION, + sizeof(SECMOD_SLOT_DESCRIPTION)-1, tmp); + } + } ); SECMOD_HANDLE_FINAL_ARG(moduleSpec) SECMOD_SPEC_COPY(newSpecPtr, modulePrev, moduleSpec); } + if (tmp) { + PORT_Free(tmp); + tmp = NULL; + } *newSpecPtr = 0; /* no target found, return the newSpec */ @@ -341,6 +491,185 @@ secmod_ParseModuleSpecForTokens(char *moduleSpec, char ***children, return newSpec; } +/* get the database and flags from the spec */ +static char * +secmod_getConfigDir(char *spec, char **certPrefix, char **keyPrefix, + PRBool *readOnly) +{ + char * config = NULL; + + *certPrefix = NULL; + *keyPrefix = NULL; + *readOnly = secmod_argHasFlag("flags","readOnly",spec); + + spec = secmod_argStrip(spec); + while (*spec) { + int next; + SECMOD_HANDLE_STRING_ARG(spec, config, "configdir=", ;) + SECMOD_HANDLE_STRING_ARG(spec, *certPrefix, "certPrefix=", ;) + SECMOD_HANDLE_STRING_ARG(spec, *keyPrefix, "keyPrefix=", ;) + SECMOD_HANDLE_FINAL_ARG(spec) + } + return config; +} + +struct SECMODConfigListStr { + char *config; + char *certPrefix; + char *keyPrefix; + PRBool isReadOnly; +}; + +/* + * return an array of already openned databases from a spec list. + */ +SECMODConfigList * +secmod_GetConfigList(PRBool isFIPS, char *spec, int *count) +{ + char **children; + CK_SLOT_ID *ids; + char *strippedSpec; + int childCount; + SECMODConfigList *conflist = NULL; + int i; + + strippedSpec = secmod_ParseModuleSpecForTokens(PR_TRUE, isFIPS, + spec,&children,&ids); + if (strippedSpec == NULL) { + return NULL; + } + + for (childCount=0; children && children[childCount]; childCount++) ; + *count = childCount+1; /* include strippedSpec */ + conflist = PORT_NewArray(SECMODConfigList,*count); + if (conflist == NULL) { + *count = 0; + goto loser; + } + + conflist[0].config = secmod_getConfigDir(strippedSpec, + &conflist[0].certPrefix, + &conflist[0].keyPrefix, + &conflist[0].isReadOnly); + for (i=0; i < childCount; i++) { + conflist[i+1].config = secmod_getConfigDir(children[i], + &conflist[i+1].certPrefix, + &conflist[i+1].keyPrefix, + &conflist[i+1].isReadOnly); + } + +loser: + secmod_FreeChildren(children, ids); + PORT_Free(strippedSpec); + return conflist; +} + +/* + * determine if we are trying to open an old dbm database. For this test + * RDB databases should return PR_FALSE. + */ +static PRBool +secmod_configIsDBM(char *configDir) +{ + char *env; + + /* explicit dbm open */ + if (strncmp(configDir, "dbm:", 4) == 0) { + return PR_TRUE; + } + /* explicit open of a non-dbm database */ + if ((strncmp(configDir, "sql:",4) == 0) + || (strncmp(configDir, "rdb:", 4) == 0) + || (strncmp(configDir, "extern:", 7) == 0)) { + return PR_FALSE; + } + env = PR_GetEnv("NSS_DEFAULT_DB_TYPE"); + /* implicit dbm open */ + if ((env == NULL) || (strcmp(env,"dbm") == 0)) { + return PR_TRUE; + } + /* implicit non-dbm open */ + return PR_FALSE; +} + +/* + * match two prefixes. prefix may be NULL. NULL patches '\0' + */ +static PRBool +secmod_matchPrefix(char *prefix1, char *prefix2) +{ + if ((prefix1 == NULL) || (*prefix1 == 0)) { + if ((prefix2 == NULL) || (*prefix2 == 0)) { + return PR_TRUE; + } + return PR_FALSE; + } + if (strcmp(prefix1, prefix2) == 0) { + return PR_TRUE; + } + return PR_FALSE; +} + +/* + * return true if we are requesting a database that is already openned. + */ +PRBool +secmod_MatchConfigList(char *spec, SECMODConfigList *conflist, int count) +{ + char *config; + char *certPrefix; + char *keyPrefix; + PRBool isReadOnly; + PRBool ret=PR_FALSE; + int i; + + config = secmod_getConfigDir(spec, &certPrefix, &keyPrefix, &isReadOnly); + if (!config) { + ret=PR_TRUE; + goto done; + } + + /* NOTE: we dbm isn't multiple open safe. If we open the same database + * twice from two different locations, then we can corrupt our database + * (the cache will be inconsistent). Protect against this by claiming + * for comparison only that we are always openning dbm databases read only. + */ + if (secmod_configIsDBM(config)) { + isReadOnly = 1; + } + for (i=0; i < count; i++) { + if ((strcmp(config,conflist[i].config) == 0) && + secmod_matchPrefix(certPrefix, conflist[i].certPrefix) && + secmod_matchPrefix(keyPrefix, conflist[i].keyPrefix) && + /* this last test -- if we just need the DB open read only, + * than any open will suffice, but if we requested it read/write + * and it's only open read only, we need to open it again */ + (isReadOnly || !conflist[i].isReadOnly)) { + ret = PR_TRUE; + goto done; + } + } + + ret = PR_FALSE; +done: + PORT_Free(config); + PORT_Free(certPrefix); + PORT_Free(keyPrefix); + return ret; +} + +void +secmod_FreeConfigList(SECMODConfigList *conflist, int count) +{ + int i; + for (i=0; i < count; i++) { + PORT_Free(conflist[i].config); + PORT_Free(conflist[i].certPrefix); + PORT_Free(conflist[i].keyPrefix); + } + PORT_Free(conflist); +} + void secmod_FreeChildren(char **children, CK_SLOT_ID *ids) { @@ -515,7 +844,8 @@ secmod_MkAppendTokensList(PRArenaPool *arena, char *oldParam, char *newToken, SECStatus rv; /* first strip out and save the old tokenlist */ - rawParam = secmod_ParseModuleSpecForTokens(oldParam,&oldChildren,&oldIds); + rawParam = secmod_ParseModuleSpecForTokens(PR_FALSE,PR_FALSE, + oldParam,&oldChildren,&oldIds); if (!rawParam) { goto loser; } @@ -769,6 +1099,9 @@ SECMOD_LoadModule(char *modulespec,SECMODModule *parent, PRBool recurse) } if (parent) { module->parent = SECMOD_ReferenceModule(parent); + if (module->internal && secmod_IsInternalKeySlot(parent)) { + module->internal = parent->internal; + } } /* load it */ diff --git a/security/nss/lib/pk11wrap/pk11priv.h b/security/nss/lib/pk11wrap/pk11priv.h index 6b90c25d7..6f88e9330 100644 --- a/security/nss/lib/pk11wrap/pk11priv.h +++ b/security/nss/lib/pk11wrap/pk11priv.h @@ -114,6 +114,7 @@ SECStatus PK11_InitToken(PK11SlotInfo *slot, PRBool loadCerts); void PK11_InitSlot(SECMODModule *mod,CK_SLOT_ID slotID,PK11SlotInfo *slot); PRBool PK11_NeedPWInitForSlot(PK11SlotInfo *slot); SECStatus PK11_ReadSlotCerts(PK11SlotInfo *slot); +void pk11_SetInternalKeySlot(PK11SlotInfo *slot); /********************************************************************* * Mechanism Mapping functions diff --git a/security/nss/lib/pk11wrap/pk11slot.c b/security/nss/lib/pk11wrap/pk11slot.c index 9a9216a56..dc5483e67 100644 --- a/security/nss/lib/pk11wrap/pk11slot.c +++ b/security/nss/lib/pk11wrap/pk11slot.c @@ -1129,6 +1129,8 @@ PK11_InitToken(PK11SlotInfo *slot, PRBool loadCerts) PR_TRUE : PR_FALSE); slot->readOnly = ((tokenInfo.flags & CKF_WRITE_PROTECTED) ? PR_TRUE : PR_FALSE); + + slot->hasRandom = ((tokenInfo.flags & CKF_RNG) ? PR_TRUE : PR_FALSE); slot->protectedAuthPath = ((tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) @@ -1248,7 +1250,33 @@ PK11_InitToken(PK11SlotInfo *slot, PRBool loadCerts) PK11_FreeSlot(int_slot); } } - + /* work around a problem in softoken where it incorrectly + * reports databases opened read only as read/write. */ + if (slot->isInternal && !slot->readOnly) { + CK_SESSION_HANDLE session = CK_INVALID_SESSION; + + /* try to open a R/W session */ + crv =PK11_GETTAB(slot)->C_OpenSession(slot->slotID, + CKF_RW_SESSION|CKF_SERIAL_SESSION, slot, pk11_notify ,&session); + /* what a well behaved token should return if you open + * a RW session on a read only token */ + if (crv == CKR_TOKEN_WRITE_PROTECTED) { + slot->readOnly = PR_TRUE; + } else if (crv == CKR_OK) { + CK_SESSION_INFO sessionInfo; + + /* Because of a second bug in softoken, which silently returns + * a RO session, we need to check what type of session we got. */ + crv = PK11_GETTAB(slot)->C_GetSessionInfo(session, &sessionInfo); + if (crv == CKR_OK) { + if ((sessionInfo.flags & CKF_RW_SESSION) == 0) { + /* session was readonly, so this softoken slot must be * readonly */ + slot->readOnly = PR_TRUE; + } + } + PK11_GETTAB(slot)->C_CloseSession(session); + } + } return SECSuccess; } @@ -1697,12 +1725,29 @@ PK11_NeedUserInit(PK11SlotInfo *slot) return (PRBool)((slot->flags & CKF_USER_PIN_INITIALIZED) == 0); } +static PK11SlotInfo *pk11InternalKeySlot = NULL; +void +pk11_SetInternalKeySlot(PK11SlotInfo *slot) +{ + if (pk11InternalKeySlot) { + PK11_FreeSlot(pk11InternalKeySlot); + } + pk11InternalKeySlot = slot ? PK11_ReferenceSlot(slot) : NULL; +} + + /* get the internal key slot. FIPS has only one slot for both key slots and * default slots */ PK11SlotInfo * PK11_GetInternalKeySlot(void) { - SECMODModule *mod = SECMOD_GetInternalModule(); + SECMODModule *mod; + + if (pk11InternalKeySlot) { + return PK11_ReferenceSlot(pk11InternalKeySlot); + } + + mod = SECMOD_GetInternalModule(); PORT_Assert(mod != NULL); if (!mod) { PORT_SetError( SEC_ERROR_NO_MODULE ); @@ -1721,6 +1766,9 @@ PK11_GetInternalSlot(void) PORT_SetError( SEC_ERROR_NO_MODULE ); return NULL; } + if (mod->isFIPS) { + return PK11_GetInternalKeySlot(); + } return PK11_ReferenceSlot(mod->slots[0]); } diff --git a/security/nss/lib/pk11wrap/secmodi.h b/security/nss/lib/pk11wrap/secmodi.h index 879edce30..b67e6df99 100644 --- a/security/nss/lib/pk11wrap/secmodi.h +++ b/security/nss/lib/pk11wrap/secmodi.h @@ -89,12 +89,25 @@ extern unsigned long SECMOD_InternaltoPubCipherFlags(unsigned long internalFlags SECStatus secmod_LoadPKCS11Module(SECMODModule *, SECMODModule **oldModule); SECStatus SECMOD_UnloadModule(SECMODModule *); void SECMOD_SetInternalModule(SECMODModule *); +PRBool secmod_IsInternalKeySlot(SECMODModule *); + +/* tools for checking if we are loading the same database twice */ +typedef struct SECMODConfigListStr SECMODConfigList; +/* collect all the databases in a given spec */ +SECMODConfigList *secmod_GetConfigList(PRBool isFIPS, char *spec, int *count); +/* see is a spec matches a database on the list */ +PRBool secmod_MatchConfigList(char *spec, + SECMODConfigList *conflist, int count); +/* free our list of databases */ +void secmod_FreeConfigList(SECMODConfigList *conflist, int count); /* parsing parameters */ /* returned char * must be freed by caller with PORT_Free */ /* children and ids are null terminated arrays which must be freed with * secmod_FreeChildren */ -char *secmod_ParseModuleSpecForTokens(char *moduleSpec, +char *secmod_ParseModuleSpecForTokens(PRBool convert, + PRBool isFIPS, + char *moduleSpec, char ***children, CK_SLOT_ID **ids); void secmod_FreeChildren(char **children, CK_SLOT_ID *ids); diff --git a/security/nss/tests/all.sh b/security/nss/tests/all.sh index 4de519b40..6cf5cf4e4 100755 --- a/security/nss/tests/all.sh +++ b/security/nss/tests/all.sh @@ -188,7 +188,7 @@ run_cycle_pkix() export NSS_ENABLE_PKIX_VERIFY TESTS="${ALL_TESTS}" - TESTS_SKIP="cipher dbtests sdr crmf smime merge" + TESTS_SKIP="cipher dbtests sdr crmf smime merge multinit" echo "${NSS_SSL_TESTS}" | grep "_" > /dev/null RET=$? @@ -306,7 +306,7 @@ run_cycles() cycles="standard pkix upgradedb sharedb" CYCLES=${NSS_CYCLES:-$cycles} -tests="cipher libpkix cert dbtests tools fips sdr crmf smime ssl ocsp merge pkits chains" +tests="cipher libpkix cert dbtests tools fips sdr crmf smime ssl ocsp merge pkits chains multinit" TESTS=${NSS_TESTS:-$tests} ALL_TESTS=${TESTS} diff --git a/security/nss/tests/multinit/multinit.sh b/security/nss/tests/multinit/multinit.sh new file mode 100755 index 000000000..998e66f80 --- /dev/null +++ b/security/nss/tests/multinit/multinit.sh @@ -0,0 +1,191 @@ +#! /bin/sh +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1994-2000 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +######################################################################## +# +# mozilla/security/nss/tests/multinit/multinit.sh +# +# Script to test NSS multinit +# +# needs to work on all Unix and Windows platforms +# +# special strings +# --------------- +# FIXME ... known problems, search for this string +# NOTE .... unexpected behavior +# +######################################################################## + +############################## multinit_init ############################## +# local shell function to initialize this script +######################################################################## +multinit_init() +{ + SCRIPTNAME=multinit.sh # sourced - $0 would point to all.sh + + if [ -z "${CLEANUP}" ] ; then # if nobody else is responsible for + CLEANUP="${SCRIPTNAME}" # cleaning this script will do it + fi + + if [ -z "${INIT_SOURCED}" -o "${INIT_SOURCED}" != "TRUE" ]; then + cd ../common + . ./init.sh + fi + if [ ! -r $CERT_LOG_FILE ]; then # we need certificates here + cd ../cert + . ./cert.sh + fi + SCRIPTNAME=multinit.sh + + html_head "MULTI Tests" + + grep "SUCCESS: SMIME passed" $CERT_LOG_FILE >/dev/null || { + Exit 11 "Fatal - S/MIME of cert.sh needs to pass first" + } + + # set up our directories + MULTINITDIR=${HOSTDIR}/multinit + MULTINITDIR_1=${MULTINITDIR}/dir1 + MULTINITDIR_2=${MULTINITDIR}/dir2 + MULTINITDIR_3=${MULTINITDIR}/dir3 + R_MULINITDIR=../multinit + R_MULTINITDIR_1=${R_MULTINITDIR}/dir1 + R_MULTINITDIR_2=${R_MULTINITDIR}/dir2 + R_MULTINITDIR_3=${R_MULTINITDIR}/dir3 + # first create them all + mkdir -p ${MULTINITDIR} + mkdir -p ${MULTINITDIR_1} + mkdir -p ${MULTINITDIR_2} + mkdir -p ${MULTINITDIR_3} + # now copy them fro alice, bob, and dave + cd ${MULTINITDIR} + cp ${P_R_ALICEDIR}/* ${MULTINITDIR_1}/ + cp ${P_R_BOBDIR}/* ${MULTINITDIR_2}/ + cp ${P_R_DAVEDIR}/* ${MULTINITDIR_3}/ + # finally delete the RootCerts module to keep the certificate noice in the + # summary lines down + echo | modutil -delete RootCerts -dbdir ${MULTINITDIR_1} + echo | modutil -delete RootCerts -dbdir ${MULTINITDIR_2} + echo | modutil -delete RootCerts -dbdir ${MULTINITDIR_3} + MULTINIT_TESTS=${QADIR}/multinit/multinit.txt +} + + +############################## multinit_main ############################## +# local shell function to test basic signed and enveloped messages +# from 1 --> 2" +######################################################################## +multinit_main() +{ + html_head "Multi init interface testing" + exec < ${MULTINIT_TESTS} + while read order commands shutdown_type dirs readonly testname + do + if [ "$order" != "#" ]; then + read tag expected_result + + # handle the case where we expect different results based on + # the database type. + if [ "$tag" != "all" ]; then + read tag2 expected_result2 + if [ "$NSS_DEFAULT_DB_TYPE" == "$tag2" ]; then + expected_result=$expected_result2 + fi + fi + + # convert shutdown type to option flags + shutdown_command=""; + if [ "$shutdown_type" == "old" ]; then + shutdown_command="--oldStype" + fi + + # convert read only to option flags + ro_command=""; + case $readonly in + all) ro_command="--main_readonly --lib1_readonly --lib2_readonly";; + libs) ro_command="--lib1_readonly --lib2_readonly";; + main) ro_command="--main_readonly";; + lib1) ro_command="--lib1_readonly";; + lib2) ro_command="--lib2_readonly";; + none) ;; + *) ;; + esac + + # convert commands to option flags + main_command=`echo $commands | sed -e 's;,.*$;;'` + lib1_command=`echo $commands | sed -e 's;,.*,;+&+;' -e 's;^.*+,;;' -e 's;,+.*$;;'` + lib2_command=`echo $commands | sed -e 's;^.*,;;'` + + # convert db's to option flags + main_db=`echo $dirs | sed -e 's;,.*$;;'` + lib1_db=`echo $dirs | sed -e 's;,.*,;+&+;' -e 's;^.*+,;;' -e 's;,+.*$;;'` + lib2_db=`echo $dirs | sed -e 's;^.*,;;'` + + # show us the command we are executing + echo ${PROFILETOOL} ${BINDIR}/multinit --order $order --main_command $main_command --lib1_command $lib1_command --lib2_command $lib2_command $shutdown_command --main_db $main_db --lib1_db $lib1_db --lib2_db $lib2_db $ro_command --main_token_name "Main" --lib1_token_name "Lib1" --lib2_token_name "Lib2" --verbose --summary + + # execute the command an collect the result. Most of the user + # visible output goes to stderr, so it's not captured by the pipe + actual_result=`${PROFILETOOL} ${BINDIR}/multinit --order $order --main_command $main_command --lib1_command $lib1_command --lib2_command $lib2_command $shutdown_command --main_db $main_db --lib1_db $lib1_db --lib2_db $lib2_db $ro_command --main_token_name "Main" --lib1_token_name "Lib1" --lib2_token_name "Lib2" --verbose --summary | grep "^result=" | sed -e 's;^result=;;'` + + # show what we got and what we expected for diagnostic purposes + echo "actual = |$actual_result|" + echo "expected = |$expected_result|" + test "$actual_result" == "$expected_result" + html_msg $? 0 "$testname" + fi + done +} + +############################## multinit_cleanup ########################### +# local shell function to finish this script (no exit since it might be +# sourced) +######################################################################## +multinit_cleanup() +{ + html "</TABLE><BR>" + cd ${QADIR} + . common/cleanup.sh +} + +################## main ################################################# + +multinit_init +multinit_main +multinit_cleanup diff --git a/security/nss/tests/multinit/multinit.txt b/security/nss/tests/multinit/multinit.txt new file mode 100644 index 000000000..d5296dc0e --- /dev/null +++ b/security/nss/tests/multinit/multinit.txt @@ -0,0 +1,79 @@ +# +# This file defines the tests for multiple initialization of NSS in +# different libraries. +# +# Test description lines control the parameters for the multinit test program. +# +# Init order: Upper case/digits indicate an init call, lower case indicate +# a shutdown call. +# M,m-Main 1,i-lib1, 2,z-lib2 +# Main calls the traditional NSS init calls (simulating the main application) +# lib1 and lib2 call NSS_InitContext(). +# +# All functions call NSS_ShutdownContext unless 'main shutdown type' is set to +# 'old', in which case main will call the traditional NSS_Shutdown(). +# +# Commands: comma separated list of commands to execute. These simulate +# executing commands from either a library or main. In each cycle, multinit +# will do one initialize or shutdown, then execute all the commands +# for any of the libraries or main that is currently initialized. The same +# command is executed in each cycle that it's library is initialized. +# +# Commands are given in order or 'main','lib1','lib2'. Valid commands are: +# none - don't execute any commands for this library (or main). +# list_certs - list all the visible certs in the system. +# list_slots - list all the slots in the system. +# key_slot - list the current default key slot. +# +# Main Shutdown Type - which kind of shutdown does main call. See Init order. +# +# Directories - which directory should each init open. Listed in order of: +# (main init directory),(lib1 init directory),(lib2 init directory). +# +# RO - Which databases to open up read only, valid values are: +# all - main, lib1, and lib2 +# none - open all directories R/W +# libs - lib1 & lib2 +# main, lib1, lib2 - their respective directories only. +# +# Test description lines are followed by their expected summary output. +# output lines are of the form: +# +# tag expected output. +# +# where tag is one of +# all - applies to all database types +# sql - expected output for sql databases +# dbm - expected output for dbm databases +# +# if you do not specify all, you must have one line each for sql and dbm +# +# main +# init main,lib1,lib2 shutdown main,lib1,lib2 Test Case name +# order commands type directories RO +# ------ ------------------------ --- ----------- ----- -------------- + 1M2zmi list_slots,list_certs,none new dir1,dir2,dir3 all Progressive init +all 1C<Bob>uuuC<Dave>pppC<Eve>pppC<NSS Test CA>CTCCMS<NSS Generic Crypto Services>ttS<Main>ttS<Lib1>ttC<Alice>uuuC<Bob>pupupuC<Dave>pppC<Eve>pppC<NSS Test CA>CTCC2S<NSS Generic Crypto Services>ttS<Lib2>ttS<Main>ttS<Lib1>ttC<Alice>uuuC<Bob>pupupuC<Dave>pupupuC<Eve>pppC<NSS Test CA>CTCCZS<NSS Generic Crypto Services>ttS<Lib2>ttS<Main>ttS<Lib1>ttC<Alice>uuuC<Bob>pupupuC<Dave>pupupuC<Eve>pppC<NSS Test CA>CTCCNC<Alice>uuuC<Bob>pupupuC<Dave>pupupuC<Eve>pppC<NSS Test CA>CTCCI + 1M2zmi list_certs,none,none old dir1,dir2,dir3 all Progressive init - oldStyle +all 1MC<Alice>uuuC<Bob>pupupuC<Dave>pppC<Eve>pppC<NSS Test CA>CTCC2C<Alice>uuuC<Bob>pupupuC<Dave>pupupuC<Eve>pppC<NSS Test CA>CTCCZC<Alice>uuuC<Bob>pupupuC<Dave>pupupuC<Eve>pppC<NSS Test CA>CTCCNIE0xffffe09a + 12Mizm none,list_certs,none new dir1,dir2,dir3 all Sequenced init +all 1C<Bob>uuuC<Dave>pppC<Eve>pppC<NSS Test CA>CTCC2C<Bob>uuuC<Dave>pupupuC<Eve>pppC<NSS Test CA>CTCCMC<Alice>uuuC<Bob>pupupuC<Dave>pupupuC<Eve>pppC<NSS Test CA>CTCCIZN + 12Mizm none,list_certs,none old dir1,dir2,dir3 all Sequenced init - old Style +all 1C<Bob>uuuC<Dave>pppC<Eve>pppC<NSS Test CA>CTCC2C<Bob>uuuC<Dave>pupupuC<Eve>pppC<NSS Test CA>CTCCMC<Alice>uuuC<Bob>pupupuC<Dave>pupupuC<Eve>pppC<NSS Test CA>CTCCIZN + 1Mi2mz none,list_certs,list_slots new dir1,dir2,dir3 all Overlap shutdown +all 1C<Bob>uuuC<Dave>pppC<Eve>pppC<NSS Test CA>CTCCMC<Alice>uuuC<Bob>pupupuC<Dave>pppC<Eve>pppC<NSS Test CA>CTCCI2S<NSS Generic Crypto Services>ttS<Lib2>ttS<Main>ttS<Lib1>ttNS<NSS Generic Crypto Services>ttS<Lib2>ttS<Main>ttS<Lib1>ttZ + 1Mi2mz none,key_slot,none new dir1,dir2,dir3 all Keyslot test +all 1S<Lib1>ttMS<Main>ttI2NZ + M12miz none,key_slot,none new dir1,dir2,dir3 all Main init first +all M1S<Main>tt2S<Main>ttNS<Main>ttIZ + M12miz key_slot,none,none old dir1,dir2,dir3 all Main init first - old Style +all MS<Main>tt1S<Main>tt2S<Main>ttNIE0xffffe09aZE0xffffe09a + M12miz list_slots,none,none new dir1,dir1,dir2 all Loading the same directory twice +all MS<NSS Generic Crypto Services>ttS<Main>tt1S<NSS Generic Crypto Services>ttS<Main>tt2S<NSS Generic Crypto Services>ttS<Lib2>ttS<Main>ttNIZ + M12miz list_slots,none,none new dir1,dir1,dir2 libs Loading the same directory twice - r/w then ro +all MS<NSS Generic Crypto Services>ttS<Main>tf1S<NSS Generic Crypto Services>ttS<Main>tf2S<NSS Generic Crypto Services>ttS<Lib2>ttS<Main>tfNIZ + M12miz list_slots,none,none new dir1,dir1,dir2 main Loading the same directory twice - ro then r/w +sql MS<NSS Generic Crypto Services>ttS<Main>tt1S<NSS Generic Crypto Services>ttS<Lib1>tfS<Main>tt2S<NSS Generic Crypto Services>ttS<Lib2>tfS<Lib1>tfS<Main>ttNIZ +dbm MS<NSS Generic Crypto Services>ttS<Main>tt1S<NSS Generic Crypto Services>ttS<Main>tt2S<NSS Generic Crypto Services>ttS<Lib2>tfS<Main>ttNIZ + M12miM1zim key_slot,none,none old dir1,dir2,dir3 all Properly detect shutdown of a closed handle +all MS<Main>tt1S<Main>tt2S<Main>ttNIE0xffffe09aMS<Main>tt1S<Main>ttZE0xffffe09aS<Main>ttIS<Main>ttN diff --git a/security/nss/tests/tools/tools.sh b/security/nss/tests/tools/tools.sh index 545b742bb..8861543a7 100644 --- a/security/nss/tests/tools/tools.sh +++ b/security/nss/tests/tools/tools.sh @@ -121,16 +121,22 @@ tools_init() TOOLSDIR=${HOSTDIR}/tools COPYDIR=${TOOLSDIR}/copydir + SIGNDIR=${TOOLSDIR}/signdir R_TOOLSDIR=../tools R_COPYDIR=../tools/copydir + R_SIGNDIR=../tools/signdir P_R_COPYDIR=${R_COPYDIR} + P_R_SIGNDIR=${R_SIGNDIR} if [ -n "${MULTIACCESS_DBM}" ]; then P_R_COPYDIR="multiaccess:Tools.$version" + P_R_SIGNDIR="multiaccess:Tools.sign.$version" fi mkdir -p ${TOOLSDIR} mkdir -p ${COPYDIR} + mkdir -p ${SIGNDIR} + cp ${ALICEDIR}/* ${SIGNDIR}/ mkdir -p ${TOOLSDIR}/html cp ${QADIR}/tools/sign*.html ${TOOLSDIR}/html @@ -455,8 +461,8 @@ check_tmpfile() tools_sign() { echo "$SCRIPTNAME: Create objsign cert -------------------------------" - echo "signtool -G \"objectsigner\" -d ${P_R_ALICEDIR} -p \"nss\"" - ${BINDIR}/signtool -G "objsigner" -d ${P_R_ALICEDIR} -p "nss" 2>&1 <<SIGNSCRIPT + echo "signtool -G \"objectsigner\" -d ${P_R_SIGNDIR} -p \"nss\"" + ${BINDIR}/signtool -G "objsigner" -d ${P_R_SIGNDIR} -p "nss" 2>&1 <<SIGNSCRIPT y TEST MOZ @@ -469,37 +475,37 @@ SIGNSCRIPT html_msg $? 0 "Create objsign cert (signtool -G)" echo "$SCRIPTNAME: Signing a jar of files ----------------------------" - echo "signtool -Z nojs.jar -d ${P_R_ALICEDIR} -p \"nss\" -k objsigner \\" + echo "signtool -Z nojs.jar -d ${P_R_SIGNDIR} -p \"nss\" -k objsigner \\" echo " ${R_TOOLSDIR}/html" - ${BINDIR}/signtool -Z nojs.jar -d ${P_R_ALICEDIR} -p "nss" -k objsigner \ + ${BINDIR}/signtool -Z nojs.jar -d ${P_R_SIGNDIR} -p "nss" -k objsigner \ ${R_TOOLSDIR}/html html_msg $? 0 "Signing a jar of files (signtool -Z)" echo "$SCRIPTNAME: Listing signed files in jar ----------------------" - echo "signtool -v nojs.jar -d ${P_R_ALICEDIR} -p nss -k objsigner" - ${BINDIR}/signtool -v nojs.jar -d ${P_R_ALICEDIR} -p nss -k objsigner + echo "signtool -v nojs.jar -d ${P_R_SIGNDIR} -p nss -k objsigner" + ${BINDIR}/signtool -v nojs.jar -d ${P_R_SIGNDIR} -p nss -k objsigner html_msg $? 0 "Listing signed files in jar (signtool -v)" echo "$SCRIPTNAME: Show who signed jar ------------------------------" - echo "signtool -w nojs.jar -d ${P_R_ALICEDIR}" - ${BINDIR}/signtool -w nojs.jar -d ${P_R_ALICEDIR} + echo "signtool -w nojs.jar -d ${P_R_SIGNDIR}" + ${BINDIR}/signtool -w nojs.jar -d ${P_R_SIGNDIR} html_msg $? 0 "Show who signed jar (signtool -w)" echo "$SCRIPTNAME: Signing a xpi of files ----------------------------" - echo "signtool -Z nojs.xpi -X -d ${P_R_ALICEDIR} -p \"nss\" -k objsigner \\" + echo "signtool -Z nojs.xpi -X -d ${P_R_SIGNDIR} -p \"nss\" -k objsigner \\" echo " ${R_TOOLSDIR}/html" - ${BINDIR}/signtool -Z nojs.xpi -X -d ${P_R_ALICEDIR} -p "nss" -k objsigner \ + ${BINDIR}/signtool -Z nojs.xpi -X -d ${P_R_SIGNDIR} -p "nss" -k objsigner \ ${R_TOOLSDIR}/html html_msg $? 0 "Signing a xpi of files (signtool -Z -X)" echo "$SCRIPTNAME: Listing signed files in xpi ----------------------" - echo "signtool -v nojs.xpi -d ${P_R_ALICEDIR} -p nss -k objsigner" - ${BINDIR}/signtool -v nojs.xpi -d ${P_R_ALICEDIR} -p nss -k objsigner + echo "signtool -v nojs.xpi -d ${P_R_SIGNDIR} -p nss -k objsigner" + ${BINDIR}/signtool -v nojs.xpi -d ${P_R_SIGNDIR} -p nss -k objsigner html_msg $? 0 "Listing signed files in xpi (signtool -v)" echo "$SCRIPTNAME: Show who signed xpi ------------------------------" - echo "signtool -w nojs.xpi -d ${P_R_ALICEDIR}" - ${BINDIR}/signtool -w nojs.xpi -d ${P_R_ALICEDIR} + echo "signtool -w nojs.xpi -d ${P_R_SIGNDIR}" + ${BINDIR}/signtool -w nojs.xpi -d ${P_R_SIGNDIR} html_msg $? 0 "Show who signed xpi (signtool -w)" } |