/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #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 */ 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 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; CERTCertTrust trust; unsigned 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_GetCertTrust(cert, &trust) == SECSuccess) { appendFlags(trust.sslFlags); appendFlags(trust.emailFlags); appendFlags(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 (PORT_Strcasecmp(command_string, "list_slots") == 0) { do_list_slots(progName, log); } else if (PORT_Strcasecmp(command_string, "list_certs") == 0) { do_list_certs(progName, log); } else if (PORT_Strcasecmp(command_string, "add_cert") == 0) { do_add_cert(progName, log); } else if (PORT_Strcasecmp(command_string, "key_slot") == 0) { do_key_slot(progName, log); } else if (PORT_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); }