From a435c037e421b448fa5e81d113952186c06e4f83 Mon Sep 17 00:00:00 2001 From: Kai Engert Date: Thu, 29 May 2014 21:42:08 +0200 Subject: [PATCH] Bug 970539, NSS tool improvements/fixes: certutil/btoa/pp/httpserv, r=rrelyea --HG-- extra : amend_source : 50ec9151d179f3cadbf66ff958e7bda7198405f9 --- cmd/btoa/btoa.c | 20 ++- cmd/certutil/certext.c | 346 +++++++++++++++++++++++++++++++++++++--- cmd/certutil/certutil.c | 205 +++++++++++++++++------- cmd/certutil/certutil.h | 7 +- cmd/httpserv/httpserv.c | 4 +- cmd/lib/secutil.c | 25 ++- cmd/lib/secutil.h | 3 + cmd/pp/pp.c | 50 ++++-- lib/certdb/alg1485.c | 10 +- lib/certdb/genname.c | 33 ++++ lib/certdb/genname.h | 3 + lib/nss/nss.def | 2 + lib/pk11wrap/pk11load.c | 37 ++++- lib/util/oidstring.c | 31 ++++ tests/cert/cert.sh | 196 +++++++++++++++++++++++ 15 files changed, 858 insertions(+), 114 deletions(-) diff --git a/cmd/btoa/btoa.c b/cmd/btoa/btoa.c index 7cee58acd4..9416feb478 100644 --- a/cmd/btoa/btoa.c +++ b/cmd/btoa/btoa.c @@ -92,6 +92,10 @@ static void Usage(char *progName) "-i input"); fprintf(stderr, "%-20s Define an output file to use (default is stdout)\n", "-o output"); + fprintf(stderr, "%-20s Wrap output in BEGIN/END lines and the given suffix\n", + "-w suffix"); + fprintf(stderr, "%-20s (use \"c\" as a shortcut for suffix CERTIFICATE)\n", + ""); exit(-1); } @@ -102,6 +106,7 @@ int main(int argc, char **argv) FILE *inFile, *outFile; PLOptState *optstate; PLOptStatus status; + char *suffix = NULL; inFile = 0; outFile = 0; @@ -111,7 +116,7 @@ int main(int argc, char **argv) progName = progName ? progName+1 : argv[0]; /* Parse command line arguments */ - optstate = PL_CreateOptState(argc, argv, "i:o:"); + optstate = PL_CreateOptState(argc, argv, "i:o:w:"); while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { switch (optstate->option) { default: @@ -135,6 +140,13 @@ int main(int argc, char **argv) return -1; } break; + + case 'w': + if (!strcmp(optstate->value, "c")) + suffix = strdup("CERTIFICATE"); + else + suffix = strdup(optstate->value); + break; } } if (status == PL_OPT_BAD) @@ -171,11 +183,17 @@ int main(int argc, char **argv) #endif outFile = stdout; } + if (suffix) { + fprintf(outFile, "-----BEGIN %s-----\n", suffix); + } rv = encode_file(outFile, inFile); if (rv != SECSuccess) { fprintf(stderr, "%s: lossage: error=%d errno=%d\n", progName, PORT_GetError(), errno); return -1; } + if (suffix) { + fprintf(outFile, "-----END %s-----\n", suffix); + } return 0; } diff --git a/cmd/certutil/certext.c b/cmd/certutil/certext.c index ea423706fe..a87b4b1fa6 100644 --- a/cmd/certutil/certext.c +++ b/cmd/certutil/certext.c @@ -27,6 +27,8 @@ #include "xconst.h" #include "prprf.h" #include "certutil.h" +#include "genname.h" +#include "prnetdb.h" #define GEN_BREAK(e) rv=e; break; @@ -665,53 +667,213 @@ AddNscpCertType (void *extHandle, const char *userSuppliedValue) } +SECStatus +GetOidFromString(PLArenaPool *arena, SECItem *to, + const char *from, size_t fromLen) +{ + SECStatus rv; + SECOidTag tag; + SECOidData *coid; + + /* try dotted form first */ + rv = SEC_StringToOID(arena, to, from, fromLen); + if (rv == SECSuccess) { + return rv; + } + + /* Check to see if it matches a name in our oid table. + * SECOID_FindOIDByTag returns NULL if tag is out of bounds. + */ + tag = SEC_OID_UNKNOWN; + coid = SECOID_FindOIDByTag(tag); + for ( ; coid; coid = SECOID_FindOIDByTag(++tag)) { + if (PORT_Strncasecmp(from, coid->desc, fromLen) == 0) { + break; + } + } + if (coid == NULL) { + /* none found */ + return SECFailure; + } + return SECITEM_CopyItem(arena, to, &coid->oid); +} + static SECStatus AddSubjectAltNames(PLArenaPool *arena, CERTGeneralName **existingListp, - const char *names, CERTGeneralNameType type) + const char *constNames, CERTGeneralNameType type) { CERTGeneralName *nameList = NULL; CERTGeneralName *current = NULL; PRCList *prev = NULL; - const char *cp; - char *tbuf; + char *cp, *nextName = NULL; SECStatus rv = SECSuccess; + PRBool readTypeFromName = (PRBool) (type == 0); + char *names = NULL; + + if (constNames) + names = PORT_Strdup(constNames); + + if (names == NULL) { + return SECFailure; + } /* * walk down the comma separated list of names. NOTE: there is * no sanity checks to see if the email address look like * email addresses. + * + * Each name may optionally be prefixed with a type: string. + * If it isn't, the type from the previous name will be used. + * If there wasn't a previous name yet, the type given + * as a parameter to this function will be used. + * If the type value is zero (undefined), we'll fail. */ - for (cp=names; cp; cp = PORT_Strchr(cp,',')) { + for (cp=names; cp; cp=nextName) { int len; - char *end; - + char *oidString; + char *nextComma; + CERTName *name; + PRStatus status; + unsigned char *data; + PRNetAddr addr; + + nextName = NULL; if (*cp == ',') { cp++; } - end = PORT_Strchr(cp,','); - len = end ? end-cp : PORT_Strlen(cp); - if (len <= 0) { + nextComma = PORT_Strchr(cp, ','); + if (nextComma) { + *nextComma = 0; + nextName = nextComma+1; + } + if ((*cp) == 0) { continue; } - tbuf = PORT_ArenaAlloc(arena,len+1); - PORT_Memcpy(tbuf,cp,len); - tbuf[len] = 0; - current = (CERTGeneralName *) PORT_ZAlloc(sizeof(CERTGeneralName)); + if (readTypeFromName) { + char *save=cp; + /* Because we already replaced nextComma with end-of-string, + * a found colon belongs to the current name */ + cp = PORT_Strchr(cp, ':'); + if (cp) { + *cp = 0; + cp++; + type = CERT_GetGeneralNameTypeFromString(save); + if (*cp == 0) { + continue; + } + } else { + if (type == 0) { + /* no type known yet */ + rv = SECFailure; + break; + } + cp = save; + } + } + + current = PORT_ArenaZNew(arena, CERTGeneralName); if (!current) { rv = SECFailure; break; } + + current->type = type; + switch (type) { + /* string types */ + case certRFC822Name: + case certDNSName: + case certURI: + current->name.other.data = + (unsigned char *) PORT_ArenaStrdup(arena,cp); + current->name.other.len = PORT_Strlen(cp); + break; + /* unformated data types */ + case certX400Address: + case certEDIPartyName: + /* turn a string into a data and len */ + rv = SECFailure; /* punt on these for now */ + fprintf(stderr,"EDI Party Name and X.400 Address not supported\n"); + break; + case certDirectoryName: + /* certDirectoryName */ + name = CERT_AsciiToName(cp); + if (name == NULL) { + rv = SECFailure; + fprintf(stderr, "Invalid Directory Name (\"%s\")\n", cp); + break; + } + rv = CERT_CopyName(arena,¤t->name.directoryName,name); + CERT_DestroyName(name); + break; + /* types that require more processing */ + case certIPAddress: + /* convert the string to an ip address */ + status = PR_StringToNetAddr(cp, &addr); + if (status != PR_SUCCESS) { + rv = SECFailure; + fprintf(stderr, "Invalid IP Address (\"%s\")\n", cp); + break; + } + + if (PR_NetAddrFamily(&addr) == PR_AF_INET) { + len = sizeof(addr.inet.ip); + data = (unsigned char *)&addr.inet.ip; + } else if (PR_NetAddrFamily(&addr) == PR_AF_INET6) { + len = sizeof(addr.ipv6.ip); + data = (unsigned char *)&addr.ipv6.ip; + } else { + fprintf(stderr, "Invalid IP Family\n"); + rv = SECFailure; + break; + } + current->name.other.data = PORT_ArenaAlloc(arena, len); + if (current->name.other.data == NULL) { + rv = SECFailure; + break; + } + current->name.other.len = len; + PORT_Memcpy(current->name.other.data,data, len); + break; + case certRegisterID: + rv = GetOidFromString(arena, ¤t->name.other, cp, strlen(cp)); + break; + case certOtherName: + oidString = cp; + cp = PORT_Strchr(cp,';'); + if (cp == NULL) { + rv = SECFailure; + fprintf(stderr, "missing name in other name\n"); + break; + } + *cp++ = 0; + current->name.OthName.name.data = + (unsigned char *) PORT_ArenaStrdup(arena,cp); + if (current->name.OthName.name.data == NULL) { + rv = SECFailure; + break; + } + current->name.OthName.name.len = PORT_Strlen(cp); + rv = GetOidFromString(arena, ¤t->name.OthName.oid, + oidString, strlen(oidString)); + break; + default: + rv = SECFailure; + fprintf(stderr, "Missing or invalid Subject Alternate Name type\n"); + break; + } + if (rv == SECFailure) { + break; + } + if (prev) { current->l.prev = prev; prev->next = &(current->l); } else { nameList = current; } - current->type = type; - current->name.other.data = (unsigned char *)tbuf; - current->name.other.len = PORT_Strlen(tbuf); prev = &(current->l); } + PORT_Free(names); /* at this point nameList points to the head of a doubly linked, * but not yet circular, list and current points to its tail. */ if (rv == SECSuccess && nameList) { @@ -749,6 +911,12 @@ AddDNSSubjectAlt(PLArenaPool *arena, CERTGeneralName **existingListp, return AddSubjectAltNames(arena, existingListp, dnsNames, certDNSName); } +static SECStatus +AddGeneralSubjectAlt(PLArenaPool *arena, CERTGeneralName **existingListp, + const char *altNames) +{ + return AddSubjectAltNames(arena, existingListp, altNames, 0); +} static SECStatus AddBasicConstraint(void *extHandle) @@ -1746,12 +1914,73 @@ AddInfoAccess(void *extHandle, PRBool addSIAExt, PRBool isCACert) return (rv); } +/* Example of valid input: + * 1.2.3.4:critical:/tmp/abc,5.6.7.8:not-critical:/tmp/xyz + */ +static SECStatus +parseNextGenericExt(const char *nextExtension, const char **oid, int *oidLen, + const char **crit, int *critLen, + const char **filename, int *filenameLen, + const char **next) +{ + const char *nextColon; + const char *nextComma; + const char *iter = nextExtension; + + if (!iter || !*iter) + return SECFailure; + + /* Require colons at earlier positions than nextComma (or end of string ) */ + nextComma = strchr(iter, ','); + + *oid = iter; + nextColon = strchr(iter, ':'); + if (!nextColon || (nextComma && nextColon > nextComma)) + return SECFailure; + *oidLen = (nextColon - *oid); + + if (!*oidLen) + return SECFailure; + + iter = nextColon; + ++iter; + + *crit = iter; + nextColon = strchr(iter, ':'); + if (!nextColon || (nextComma && nextColon > nextComma)) + return SECFailure; + *critLen = (nextColon - *crit); + + if (!*critLen) + return SECFailure; + + iter = nextColon; + ++iter; + + *filename = iter; + if (nextComma) { + *filenameLen = (nextComma - *filename); + iter = nextComma; + ++iter; + *next = iter; + } else { + *filenameLen = strlen(*filename); + *next = NULL; + } + + if (!*filenameLen) + return SECFailure; + + return SECSuccess; +} + SECStatus AddExtensions(void *extHandle, const char *emailAddrs, const char *dnsNames, - certutilExtnList extList) + certutilExtnList extList, const char *extGeneric) { SECStatus rv = SECSuccess; char *errstring = NULL; + const char *nextExtension = NULL; do { /* Add key usage extension */ @@ -1864,7 +2093,7 @@ AddExtensions(void *extHandle, const char *emailAddrs, const char *dnsNames, } } - if (emailAddrs || dnsNames) { + if (emailAddrs || dnsNames || extList[ext_subjectAltName].activated) { PLArenaPool *arena; CERTGeneralName *namelist = NULL; SECItem item = { 0, NULL, 0 }; @@ -1874,10 +2103,21 @@ AddExtensions(void *extHandle, const char *emailAddrs, const char *dnsNames, rv = SECFailure; break; } + + rv = SECSuccess; + + if (emailAddrs) { + rv |= AddEmailSubjectAlt(arena, &namelist, emailAddrs); + } - rv = AddEmailSubjectAlt(arena, &namelist, emailAddrs); + if (dnsNames) { + rv |= AddDNSSubjectAlt(arena, &namelist, dnsNames); + } - rv |= AddDNSSubjectAlt(arena, &namelist, dnsNames); + if (extList[ext_subjectAltName].activated) { + rv |= AddGeneralSubjectAlt(arena, &namelist, + extList[ext_subjectAltName].arg); + } if (rv == SECSuccess) { rv = CERT_EncodeAltNameExtension(arena, namelist, &item); @@ -1898,5 +2138,71 @@ AddExtensions(void *extHandle, const char *emailAddrs, const char *dnsNames, if (rv != SECSuccess) { SECU_PrintError(progName, "Problem creating %s extension", errstring); } + + nextExtension = extGeneric; + while (nextExtension && *nextExtension) { + SECItem oid_item, value; + PRBool isCritical; + const char *oid, *crit, *filename, *next; + int oidLen, critLen, filenameLen; + PRFileDesc *inFile = NULL; + char *zeroTerminatedFilename = NULL; + + rv = parseNextGenericExt(nextExtension, &oid, &oidLen, &crit, &critLen, + &filename, &filenameLen, &next); + if (rv!= SECSuccess) { + SECU_PrintError(progName, + "error parsing generic extension parameter %s", + nextExtension); + break; + } + oid_item.data = NULL; + oid_item.len = 0; + rv = GetOidFromString(NULL, &oid_item, oid, oidLen); + if (rv != SECSuccess) { + SECU_PrintError(progName, "malformed extension OID %s", nextExtension); + break; + } + if (!strncmp("critical", crit, critLen)) { + isCritical = PR_TRUE; + } else if (!strncmp("not-critical", crit, critLen)) { + isCritical = PR_FALSE; + } else { + rv = SECFailure; + SECU_PrintError(progName, "expected 'critical' or 'not-critical'"); + break; + } + zeroTerminatedFilename = PL_strndup(filename, filenameLen); + if (!zeroTerminatedFilename) { + rv = SECFailure; + SECU_PrintError(progName, "out of memory"); + break; + } + rv = SECFailure; + inFile = PR_Open(zeroTerminatedFilename, PR_RDONLY, 0); + if (inFile) { + rv = SECU_ReadDERFromFile(&value, inFile, PR_FALSE, PR_FALSE); + PR_Close(inFile); + inFile = NULL; + } + if (rv != SECSuccess) { + SECU_PrintError(progName, "unable to read file %s", + zeroTerminatedFilename); + } + PL_strfree(zeroTerminatedFilename); + if (rv != SECSuccess) { + break; + } + rv = CERT_AddExtensionByOID(extHandle, &oid_item, &value, isCritical, + PR_FALSE /*copyData*/); + if (rv != SECSuccess) { + SECITEM_FreeItem(&oid_item, PR_FALSE); + SECITEM_FreeItem(&value, PR_FALSE); + SECU_PrintError(progName, "failed to add extension %s", nextExtension); + break; + } + nextExtension = next; + } + return rv; } diff --git a/cmd/certutil/certutil.c b/cmd/certutil/certutil.c index 1c33e6fccf..5b5d744aed 100644 --- a/cmd/certutil/certutil.c +++ b/cmd/certutil/certutil.c @@ -182,7 +182,7 @@ static SECStatus CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType, SECOidTag hashAlgTag, CERTName *subject, char *phone, int ascii, const char *emailAddrs, const char *dnsNames, - certutilExtnList extnList, + certutilExtnList extnList, const char *extGeneric, /*out*/ SECItem *result) { CERTSubjectPublicKeyInfo *spki; @@ -220,7 +220,7 @@ CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType, PORT_FreeArena (arena, PR_FALSE); return SECFailure; } - if (AddExtensions(extHandle, emailAddrs, dnsNames, extnList) + if (AddExtensions(extHandle, emailAddrs, dnsNames, extnList, extGeneric) != SECSuccess) { PORT_FreeArena (arena, PR_FALSE); return SECFailure; @@ -420,11 +420,64 @@ DumpChain(CERTCertDBHandle *handle, char *name, PRBool ascii) } static SECStatus -listCerts(CERTCertDBHandle *handle, char *name, char *email, PK11SlotInfo *slot, - PRBool raw, PRBool ascii, PRFileDesc *outfile, void *pwarg) +outputCertOrExtension(CERTCertificate *the_cert, PRBool raw, PRBool ascii, + SECItem *extensionOID, PRFileDesc *outfile) { SECItem data; PRInt32 numBytes; + SECStatus rv = SECFailure; + if (extensionOID) { + int i; + PRBool found = PR_FALSE; + for (i=0; the_cert->extensions[i] != NULL; i++) { + CERTCertExtension *extension = the_cert->extensions[i]; + if (SECITEM_CompareItem(&extension->id, extensionOID) == SECEqual) { + found = PR_TRUE; + numBytes = PR_Write(outfile, extension->value.data, + extension->value.len); + rv = SECSuccess; + if (numBytes != (PRInt32) extension->value.len) { + SECU_PrintSystemError(progName, "error writing extension"); + rv = SECFailure; + } + rv = SECSuccess; + break; + } + } + if (!found) { + SECU_PrintSystemError(progName, "extension not found"); + rv = SECFailure; + } + } else { + data.data = the_cert->derCert.data; + data.len = the_cert->derCert.len; + if (ascii) { + PR_fprintf(outfile, "%s\n%s\n%s\n", NS_CERT_HEADER, + BTOA_DataToAscii(data.data, data.len), NS_CERT_TRAILER); + rv = SECSuccess; + } else if (raw) { + numBytes = PR_Write(outfile, data.data, data.len); + rv = SECSuccess; + if (numBytes != (PRInt32) data.len) { + SECU_PrintSystemError(progName, "error writing raw cert"); + rv = SECFailure; + } + } else { + rv = SEC_PrintCertificateAndTrust(the_cert, "Certificate", NULL); + if (rv != SECSuccess) { + SECU_PrintError(progName, "problem printing certificate"); + } + } + } + return rv; +} + +static SECStatus +listCerts(CERTCertDBHandle *handle, char *name, char *email, + PK11SlotInfo *slot, PRBool raw, PRBool ascii, + SECItem *extensionOID, + PRFileDesc *outfile, void *pwarg) +{ SECStatus rv = SECFailure; CERTCertList *certs; CERTCertListNode *node; @@ -461,34 +514,13 @@ listCerts(CERTCertDBHandle *handle, char *name, char *email, PK11SlotInfo *slot, } for (node = CERT_LIST_HEAD(certs); !CERT_LIST_END(node,certs); node = CERT_LIST_NEXT(node)) { - the_cert = node->cert; - /* now get the subjectList that matches this cert */ - data.data = the_cert->derCert.data; - data.len = the_cert->derCert.len; - if (ascii) { - PR_fprintf(outfile, "%s\n%s\n%s\n", NS_CERT_HEADER, - BTOA_DataToAscii(data.data, data.len), NS_CERT_TRAILER); - rv = SECSuccess; - } else if (raw) { - numBytes = PR_Write(outfile, data.data, data.len); - if (numBytes != (PRInt32) data.len) { - SECU_PrintSystemError(progName, "error writing raw cert"); - rv = SECFailure; - } - rv = SECSuccess; - } else { - rv = SEC_PrintCertificateAndTrust(the_cert, "Certificate", NULL); - if (rv != SECSuccess) { - SECU_PrintError(progName, "problem printing certificate"); - } - - } + rv = outputCertOrExtension(node->cert, raw, ascii, extensionOID, + outfile); if (rv != SECSuccess) { break; } } } else if (email) { - CERTCertificate *the_cert; certs = PK11_FindCertsFromEmailAddress(email, NULL); if (!certs) { SECU_PrintError(progName, @@ -498,28 +530,8 @@ listCerts(CERTCertDBHandle *handle, char *name, char *email, PK11SlotInfo *slot, } for (node = CERT_LIST_HEAD(certs); !CERT_LIST_END(node,certs); node = CERT_LIST_NEXT(node)) { - the_cert = node->cert; - /* now get the subjectList that matches this cert */ - data.data = the_cert->derCert.data; - data.len = the_cert->derCert.len; - if (ascii) { - PR_fprintf(outfile, "%s\n%s\n%s\n", NS_CERT_HEADER, - BTOA_DataToAscii(data.data, data.len), - NS_CERT_TRAILER); - rv = SECSuccess; - } else if (raw) { - numBytes = PR_Write(outfile, data.data, data.len); - rv = SECSuccess; - if (numBytes != (PRInt32) data.len) { - SECU_PrintSystemError(progName, "error writing raw cert"); - rv = SECFailure; - } - } else { - rv = SEC_PrintCertificateAndTrust(the_cert, "Certificate", NULL); - if (rv != SECSuccess) { - SECU_PrintError(progName, "problem printing certificate"); - } - } + rv = outputCertOrExtension(node->cert, raw, ascii, extensionOID, + outfile); if (rv != SECSuccess) { break; } @@ -547,8 +559,9 @@ listCerts(CERTCertDBHandle *handle, char *name, char *email, PK11SlotInfo *slot, static SECStatus ListCerts(CERTCertDBHandle *handle, char *nickname, char *email, - PK11SlotInfo *slot, PRBool raw, PRBool ascii, PRFileDesc *outfile, - secuPWData *pwdata) + PK11SlotInfo *slot, PRBool raw, PRBool ascii, + SECItem *extensionOID, + PRFileDesc *outfile, secuPWData *pwdata) { SECStatus rv; @@ -569,7 +582,8 @@ ListCerts(CERTCertDBHandle *handle, char *nickname, char *email, CERT_DestroyCertList(list); return SECSuccess; } - rv = listCerts(handle, nickname, email, slot, raw, ascii, outfile, pwdata); + rv = listCerts(handle, nickname, email, slot, raw, ascii, + extensionOID, outfile, pwdata); return rv; } @@ -615,6 +629,15 @@ ValidateCert(CERTCertDBHandle *handle, char *name, char *date, case 'O': usage = certificateUsageStatusResponder; break; + case 'L': + usage = certificateUsageSSLCA; + break; + case 'A': + usage = certificateUsageAnyCA; + break; + case 'Y': + usage = certificateUsageVerifyCA; + break; case 'C': usage = certificateUsageSSLClient; break; @@ -989,7 +1012,7 @@ PrintSyntax(char *progName) FPS "\t\t [-f targetPWfile] [-@ sourcePWFile]\n"); FPS "\t%s -L [-n cert-name] [--email email-address] [-X] [-r] [-a]\n", progName); - FPS "\t\t [-d certdir] [-P dbprefix]\n"); + FPS "\t\t [--dump-ext-val OID] [-d certdir] [-P dbprefix]\n"); FPS "\t%s -M -n cert-name -t trustargs [-d certdir] [-P dbprefix]\n", progName); FPS "\t%s -O -n cert-name [-X] [-d certdir] [-a] [-P dbprefix]\n", progName); @@ -1008,7 +1031,8 @@ PrintSyntax(char *progName) "\t\t [-p phone] [-1] [-2] [-3] [-4] [-5] [-6] [-7 emailAddrs]\n" "\t\t [-8 DNS-names]\n" "\t\t [--extAIA] [--extSIA] [--extCP] [--extPM] [--extPC] [--extIA]\n" - "\t\t [--extSKID] [--extNC]\n", progName); + "\t\t [--extSKID] [--extNC] [--extSAN type:name[,type:name]...]\n" + "\t\t [--extGeneric OID:critical-flag:filename[,OID:critical-flag:filename]...]\n", progName); FPS "\t%s -U [-X] [-d certdir] [-P dbprefix]\n", progName); exit(1); } @@ -1308,7 +1332,7 @@ static void luL(enum usage_level ul, const char *command) { int is_my_command = (command && 0 == strcmp(command, "L")); if (ul == usage_all || !command || is_my_command) - FPS "%-15s List all certs, or print out a single named cert\n", + FPS "%-15s List all certs, or print out a single named cert (or a subset)\n", "-L"); if (ul == usage_selected && !is_my_command) return; @@ -1327,6 +1351,9 @@ static void luL(enum usage_level ul, const char *command) " -r"); FPS "%-20s For single cert, print ASCII encoding (RFC1113)\n", " -a"); + FPS "%-20s \n" + "%-20s For single cert, print binary DER encoding of extension OID\n", + " --dump-ext-val OID", ""); FPS "\n"); } @@ -1472,6 +1499,9 @@ static void luV(enum usage_level ul, const char *command) FPS "%-20s Specify certificate usage:\n", " -u certusage"); FPS "%-25s C \t SSL Client\n", ""); FPS "%-25s V \t SSL Server\n", ""); + FPS "%-25s L \t SSL CA\n", ""); + FPS "%-25s A \t Any CA\n", ""); + FPS "%-25s Y \t Verify CA\n", ""); FPS "%-25s S \t Email signer\n", ""); FPS "%-25s R \t Email Recipient\n", ""); FPS "%-25s O \t OCSP status responder\n", ""); @@ -1638,6 +1668,18 @@ static void luS(enum usage_level ul, const char *command) " See -G for available key flag options"); FPS "%-20s Create a name constraints extension\n", " --extNC "); + FPS "%-20s \n" + "%-20s Create a Subject Alt Name extension with one or multiple names\n", + " --extSAN type:name[,type:name]...", ""); + FPS "%-20s - type: directory, dn, dns, edi, ediparty, email, ip, ipaddr,\n", ""); + FPS "%-20s other, registerid, rfc822, uri, x400, x400addr\n", ""); + FPS "%-20s \n" + "%-20s Add one or multiple extensions that certutil cannot encode yet,\n" + "%-20s by loading their encodings from external files.\n", + " --extGeneric OID:critical-flag:filename[,OID:critical-flag:filename]...", "", ""); + FPS "%-20s - OID (example): 1.2.3.4\n", ""); + FPS "%-20s - critical-flag: critical or not-critical\n", ""); + FPS "%-20s - filename: full path to a file containing an encoded extension\n", ""); FPS "\n"); } @@ -1836,6 +1878,7 @@ CreateCert( PRBool ascii, PRBool selfsign, certutilExtnList extnList, + const char *extGeneric, int certVersion, SECItem * certDER) { @@ -1864,7 +1907,7 @@ CreateCert( GEN_BREAK (SECFailure) } - rv = AddExtensions(extHandle, emailAddrs, dnsNames, extnList); + rv = AddExtensions(extHandle, emailAddrs, dnsNames, extnList, extGeneric); if (rv != SECSuccess) { GEN_BREAK (SECFailure) } @@ -2212,6 +2255,9 @@ enum certutilOpts { opt_KeyAttrFlags, opt_EmptyPassword, opt_CertVersion, + opt_AddSubjectAltNameExt, + opt_DumpExtensionValue, + opt_GenericExtensions, opt_Help }; @@ -2323,6 +2369,11 @@ secuCommandFlag options_init[] = "empty-password"}, { /* opt_CertVersion */ 0, PR_FALSE, 0, PR_FALSE, "certVersion"}, + { /* opt_AddSubjectAltExt */ 0, PR_TRUE, 0, PR_FALSE, "extSAN"}, + { /* opt_DumpExtensionValue */ 0, PR_TRUE, 0, PR_FALSE, + "dump-ext-val"}, + { /* opt_GenericExtensions */ 0, PR_TRUE, 0, PR_FALSE, + "extGeneric"}, }; #define NUM_OPTIONS ((sizeof options_init) / (sizeof options_init[0])) @@ -2663,9 +2714,10 @@ certutil_main(int argc, char **argv, PRBool initialize) return 255; } - /* if -L is given raw or ascii mode, it must be for only one cert. */ + /* if -L is given raw, ascii or dump mode, it must be for only one cert. */ if (certutil.commands[cmd_ListCerts].activated && (certutil.options[opt_ASCIIForIO].activated || + certutil.options[opt_DumpExtensionValue].activated || certutil.options[opt_BinaryDER].activated) && !certutil.options[opt_Nickname].activated) { PR_fprintf(PR_STDERR, @@ -2985,10 +3037,29 @@ certutil_main(int argc, char **argv, PRBool initialize) /* List certs (-L) */ if (certutil.commands[cmd_ListCerts].activated) { - rv = ListCerts(certHandle, name, email, slot, - certutil.options[opt_BinaryDER].activated, - certutil.options[opt_ASCIIForIO].activated, - outFile, &pwdata); + if (certutil.options[opt_DumpExtensionValue].activated) { + const char *oid_str; + SECItem oid_item; + oid_item.data = NULL; + oid_item.len = 0; + oid_str = certutil.options[opt_DumpExtensionValue].arg; + SECStatus srv = GetOidFromString(NULL, &oid_item, + oid_str, strlen(oid_str)); + if (srv != SECSuccess) { + SECU_PrintError(progName, "malformed extension OID %s", + oid_str); + goto shutdown; + } + rv = ListCerts(certHandle, name, email, slot, + PR_TRUE /*binary*/, PR_FALSE /*ascii*/, + &oid_item, + outFile, &pwdata); + } else { + rv = ListCerts(certHandle, name, email, slot, + certutil.options[opt_BinaryDER].activated, + certutil.options[opt_ASCIIForIO].activated, + NULL, outFile, &pwdata); + } goto shutdown; } if (certutil.commands[cmd_DumpChain].activated) { @@ -3179,6 +3250,12 @@ certutil_main(int argc, char **argv, PRBool initialize) certutil_extns[ext_extKeyUsage].arg = certutil.options[opt_AddCmdExtKeyUsageExt].arg; } + certutil_extns[ext_subjectAltName].activated = + certutil.options[opt_AddSubjectAltNameExt].activated; + if (certutil_extns[ext_subjectAltName].activated) { + certutil_extns[ext_subjectAltName].arg = + certutil.options[opt_AddSubjectAltNameExt].arg; + } certutil_extns[ext_authInfoAcc].activated = certutil.options[opt_AddAuthInfoAccExt].activated; @@ -3218,6 +3295,8 @@ certutil_main(int argc, char **argv, PRBool initialize) certutil.options[opt_ExtendedEmailAddrs].arg, certutil.options[opt_ExtendedDNSNames].arg, certutil_extns, + (certutil.options[opt_GenericExtensions].activated ? + certutil.options[opt_GenericExtensions].arg : NULL), &certReqDER); if (rv) goto shutdown; @@ -3240,6 +3319,8 @@ certutil_main(int argc, char **argv, PRBool initialize) NULL, NULL, nullextnlist, + (certutil.options[opt_GenericExtensions].activated ? + certutil.options[opt_GenericExtensions].arg : NULL), &certReqDER); if (rv) goto shutdown; @@ -3259,6 +3340,8 @@ certutil_main(int argc, char **argv, PRBool initialize) certutil.commands[cmd_CreateNewCert].activated, certutil.options[opt_SelfSign].activated, certutil_extns, + (certutil.options[opt_GenericExtensions].activated ? + certutil.options[opt_GenericExtensions].arg : NULL), certVersion, &certDER); if (rv) diff --git a/cmd/certutil/certutil.h b/cmd/certutil/certutil.h index d4388fc349..9286649071 100644 --- a/cmd/certutil/certutil.h +++ b/cmd/certutil/certutil.h @@ -35,6 +35,7 @@ enum certutilExtns { ext_inhibitAnyPolicy, ext_subjectKeyID, ext_nameConstraints, + ext_subjectAltName, ext_End }; @@ -47,7 +48,11 @@ typedef ExtensionEntry certutilExtnList[ext_End]; extern SECStatus AddExtensions(void *extHandle, const char *emailAddrs, const char *dnsNames, - certutilExtnList extList); + certutilExtnList extList, const char *extGeneric); + +extern SECStatus +GetOidFromString(PLArenaPool *arena, SECItem *to, + const char *from, size_t fromLen); #endif /* _CERTUTIL_H */ diff --git a/cmd/httpserv/httpserv.c b/cmd/httpserv/httpserv.c index 6f37e42a84..875b62bbd0 100644 --- a/cmd/httpserv/httpserv.c +++ b/cmd/httpserv/httpserv.c @@ -1312,8 +1312,10 @@ main(int argc, char **argv) inFile = PR_Open(revoInfo->crlFilename, PR_RDONLY, 0); if (inFile) { rv = SECU_ReadDERFromFile(&crlDER, inFile, PR_FALSE, PR_FALSE); + PR_Close(inFile); + inFile = NULL; } - if (!inFile || rv != SECSuccess) { + if (rv != SECSuccess) { fprintf(stderr, "unable to read crl file %s\n", revoInfo->crlFilename); exit(1); diff --git a/cmd/lib/secutil.c b/cmd/lib/secutil.c index d06dcf3cb3..02570103a0 100644 --- a/cmd/lib/secutil.c +++ b/cmd/lib/secutil.c @@ -52,6 +52,19 @@ static char consoleName[] = { #include "ssl.h" #include "sslproto.h" +static PRBool utf8DisplayEnabled = PR_FALSE; + +void +SECU_EnableUtf8Display(PRBool enable) +{ + utf8DisplayEnabled = enable; +} + +PRBool +SECU_GetUtf8DisplayEnabled(void) +{ + return utf8DisplayEnabled; +} static void secu_ClearPassword(char *p) @@ -609,12 +622,22 @@ secu_PrintRawStringQuotesOptional(FILE *out, SECItem *si, const char *m, for (i = 0; i < si->len; i++) { unsigned char val = si->data[i]; + unsigned char c; if (SECU_GetWrapEnabled() && column > 76) { SECU_Newline(out); SECU_Indent(out, level); column = level*INDENT_MULT; } - fprintf(out,"%c", printable[val]); column++; + if (utf8DisplayEnabled) { + if (val < 32) + c = '.'; + else + c = val; + } else { + c = printable[val]; + } + fprintf(out,"%c", c); + column++; } if (quotes) { diff --git a/cmd/lib/secutil.h b/cmd/lib/secutil.h index 71a7f59b8c..2a29991888 100644 --- a/cmd/lib/secutil.h +++ b/cmd/lib/secutil.h @@ -139,6 +139,9 @@ SECU_GetClientAuthData(void *arg, PRFileDesc *fd, extern PRBool SECU_GetWrapEnabled(void); extern void SECU_EnableWrap(PRBool enable); +extern PRBool SECU_GetUtf8DisplayEnabled(void); +extern void SECU_EnableUtf8Display(PRBool enable); + /* revalidate the cert and print information about cert verification * failure at time == now */ extern void diff --git a/cmd/pp/pp.c b/cmd/pp/pp.c index c97b3e79b6..a739a915ec 100644 --- a/cmd/pp/pp.c +++ b/cmd/pp/pp.c @@ -22,22 +22,27 @@ extern int fprintf(FILE *, char *, ...); static void Usage(char *progName) { fprintf(stderr, - "Usage: %s -t type [-a] [-i input] [-o output] [-w]\n", + "Usage: %s [-t type] [-a] [-i input] [-o output] [-w] [-u]\n", progName); - fprintf(stderr, "%-20s Specify the input type (must be one of %s,\n", + fprintf(stderr, "Pretty prints a file containing ASN.1 data in DER or ascii format.\n"); + fprintf(stderr, "%-14s Specify input and display type: %s (sk),\n", "-t type", SEC_CT_PRIVATE_KEY); - fprintf(stderr, "%-20s %s, %s, %s,\n", "", SEC_CT_PUBLIC_KEY, + fprintf(stderr, "%-14s %s (pk), %s (c), %s (cr),\n", "", SEC_CT_PUBLIC_KEY, SEC_CT_CERTIFICATE, SEC_CT_CERTIFICATE_REQUEST); - fprintf(stderr, "%-20s %s, %s, %s or %s)\n", "", SEC_CT_CERTIFICATE_ID, + fprintf(stderr, "%-14s %s (ci), %s (p7), %s or %s (n).\n", "", SEC_CT_CERTIFICATE_ID, SEC_CT_PKCS7, SEC_CT_CRL, SEC_CT_NAME); - fprintf(stderr, "%-20s Input is in ascii encoded form (RFC1113)\n", + fprintf(stderr, "%-14s (Use either the long type name or the shortcut.)\n", "", SEC_CT_CERTIFICATE_ID, + SEC_CT_PKCS7, SEC_CT_CRL, SEC_CT_NAME); + fprintf(stderr, "%-14s Input is in ascii encoded form (RFC1113)\n", "-a"); - fprintf(stderr, "%-20s Define an input file to use (default is stdin)\n", + fprintf(stderr, "%-14s Define an input file to use (default is stdin)\n", "-i input"); - fprintf(stderr, "%-20s Define an output file to use (default is stdout)\n", + fprintf(stderr, "%-14s Define an output file to use (default is stdout)\n", "-o output"); - fprintf(stderr, "%-20s Don't wrap long output lines\n", + fprintf(stderr, "%-14s Don't wrap long output lines\n", "-w"); + fprintf(stderr, "%-14s Use UTF-8 (default is to show non-ascii as .)\n", + "-u"); exit(-1); } @@ -59,7 +64,7 @@ int main(int argc, char **argv) inFile = 0; outFile = 0; typeTag = 0; - optstate = PL_CreateOptState(argc, argv, "at:i:o:w"); + optstate = PL_CreateOptState(argc, argv, "at:i:o:uw"); while ( PL_GetNextOpt(optstate) == PL_OPT_OK ) { switch (optstate->option) { case '?': @@ -92,6 +97,10 @@ int main(int argc, char **argv) typeTag = strdup(optstate->value); break; + case 'u': + SECU_EnableUtf8Display(PR_TRUE); + break; + case 'w': wrap = PR_FALSE; break; @@ -125,27 +134,34 @@ int main(int argc, char **argv) SECU_EnableWrap(wrap); /* Pretty print it */ - if (PORT_Strcmp(typeTag, SEC_CT_CERTIFICATE) == 0) { + if (PORT_Strcmp(typeTag, SEC_CT_CERTIFICATE) == 0 || + PORT_Strcmp(typeTag, "c") == 0) { rv = SECU_PrintSignedData(outFile, &data, "Certificate", 0, SECU_PrintCertificate); - } else if (PORT_Strcmp(typeTag, SEC_CT_CERTIFICATE_ID) == 0) { + } else if (PORT_Strcmp(typeTag, SEC_CT_CERTIFICATE_ID) == 0 || + PORT_Strcmp(typeTag, "ci") == 0) { rv = SECU_PrintSignedContent(outFile, &data, 0, 0, SECU_PrintDumpDerIssuerAndSerial); - } else if (PORT_Strcmp(typeTag, SEC_CT_CERTIFICATE_REQUEST) == 0) { + } else if (PORT_Strcmp(typeTag, SEC_CT_CERTIFICATE_REQUEST) == 0 || + PORT_Strcmp(typeTag, "cr") == 0) { rv = SECU_PrintSignedData(outFile, &data, "Certificate Request", 0, SECU_PrintCertificateRequest); - } else if (PORT_Strcmp (typeTag, SEC_CT_CRL) == 0) { + } else if (PORT_Strcmp(typeTag, SEC_CT_CRL) == 0) { rv = SECU_PrintSignedData (outFile, &data, "CRL", 0, SECU_PrintCrl); #ifdef HAVE_EPV_TEMPLATE - } else if (PORT_Strcmp(typeTag, SEC_CT_PRIVATE_KEY) == 0) { + } else if (PORT_Strcmp(typeTag, SEC_CT_PRIVATE_KEY) == 0 || + PORT_Strcmp(typeTag, "sk") == 0) { rv = SECU_PrintPrivateKey(outFile, &data, "Private Key", 0); #endif - } else if (PORT_Strcmp(typeTag, SEC_CT_PUBLIC_KEY) == 0) { + } else if (PORT_Strcmp(typeTag, SEC_CT_PUBLIC_KEY) == 0 || + PORT_Strcmp (typeTag, "pk") == 0) { rv = SECU_PrintSubjectPublicKeyInfo(outFile, &data, "Public Key", 0); - } else if (PORT_Strcmp(typeTag, SEC_CT_PKCS7) == 0) { + } else if (PORT_Strcmp(typeTag, SEC_CT_PKCS7) == 0 || + PORT_Strcmp (typeTag, "p7") == 0) { rv = SECU_PrintPKCS7ContentInfo(outFile, &data, "PKCS #7 Content Info", 0); - } else if (PORT_Strcmp(typeTag, SEC_CT_NAME) == 0) { + } else if (PORT_Strcmp(typeTag, SEC_CT_NAME) == 0 || + PORT_Strcmp (typeTag, "n") == 0) { rv = SECU_PrintDERName(outFile, &data, "Name", 0); } else { fprintf(stderr, "%s: don't know how to print out '%s' files\n", diff --git a/lib/certdb/alg1485.c b/lib/certdb/alg1485.c index edb95af487..ea1621bcf6 100644 --- a/lib/certdb/alg1485.c +++ b/lib/certdb/alg1485.c @@ -28,12 +28,12 @@ static const NameToKind name2kinds[] = { * (See: http://www.iana.org/assignments/ldap-parameters) */ /* RFC 3280, 4630 MUST SUPPORT */ - { "CN", 64, SEC_OID_AVA_COMMON_NAME, SEC_ASN1_DS}, + { "CN", 640, SEC_OID_AVA_COMMON_NAME, SEC_ASN1_DS}, { "ST", 128, SEC_OID_AVA_STATE_OR_PROVINCE, SEC_ASN1_DS}, - { "O", 64, SEC_OID_AVA_ORGANIZATION_NAME, + { "O", 128, SEC_OID_AVA_ORGANIZATION_NAME, SEC_ASN1_DS}, - { "OU", 64, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME, + { "OU", 128, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME, SEC_ASN1_DS}, { "dnQualifier", 32767, SEC_OID_AVA_DN_QUALIFIER, SEC_ASN1_PRINTABLE_STRING}, { "C", 2, SEC_OID_AVA_COUNTRY_NAME, SEC_ASN1_PRINTABLE_STRING}, @@ -377,7 +377,7 @@ ParseRFC1485AVA(PLArenaPool *arena, const char **pbp, const char *endptr) char sep = 0; char tagBuf[32]; - char valBuf[384]; + char valBuf[1024]; PORT_Assert(arena); if (SECSuccess != scanTag(pbp, endptr, tagBuf, sizeof tagBuf) || @@ -889,7 +889,7 @@ get_hex_string(SECItem *data) static SECStatus AppendAVA(stringBuf *bufp, CERTAVA *ava, CertStrictnessLevel strict) { -#define TMPBUF_LEN 384 +#define TMPBUF_LEN 2048 const NameToKind *pn2k = name2kinds; SECItem *avaValue = NULL; char *unknownTag = NULL; diff --git a/lib/certdb/genname.c b/lib/certdb/genname.c index de9e1f8747..412a9d2c6b 100644 --- a/lib/certdb/genname.c +++ b/lib/certdb/genname.c @@ -137,6 +137,39 @@ const SEC_ASN1Template CERT_GeneralNamesTemplate[] = { }; +static struct { + CERTGeneralNameType type; + char *name; +} typesArray[] = { + { certOtherName, "other" }, + { certRFC822Name, "email" }, + { certRFC822Name, "rfc822" }, + { certDNSName, "dns" }, + { certX400Address, "x400" }, + { certX400Address, "x400addr" }, + { certDirectoryName, "directory" }, + { certDirectoryName, "dn" }, + { certEDIPartyName, "edi" }, + { certEDIPartyName, "ediparty" }, + { certURI, "uri" }, + { certIPAddress, "ip" }, + { certIPAddress, "ipaddr" }, + { certRegisterID, "registerid" } +}; + +CERTGeneralNameType +CERT_GetGeneralNameTypeFromString(const char *string) +{ + int types_count = sizeof(typesArray)/sizeof(typesArray[0]); + int i; + + for (i=0; i < types_count; i++) { + if (PORT_Strcasecmp(string, typesArray[i].name) == 0) { + return typesArray[i].type; + } + } + return 0; +} CERTGeneralName * CERT_NewGeneralName(PLArenaPool *arena, CERTGeneralNameType type) diff --git a/lib/certdb/genname.h b/lib/certdb/genname.h index 091c82c127..1d94376d05 100644 --- a/lib/certdb/genname.h +++ b/lib/certdb/genname.h @@ -26,6 +26,9 @@ cert_DecodeGeneralNames(PLArenaPool *arena, SECItem **encodedGenName); extern SECStatus cert_DestroyGeneralNames(CERTGeneralName *name); +extern CERTGeneralNameType +CERT_GetGeneralNameTypeFromString(const char *string); + extern SECStatus cert_EncodeNameConstraints(CERTNameConstraints *constraints, PLArenaPool *arena, SECItem *dest); diff --git a/lib/nss/nss.def b/lib/nss/nss.def index cd1308f09e..6f6b670811 100644 --- a/lib/nss/nss.def +++ b/lib/nss/nss.def @@ -1055,6 +1055,8 @@ SECMOD_InternaltoPubMechFlags; ;+}; ;+NSS_3.16.2 { # NSS 3.16.2 release ;+ global: +CERT_AddExtensionByOID; +CERT_GetGeneralNameTypeFromString; PK11_PubEncrypt; PK11_PrivDecrypt; ;+ local: diff --git a/lib/pk11wrap/pk11load.c b/lib/pk11wrap/pk11load.c index e1e764b163..6700180ad1 100644 --- a/lib/pk11wrap/pk11load.c +++ b/lib/pk11wrap/pk11load.c @@ -55,6 +55,11 @@ static const CK_C_INITIALIZE_ARGS secmodLockFunctions = { CKF_OS_LOCKING_OK ,NULL }; +static const CK_C_INITIALIZE_ARGS secmodNoLockArgs = { + NULL, NULL, NULL, NULL, + CKF_LIBRARY_CANT_CREATE_OS_THREADS + ,NULL +}; static PRBool loadSingleThreadedModules = PR_TRUE; static PRBool enforceAlreadyInitializedError = PR_TRUE; @@ -209,12 +214,18 @@ secmod_ModuleInit(SECMODModule *mod, SECMODModule **reload, return SECFailure; } - if (mod->isThreadSafe == PR_FALSE) { - pInitArgs = NULL; - } else if (mod->libraryParams == NULL) { - pInitArgs = (void *) &secmodLockFunctions; + if (mod->libraryParams == NULL) { + if (mod->isThreadSafe) { + pInitArgs = (void *) &secmodLockFunctions; + } else { + pInitArgs = NULL; + } } else { - moduleArgs = secmodLockFunctions; + if (mod->isThreadSafe) { + moduleArgs = secmodLockFunctions; + } else { + moduleArgs = secmodNoLockArgs; + } moduleArgs.LibraryParameters = (void *) mod->libraryParams; pInitArgs = &moduleArgs; } @@ -251,18 +262,30 @@ secmod_ModuleInit(SECMODModule *mod, SECMODModule **reload, } } if (crv != CKR_OK) { - if (pInitArgs == NULL || + if (!mod->isThreadSafe || crv == CKR_NETSCAPE_CERTDB_FAILED || crv == CKR_NETSCAPE_KEYDB_FAILED) { PORT_SetError(PK11_MapError(crv)); return SECFailure; } + /* If we had attempted to init a single threaded module "with" + * parameters and it failed, should we retry "without" parameters? + * (currently we don't retry in this scenario) */ + if (!loadSingleThreadedModules) { PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11); return SECFailure; } + /* If we arrive here, the module failed a ThreadSafe init. */ mod->isThreadSafe = PR_FALSE; - crv = PK11_GETTAB(mod)->C_Initialize(NULL); + if (!mod->libraryParams) { + pInitArgs = NULL; + } else { + moduleArgs = secmodNoLockArgs; + moduleArgs.LibraryParameters = (void *) mod->libraryParams; + pInitArgs = &moduleArgs; + } + crv = PK11_GETTAB(mod)->C_Initialize(pInitArgs); if ((CKR_CRYPTOKI_ALREADY_INITIALIZED == crv) && (!enforceAlreadyInitializedError)) { *alreadyLoaded = PR_TRUE; diff --git a/lib/util/oidstring.c b/lib/util/oidstring.c index 8bb963e083..74b2dce4c4 100644 --- a/lib/util/oidstring.c +++ b/lib/util/oidstring.c @@ -6,6 +6,7 @@ #include "secitem.h" #include "secport.h" #include "secerr.h" +#include "secoid.h" /* if to->data is not NULL, and to->len is large enough to hold the result, * then the resultant OID will be copyed into to->data, and to->len will be @@ -112,3 +113,33 @@ SEC_StringToOID(PLArenaPool *pool, SECItem *to, const char *from, PRUint32 len) } return rv; } + +SECStatus +SEC_NumberOrNameStringToOIDTag(PLArenaPool *arena, SECOidTag *to, const char *from) +{ + SECStatus rv; + SECOidTag tag; + SECOidData *coid; + + /* try dotted form first */ + rv = SEC_StringToOID(arena, to, from, strlen(from)); + if (rv == SECSuccess) { + return rv; + } + + /* Check to see if it matches a name in our oid table. + * SECOID_FindOIDByTag returns NULL if tag is out of bounds. + */ + tag = SEC_OID_UNKNOWN; + coid = SECOID_FindOIDByTag_Util(tag); + for ( ; coid; coid = SECOID_FindOIDByTag(++tag)) { + if (PORT_Strcasecmp(from, coid->desc) == 0) { + break; + } + } + if (coid == NULL) { + /* none found */ + return SECFailure; + } + return SECITEM_CopyItem(arena, to, &coid->oid); +} diff --git a/tests/cert/cert.sh b/tests/cert/cert.sh index 313c663f6a..1a23c19c4a 100755 --- a/tests/cert/cert.sh +++ b/tests/cert/cert.sh @@ -1176,6 +1176,201 @@ cert_extensions() done < ${QADIR}/cert/certext.txt } +cert_make_with_param() +{ + DIRPASS="$1" + CERTNAME="$2" + MAKE="$3" + SUBJ="$4" + EXTRA="$5" + EXPECT="$6" + TESTNAME="$7" + + echo certutil ${DIRPASS} -s "${SUBJ}" ${MAKE} ${CERTNAME} ${EXTRA} + ${BINDIR}/certutil ${DIRPASS} -s "${SUBJ}" ${MAKE} ${CERTNAME} ${EXTRA} + + RET=$? + if [ "${RET}" -ne "${EXPECT}" ]; then + # if we expected failure to create, then delete unexpected certificate + if [ "${EXPECT}" -ne 0 ]; then + ${BINDIR}/certutil ${DIRPASS} -D ${CERTNAME} + fi + + CERTFAILED=1 + html_failed "${TESTNAME} (${COUNT}) - ${EXTRA}" + cert_log "ERROR: ${TESTNAME} - ${EXTRA} failed" + return 1 + fi + + html_passed "${TESTNAME} (${COUNT})" + return 0 +} + +cert_list_and_count_dns() +{ + DIRPASS="$1" + CERTNAME="$2" + EXPECT="$3" + EXPECTCOUNT="$4" + TESTNAME="$5" + + echo certutil ${DIRPASS} -L ${CERTNAME} + ${BINDIR}/certutil ${DIRPASS} -L ${CERTNAME} + + RET=$? + if [ "${RET}" -ne "${EXPECT}" ]; then + CERTFAILED=1 + html_failed "${TESTNAME} (${COUNT}) - list and count" + cert_log "ERROR: ${TESTNAME} - list and count failed" + return 1 + fi + + LISTCOUNT=`${BINDIR}/certutil ${DIRPASS} -L ${CERTNAME} | grep -wc DNS` + if [ "${LISTCOUNT}" -ne "${EXPECTCOUNT}" ]; then + CERTFAILED=1 + html_failed "${TESTNAME} (${COUNT}) - list and count" + cert_log "ERROR: ${TESTNAME} - list and count failed" + return 1 + fi + + html_passed "${TESTNAME} (${COUNT})" + return 0 +} + +cert_dump_ext_to_file() +{ + DIRPASS="$1" + CERTNAME="$2" + OID="$3" + OUTFILE="$4" + EXPECT="$5" + TESTNAME="$6" + + echo certutil ${DIRPASS} -L ${CERTNAME} --dump-ext-val ${OID} + echo "writing output to ${OUTFILE}" + ${BINDIR}/certutil ${DIRPASS} -L ${CERTNAME} --dump-ext-val ${OID} > ${OUTFILE} + + RET=$? + if [ "${RET}" -ne "${EXPECT}" ]; then + CERTFAILED=1 + html_failed "${TESTNAME} (${COUNT}) - dump to file" + cert_log "ERROR: ${TESTNAME} - dump to file failed" + return 1 + fi + + html_passed "${TESTNAME} (${COUNT})" + return 0 +} + +cert_delete() +{ + DIRPASS="$1" + CERTNAME="$2" + EXPECT="$3" + TESTNAME="$4" + + echo certutil ${DIRPASS} -D ${CERTNAME} + ${BINDIR}/certutil ${DIRPASS} -D ${CERTNAME} + + RET=$? + if [ "${RET}" -ne "${EXPECT}" ]; then + CERTFAILED=1 + html_failed "${TESTNAME} (${COUNT}) - delete cert" + cert_log "ERROR: ${TESTNAME} - delete cert failed" + return 1 + fi + + html_passed "${TESTNAME} (${COUNT})" + return 0 +} + +cert_inc_count() +{ + COUNT=`expr ${COUNT} + 1` +} + +############################## cert_crl_ssl ############################ +# test adding subject-alt-name, dumping, and adding generic extension +######################################################################## +cert_san_and_generic_extensions() +{ + EXTDUMP=${CERT_EXTENSIONS_DIR}/sanext.der + + DIR="-d ${CERT_EXTENSIONS_DIR} -f ${R_PWFILE}" + CERTNAME="-n WithSAN" + MAKE="-S -t ,, -x -z ${R_NOISE_FILE}" + SUBJ="CN=example.com" + + TESTNAME="san-and-generic-extensions" + + cert_inc_count + cert_make_with_param "${DIR}" "${CERTNAME}" "${MAKE}" "${SUBJ}" \ + "--extSAN example.com" 255 \ + "create cert with invalid SAN parameter" + + cert_inc_count + cert_make_with_param "${DIR}" "${CERTNAME}" "${MAKE}" "${SUBJ}" \ + "--extSAN example.com,dns:www.example.com" 255 \ + "create cert with invalid SAN parameter" + + TN="create cert with valid SAN parameter" + + cert_inc_count + cert_make_with_param "${DIR}" "${CERTNAME}" "${MAKE}" "${SUBJ}" \ + "--extSAN dns:example.com,dns:www.example.com" 0 \ + "${TN}" + + cert_inc_count + cert_list_and_count_dns "${DIR}" "${CERTNAME}" 0 2 \ + "${TN}" + + cert_inc_count + cert_dump_ext_to_file "${DIR}" "${CERTNAME}" "2.5.29.17" "${EXTDUMP}" 0 \ + "dump extension 2.5.29.17 to file ${EXTDUMP}" + + cert_inc_count + cert_delete "${DIR}" "${CERTNAME}" 0 \ + "${TN}" + + cert_inc_count + cert_list_and_count_dns "${DIR}" "${CERTNAME}" 255 0 \ + "expect failure to list cert, because we deleted it" + + cert_inc_count + cert_make_with_param "${DIR}" "${CERTNAME}" "${MAKE}" "${SUBJ}" \ + "--extGeneric ${EXTDUMP}" 255 \ + "create cert with invalid generic ext parameter" + + cert_inc_count + cert_make_with_param "${DIR}" "${CERTNAME}" "${MAKE}" "${SUBJ}" \ + "--extGeneric not-critical:${EXTDUMP}" 255 \ + "create cert with invalid generic ext parameter" + + cert_inc_count + cert_make_with_param "${DIR}" "${CERTNAME}" "${MAKE}" "${SUBJ}" \ + "--extGeneric not-critical:${EXTDUMP},2.5.29.17:critical:${EXTDUMP}" 255 \ + "create cert with invalid generic ext parameter" + + TN="create cert with valid generic ext parameter" + + cert_inc_count + cert_make_with_param "${DIR}" "${CERTNAME}" "${MAKE}" "${SUBJ}" \ + "--extGeneric 2.5.29.17:not-critical:${EXTDUMP}" 0 \ + "${TN}" + + cert_inc_count + cert_list_and_count_dns "${DIR}" "${CERTNAME}" 0 2 \ + "${TN}" + + cert_inc_count + cert_delete "${DIR}" "${CERTNAME}" 0 \ + "${TN}" + + cert_inc_count + cert_list_and_count_dns "${DIR}" "${CERTNAME}" 255 0 \ + "expect failure to list cert, because we deleted it" +} + ############################## cert_crl_ssl ############################ # local shell function to generate certs and crls for SSL tests ######################################################################## @@ -1513,6 +1708,7 @@ if [ -z "$NSS_TEST_DISABLE_FIPS" ]; then fi cert_eccurves cert_extensions +cert_san_and_generic_extensions cert_test_password cert_test_distrust cert_test_ocspresp