Bug 970539, NSS tool improvements/fixes: certutil/btoa/pp/httpserv, r=rrelyea
authorKai Engert <kaie@kuix.de>
Thu, 29 May 2014 21:42:08 +0200
changeset 11161 04c846cc7a85936d10f7767c81c084034473331a
parent 11160 33c165684527a4e765e364f7a9f28362e9a4c97d
child 11162 1f94c145c50bedcf93df28269141d20dc4603a01
push id403
push userkaie@kuix.de
push dateThu, 29 May 2014 19:44:25 +0000
reviewersrrelyea
bugs970539
Bug 970539, NSS tool improvements/fixes: certutil/btoa/pp/httpserv, r=rrelyea
cmd/btoa/btoa.c
cmd/certutil/certext.c
cmd/certutil/certutil.c
cmd/certutil/certutil.h
cmd/httpserv/httpserv.c
cmd/lib/secutil.c
cmd/lib/secutil.h
cmd/pp/pp.c
lib/certdb/alg1485.c
lib/certdb/genname.c
lib/certdb/genname.h
lib/nss/nss.def
lib/pk11wrap/pk11load.c
lib/util/oidstring.c
tests/cert/cert.sh
--- a/cmd/btoa/btoa.c
+++ b/cmd/btoa/btoa.c
@@ -87,36 +87,41 @@ static void Usage(char *progName)
 {
     fprintf(stderr,
 	    "Usage: %s [-i input] [-o output]\n",
 	    progName);
     fprintf(stderr, "%-20s 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",
 	    "-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);
 }
 
 int main(int argc, char **argv)
 {
     char *progName;
     SECStatus rv;
     FILE *inFile, *outFile;
     PLOptState *optstate;
     PLOptStatus status;
+    char *suffix = NULL;
 
     inFile = 0;
     outFile = 0;
     progName = strrchr(argv[0], '/');
     if (!progName)
 	progName = strrchr(argv[0], '\\');
     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:
 	    Usage(progName);
 	    break;
 
 	  case 'i':
 	    inFile = fopen(optstate->value, "rb");
@@ -130,16 +135,23 @@ int main(int argc, char **argv)
 	  case 'o':
 	    outFile = fopen(optstate->value, "wb");
 	    if (!outFile) {
 		fprintf(stderr, "%s: unable to open \"%s\" for writing\n",
 			progName, optstate->value);
 		return -1;
 	    }
 	    break;
+	
+	  case 'w':
+	    if (!strcmp(optstate->value, "c"))
+		suffix = strdup("CERTIFICATE");
+	    else
+		suffix = strdup(optstate->value);
+	    break;
 	}
     }
     if (status == PL_OPT_BAD)
 	Usage(progName);
     if (!inFile) {
 #if defined(WIN32)
 	/* If we're going to read binary data from stdin, we must put stdin
 	** into O_BINARY mode or else incoming \r\n's will become \n's.
@@ -166,16 +178,22 @@ int main(int argc, char **argv)
 	    fprintf(stderr,
 	    "%s: Cannot change stdout to binary mode. Use -o option instead.\n",
 	            progName);
 	    return smrv;
 	}
 #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;
 }
--- a/cmd/certutil/certext.c
+++ b/cmd/certutil/certext.c
@@ -22,16 +22,18 @@
 #if defined(XP_UNIX)
 #include <unistd.h>
 #endif
 
 #include "cert.h"
 #include "xconst.h"
 #include "prprf.h"
 #include "certutil.h"
+#include "genname.h"
+#include "prnetdb.h"
 
 #define GEN_BREAK(e) rv=e; break;
 
 static char *
 Gets_s(char *buff, size_t size) {
     char *str;
     
     if (buff == NULL || size < 1) {
@@ -660,63 +662,223 @@ AddNscpCertType (void *extHandle, const 
     bitStringValue.len = 1;
 
     return (CERT_EncodeAndAddBitStrExtension
             (extHandle, SEC_OID_NS_CERT_EXT_CERT_TYPE, &bitStringValue,
              isCriticalExt));
 
 }
 
+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,&current->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, &current->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, &current->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) {
         if (*existingListp != NULL) {
             PRCList *existingprev;
             /* add nameList to the end of the existing list */
             existingprev = (*existingListp)->l.prev;
             (*existingListp)->l.prev = &(current->l);
@@ -744,16 +906,22 @@ AddEmailSubjectAlt(PLArenaPool *arena, C
 
 static SECStatus 
 AddDNSSubjectAlt(PLArenaPool *arena, CERTGeneralName **existingListp,
                  const char *dnsNames)
 {
     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)
 {
     CERTBasicConstraints basicConstraint;    
     SECStatus rv;
     char buffer[10];
     PRBool yesNoAns;
@@ -1741,22 +1909,83 @@ AddInfoAccess(void *extHandle, PRBool ad
 		 yesNoAns, oidIdent,
 		 (EXTEN_EXT_VALUE_ENCODER)CERT_EncodeInfoAccessExtension);
     }
     if (arena)
         PORT_FreeArena(arena, PR_FALSE);
     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 */
         if (extList[ext_keyUsage].activated) {
             rv = AddKeyUsage(extHandle, extList[ext_keyUsage].arg);
             if (rv) {
 		errstring = "KeyUsage";
                 break;
@@ -1859,30 +2088,41 @@ AddExtensions(void *extHandle, const cha
         if (extList[ext_inhibitAnyPolicy].activated) {
             rv = AddInhibitAnyPolicy(extHandle);
             if (rv) {
 		errstring = "InhibitAnyPolicy";
                 break;
 	    }
         }
 
-        if (emailAddrs || dnsNames) {
+        if (emailAddrs || dnsNames || extList[ext_subjectAltName].activated) {
             PLArenaPool *arena;
             CERTGeneralName *namelist = NULL;
             SECItem item = { 0, NULL, 0 };
             
             arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
             if (arena == NULL) {
                 rv = SECFailure;
                 break;
             }
+            
+            rv = SECSuccess;
 
-            rv = AddEmailSubjectAlt(arena, &namelist, emailAddrs);
+            if (emailAddrs) {
+                rv |= AddEmailSubjectAlt(arena, &namelist, emailAddrs);
+            }
 
-            rv |= AddDNSSubjectAlt(arena, &namelist, dnsNames);
+            if (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);
 	        if (rv == SECSuccess) {
                     rv = CERT_AddExtension(extHandle,
                                           SEC_OID_X509_SUBJECT_ALT_NAME,
                                           &item, PR_FALSE, PR_TRUE);
 		}
@@ -1893,10 +2133,76 @@ AddExtensions(void *extHandle, const cha
                 break;
 	    }
         }
     } while (0);
     
     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;
 }
--- a/cmd/certutil/certutil.c
+++ b/cmd/certutil/certutil.c
@@ -177,17 +177,17 @@ AddCert(PK11SlotInfo *slot, CERTCertDBHa
 
     return rv;
 }
 
 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;
     CERTCertificateRequest *cr;
     SECItem *encoding;
     SECOidTag signAlgTag;
     SECStatus rv;
     PLArenaPool *arena;
@@ -215,17 +215,17 @@ CertReq(SECKEYPrivateKey *privk, SECKEYP
 	return SECFailure;
     }
     
     extHandle = CERT_StartCertificateRequestAttributes(cr);
     if (extHandle == NULL) {
         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;
     }
     CERT_FinishExtensions(extHandle);
     CERT_FinishCertificateRequestAttributes(cr);
 
     /* Der encode the request */
@@ -415,22 +415,75 @@ DumpChain(CERTCertDBHandle *handle, char
 	printf("\"%s\" [%s]\n\n", c->nickname, c->subjectName);
 	CERT_DestroyCertificate(c);
     }
     CERT_DestroyCertificateList(chain);
     return SECSuccess;
 }
 
 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;
 
     /* List certs on a non-internal slot. */
     if (!PK11_IsFriendly(slot) && PK11_NeedLogin(slot)) {
         SECStatus newrv = PK11_Authenticate(slot, PR_TRUE, pwarg);
         if (newrv != SECSuccess) {
             SECU_PrintError(progName, "could not authenticate to token %s.",
@@ -456,75 +509,34 @@ listCerts(CERTCertDBHandle *handle, char
 	CERT_DestroyCertificate(the_cert);
 	if (!certs) {
 	    PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
 	    SECU_PrintError(progName, "problem printing certificates");
 	    return SECFailure;
 	}
 	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, 
 			"Could not find certificates for email address: %s\n", 
 			email);
 	    return SECFailure;
 	}
 	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;
 	    }
 	}
     } else {
 	certs = PK11_ListCertsInSlot(slot);
 	if (certs) {
 	    for (node = CERT_LIST_HEAD(certs); !CERT_LIST_END(node,certs);
@@ -542,18 +554,19 @@ listCerts(CERTCertDBHandle *handle, char
 	return SECFailure;
     }
 
     return SECSuccess;	/* not rv ?? */
 }
 
 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;
 
     if (!ascii && !raw && !nickname && !email) {
         PR_fprintf(outfile, "\n%-60s %-5s\n%-60s %-5s\n\n",
                    "Certificate Nickname", "Trust Attributes", "",
                    "SSL,S/MIME,JAR/XPI");
     }
@@ -564,17 +577,18 @@ ListCerts(CERTCertDBHandle *handle, char
 	list = PK11_ListCerts(PK11CertListAll, pwdata);
 	for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list);
 	     node = CERT_LIST_NEXT(node)) {
 	    SECU_PrintCertNickname(node, stdout);
 	}
 	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;
 }
 
 static SECStatus 
 DeleteCert(CERTCertDBHandle *handle, char *name)
 {
     SECStatus rv;
     CERTCertificate *cert;
@@ -610,16 +624,25 @@ ValidateCert(CERTCertDBHandle *handle, c
 	    PORT_SetError (SEC_ERROR_INVALID_ARGS);
 	    return (SECFailure);
     }
     
     switch (*certUsage) {
 	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;
 	case 'V':
 	    usage = certificateUsageSSLServer;
 	    break;
 	case 'S':
 	    usage = certificateUsageEmailSigner;
@@ -984,17 +1007,17 @@ PrintSyntax(char *progName)
     FPS "\t\t [-P targetDBPrefix] [--source-prefix upgradeDBPrefix]\n");
     FPS "\t\t [-f targetPWfile] [-@ upgradePWFile]\n");
     FPS "\t%s --merge --source-dir sourceDBDir [-d targetDBdir]\n",
 	progName);
     FPS "\t\t [-P targetDBPrefix] [--source-prefix sourceDBPrefix]\n");
     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);
     FPS "\t%s -R -s subj -o cert-request-file [-d certdir] [-P dbprefix] [-p phone] [-a]\n"
 	"\t\t [-7 emailAddrs] [-k key-type-or-id] [-h token-name] [-f pwfile] [-g key-size]\n",
 	progName);
     FPS "\t%s -V -n cert-name -u usage [-b time] [-e] [-a]\n"
 	"\t\t[-X] [-d certdir] [-P dbprefix]\n",
@@ -1003,17 +1026,18 @@ PrintSyntax(char *progName)
 	progName);
     FPS "\t%s -S -n cert-name -s subj [-c issuer-name | -x]  -t trustargs\n"
 	"\t\t [-k key-type-or-id] [-q key-params] [-h token-name] [-g key-size]\n"
         "\t\t [-m serial-number] [-w warp-months] [-v months-valid]\n"
 	"\t\t [-f pwfile] [-d certdir] [-P dbprefix]\n"
         "\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);
 }
 
 enum usage_level {
     usage_all = 0, usage_selected = 1
 };
 
@@ -1303,17 +1327,17 @@ static void luK(enum usage_level ul, con
         "   -X");
     FPS "\n");
 }
 
 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;
     FPS "%-20s Pretty print named cert (list all if unspecified)\n",
         "   -n cert-name");
     FPS "%-20s \n"
               "%-20s Pretty print cert with email address (list all if unspecified)\n",
         "   --email email-address", "");
@@ -1322,16 +1346,19 @@ static void luL(enum usage_level ul, con
     FPS "%-20s Cert & Key database prefix\n",
         "   -P dbprefix");
     FPS "%-20s force the database to open R/W\n",
         "   -X");
     FPS "%-20s For single cert, print binary DER encoding\n",
         "   -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");
 }
 
 static void luM(enum usage_level ul, const char *command)
 {
     int is_my_command = (command && 0 == strcmp(command, "M"));
     if (ul == usage_all || !command || is_my_command)
     FPS "%-15s Modify trust attributes of certificate\n",
@@ -1467,16 +1494,19 @@ static void luV(enum usage_level ul, con
         "   -n cert-name");
     FPS "%-20s validity time (\"YYMMDDHHMMSS[+HHMM|-HHMM|Z]\")\n",
         "   -b time");
     FPS "%-20s Check certificate signature \n",
         "   -e ");   
     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", "");   
     FPS "%-25s J \t Object signer\n", "");   
     FPS "%-20s Cert database directory (default is ~/.netscape)\n",
         "   -d certdir");
     FPS "%-20s Input the certificate in ASCII (RFC1113); default is binary\n",
         "   -a");
@@ -1633,16 +1663,28 @@ static void luS(enum usage_level ul, con
     FPS "%-20s Create an Inhibit Any Policy extension\n",
         "   --extIA ");
     FPS "%-20s Create a subject key ID extension\n",
         "   --extSKID ");
     FPS "%-20s \n",
         "   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");
 }
 
 static void LongUsage(char *progName, enum usage_level ul, const char *command)
 {
     luA(ul, command);
     luB(ul, command);
     luE(ul, command);
@@ -1831,16 +1873,17 @@ CreateCert(
 	unsigned int serialNumber, 
 	int     warpmonths,
 	int     validityMonths,
 	const char *emailAddrs,
 	const char *dnsNames,
 	PRBool ascii,
 	PRBool  selfsign,
 	certutilExtnList extnList,
+	const char *extGeneric,
         int certVersion,
 	SECItem * certDER)
 {
     void *	extHandle;
     CERTCertificate *subjectCert 	= NULL;
     CERTCertificateRequest *certReq	= NULL;
     SECStatus 	rv 			= SECSuccess;
     CERTCertExtension **CRexts;
@@ -1859,17 +1902,17 @@ CreateCert(
 	}
         
         
 	extHandle = CERT_StartCertExtensions (subjectCert);
 	if (extHandle == NULL) {
 	    GEN_BREAK (SECFailure)
 	}
         
-        rv = AddExtensions(extHandle, emailAddrs, dnsNames, extnList);
+        rv = AddExtensions(extHandle, emailAddrs, dnsNames, extnList, extGeneric);
         if (rv != SECSuccess) {
 	    GEN_BREAK (SECFailure)
 	}
         
         if (certReq->attributes != NULL &&
 	    certReq->attributes[0] != NULL &&
 	    certReq->attributes[0]->attrType.data != NULL &&
 	    certReq->attributes[0]->attrType.len   > 0    &&
@@ -2207,16 +2250,19 @@ enum certutilOpts {
     opt_SourcePrefix,
     opt_UpgradeID,
     opt_UpgradeTokenName,
     opt_KeyOpFlagsOn,
     opt_KeyOpFlagsOff,
     opt_KeyAttrFlags,
     opt_EmptyPassword,
     opt_CertVersion,
+    opt_AddSubjectAltNameExt,
+    opt_DumpExtensionValue,
+    opt_GenericExtensions,
     opt_Help
 };
 
 static const
 secuCommandFlag commands_init[] =
 {
 	{ /* cmd_AddCert             */  'A', PR_FALSE, 0, PR_FALSE },
 	{ /* cmd_CreateNewCert       */  'C', PR_FALSE, 0, PR_FALSE },
@@ -2318,16 +2364,21 @@ secuCommandFlag options_init[] =
 	{ /* opt_KeyOpFlagsOff       */  0,   PR_TRUE, 0, PR_FALSE, 
                                                    "keyOpFlagsOff"},
 	{ /* opt_KeyAttrFlags        */  0,   PR_TRUE, 0, PR_FALSE, 
                                                    "keyAttrFlags"},
 	{ /* opt_EmptyPassword       */  0,   PR_FALSE, 0, PR_FALSE, 
                                                    "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]))
 
 static secuCommandFlag certutil_commands[NUM_COMMANDS];
 static secuCommandFlag certutil_options [NUM_OPTIONS ];
 
 static const secuCommand certutil = {
     NUM_COMMANDS, 
@@ -2658,19 +2709,20 @@ certutil_main(int argc, char **argv, PRB
          certutil.commands[cmd_CreateAndAddCert].activated) &&
         !certutil.options[opt_Trust].activated) {
 	PR_fprintf(PR_STDERR, 
 	          "%s -%c: trust is required for this command (-t).\n",
 	           progName, commandToRun);
 	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, 
 	        "%s: nickname is required to dump cert in raw or ascii mode.\n",
 	           progName);
 	return 255;
     }
     
@@ -2980,20 +3032,39 @@ merge_fail:
 	PK11_FreeSlot(sourceSlot);
 	goto shutdown;
     }
 
     /* The following 8 options are mutually exclusive with all others. */
 
     /*  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) {
 	rv = DumpChain(certHandle, name,
                        certutil.options[opt_ASCIIForIO].activated);
 	goto shutdown;
     }
     /*  XXX needs work  */
@@ -3174,16 +3245,22 @@ merge_fail:
             certutil.options[opt_AddCmdExtKeyUsageExt].activated;
         if (!certutil_extns[ext_extKeyUsage].activated) {
             certutil_extns[ext_extKeyUsage].activated =
                 certutil.options[opt_AddExtKeyUsageExt].activated;
         } else {
             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;
         certutil_extns[ext_subjInfoAcc].activated =
 				certutil.options[opt_AddSubjInfoAccExt].activated;
         certutil_extns[ext_certPolicies].activated =
 				certutil.options[opt_AddCertPoliciesExt].activated;
         certutil_extns[ext_policyMappings].activated =
@@ -3213,16 +3290,18 @@ merge_fail:
     /*  Make a cert request (-R).  */
     if (certutil.commands[cmd_CertReq].activated) {
 	rv = CertReq(privkey, pubkey, keytype, hashAlgTag, subject,
 	             certutil.options[opt_PhoneNumber].arg,
 	             certutil.options[opt_ASCIIForIO].activated,
 		     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;
 	privkey->wincx = &pwdata;
     }
 
     /*
      *  Certificate creation
@@ -3235,16 +3314,18 @@ merge_fail:
     if (certutil.commands[cmd_CreateAndAddCert].activated) {
 	static certutilExtnList nullextnlist = {{PR_FALSE, NULL}};
 	rv = CertReq(privkey, pubkey, keytype, hashAlgTag, subject,
 	             certutil.options[opt_PhoneNumber].arg,
 		     PR_FALSE, /* do not BASE64-encode regardless of -a option */
 		     NULL,
 		     NULL,
                      nullextnlist,
+		     (certutil.options[opt_GenericExtensions].activated ?
+		         certutil.options[opt_GenericExtensions].arg : NULL),
 		     &certReqDER);
 	if (rv) 
 	    goto shutdown;
 	privkey->wincx = &pwdata;
     }
 
     /*  Create a certificate (-C or -S).  */
     if (certutil.commands[cmd_CreateAndAddCert].activated ||
@@ -3254,16 +3335,18 @@ merge_fail:
 			&certReqDER, &privkey, &pwdata, hashAlgTag,
 	                serialNumber, warpmonths, validityMonths,
 		        certutil.options[opt_ExtendedEmailAddrs].arg,
 		        certutil.options[opt_ExtendedDNSNames].arg,
 		        certutil.options[opt_ASCIIForIO].activated &&
 			    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) 
 	    goto shutdown;
     }
 
     /* 
      * Adding a cert to the database (or slot)
--- a/cmd/certutil/certutil.h
+++ b/cmd/certutil/certutil.h
@@ -30,24 +30,29 @@ enum certutilExtns {
     ext_authInfoAcc,
     ext_subjInfoAcc,
     ext_certPolicies,
     ext_policyMappings,
     ext_policyConstr,
     ext_inhibitAnyPolicy,
     ext_subjectKeyID,
     ext_nameConstraints,
+    ext_subjectAltName,
     ext_End
 };
 
 typedef struct ExtensionEntryStr {
     PRBool activated;
     const char  *arg;
 } ExtensionEntry;
 
 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 */
 
--- a/cmd/httpserv/httpserv.c
+++ b/cmd/httpserv/httpserv.c
@@ -1307,18 +1307,20 @@ main(int argc, char **argv)
 	      if (!revoInfo->cert) {
 		  fprintf(stderr, "cannot find cert with nickname %s\n",
 			  revoInfo->nickname);
 		  exit(1);
 	      }
 	      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);
 	      }
 	      revoInfo->crl = 
 		  CERT_DecodeDERCrlWithFlags(NULL, &crlDER, SEC_CRL_TYPE,
 					     CRL_DECODE_DEFAULT_OPTIONS);
 	      if (!revoInfo->crl) {
--- a/cmd/lib/secutil.c
+++ b/cmd/lib/secutil.c
@@ -47,16 +47,29 @@ static char consoleName[] =  {
 #endif
 #endif
 };
 
 #include "nssutil.h"
 #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)
 {
     if (p) {
 	PORT_Memset(p, 0, PORT_Strlen(p));
 	PORT_Free(p);
     }
@@ -604,22 +617,32 @@ secu_PrintRawStringQuotesOptional(FILE *
 	column = level*INDENT_MULT;
     }
     if (quotes) {
 	fprintf(out, "\""); column++;
     }
 
     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) {
 	fprintf(out, "\""); column++;
     }
     if (SECU_GetWrapEnabled() &&
         (column != level*INDENT_MULT || column > 76)) {
 	SECU_Newline(out);
--- a/cmd/lib/secutil.h
+++ b/cmd/lib/secutil.h
@@ -134,16 +134,19 @@ extern int
 SECU_GetClientAuthData(void *arg, PRFileDesc *fd,
 		       struct CERTDistNamesStr *caNames,
 		       struct CERTCertificateStr **pRetCert,
 		       struct SECKEYPrivateKeyStr **pRetKey);
 
 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
 SECU_printCertProblems(FILE *outfile, CERTCertDBHandle *handle, 
 	CERTCertificate *cert, PRBool checksig, 
 	SECCertificateUsage certUsage, void *pinArg, PRBool verbose);
 
 /* revalidate the cert and print information about cert verification
--- a/cmd/pp/pp.c
+++ b/cmd/pp/pp.c
@@ -17,32 +17,37 @@ extern int fprintf(FILE *, char *, ...);
 
 #include "pk11func.h"
 #include "nspr.h"
 #include "nss.h"
 
 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, "%-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, "%-20s Input is in ascii encoded form (RFC1113)\n",
+    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);
 }
 
 int main(int argc, char **argv)
 {
     int rv, ascii;
     char *progName;
     FILE *outFile;
@@ -54,17 +59,17 @@ int main(int argc, char **argv)
 
     progName = strrchr(argv[0], '/');
     progName = progName ? progName+1 : argv[0];
 
     ascii = 0;
     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 '?':
 	    Usage(progName);
 	    break;
 
 	  case 'a':
 	    ascii = 1;
@@ -87,16 +92,20 @@ int main(int argc, char **argv)
 		return -1;
 	    }
 	    break;
 
 	  case 't':
 	    typeTag = strdup(optstate->value);
 	    break;
 
+	  case 'u':
+	    SECU_EnableUtf8Display(PR_TRUE);
+	    break;
+
 	  case 'w':
 	    wrap = PR_FALSE;
 	    break;
 	}
     }
     PL_DestroyOptState(optstate);
     if (!typeTag) Usage(progName);
 
@@ -120,37 +129,44 @@ int main(int argc, char **argv)
 
     /* Data is untyped, using the specified type */
     data.data = der.data;
     data.len = der.len;
 
     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",
 		progName, typeTag);
 	SECU_PrintAny(outFile, &data, "File contains", 0);
 	return -1;
     }
 
--- a/lib/certdb/alg1485.c
+++ b/lib/certdb/alg1485.c
@@ -23,22 +23,22 @@ typedef struct NameToKindStr {
 #define SEC_ASN1_DS SEC_ASN1_HIGH_TAG_NUMBER
 
 /* Add new entries to this table, and maybe to function ParseRFC1485AVA */
 static const NameToKind name2kinds[] = {
 /* IANA registered type names
  * (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},
     { "serialNumber",   64, SEC_OID_AVA_SERIAL_NUMBER,SEC_ASN1_PRINTABLE_STRING},
 
 /* RFC 3280, 4630 SHOULD SUPPORT */
     { "L",             128, SEC_OID_AVA_LOCALITY,       SEC_ASN1_DS},
     { "title",          64, SEC_OID_AVA_TITLE,          SEC_ASN1_DS},
@@ -372,17 +372,17 @@ ParseRFC1485AVA(PLArenaPool *arena, cons
     int       valLen;
     SECOidTag kind  = SEC_OID_UNKNOWN;
     SECStatus rv    = SECFailure;
     SECItem   derOid = { 0, NULL, 0 };
     SECItem   derVal = { 0, NULL, 0};
     char      sep   = 0;
 
     char tagBuf[32];
-    char valBuf[384];
+    char valBuf[1024];
 
     PORT_Assert(arena);
     if (SECSuccess != scanTag(pbp, endptr, tagBuf, sizeof tagBuf) ||
 	!(valLen    = scanVal(pbp, endptr, valBuf, sizeof valBuf))) {
 	goto loser;
     }
 
     bp = *pbp;
@@ -884,17 +884,17 @@ get_hex_string(SECItem *data)
  *
  * As a simplification, we assume the value is correctly encoded for 
  * its encoding type.  That is, we do not test that all the characters
  * in a string encoded type are allowed by that type.  We assume it.
  */
 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;
     char        *encodedAVA  = NULL;
     PRBool       useHex      = PR_FALSE;  /* use =#hexXXXX form */
     PRBool       truncateName  = PR_FALSE;
     PRBool       truncateValue = PR_FALSE;
     SECOidTag    endKind;
--- a/lib/certdb/genname.c
+++ b/lib/certdb/genname.c
@@ -132,16 +132,49 @@ static const SEC_ASN1Template CERT_Regis
 };
 
 
 const SEC_ASN1Template CERT_GeneralNamesTemplate[] = {
     { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_XTRN , 0, SEC_ASN1_SUB(SEC_AnyTemplate) }
 };
 
 
+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)
 {
     CERTGeneralName *name = arena 
                             ? PORT_ArenaZNew(arena, CERTGeneralName)
 	                    : PORT_ZNew(CERTGeneralName);
     if (name) {
--- a/lib/certdb/genname.h
+++ b/lib/certdb/genname.h
@@ -21,16 +21,19 @@ extern SECItem **
 cert_EncodeGeneralNames(PLArenaPool *arena, CERTGeneralName *names);
 
 extern CERTGeneralName *
 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);
 
 extern CERTNameConstraints *
 cert_DecodeNameConstraints(PLArenaPool *arena, const SECItem *encodedConstraints);
 
 extern CERTGeneralName *
--- a/lib/nss/nss.def
+++ b/lib/nss/nss.def
@@ -1050,13 +1050,15 @@ CERT_GetSubjectPublicKeyDigest;
 PK11_ExportDERPrivateKeyInfo;
 PK11_ExportPrivKeyInfo;
 SECMOD_InternaltoPubMechFlags;
 ;+    local:
 ;+       *;
 ;+};
 ;+NSS_3.16.2 { 	# NSS 3.16.2 release
 ;+    global:
+CERT_AddExtensionByOID;
+CERT_GetGeneralNameTypeFromString;
 PK11_PubEncrypt;
 PK11_PrivDecrypt;
 ;+    local:
 ;+       *;
 ;+};
--- a/lib/pk11wrap/pk11load.c
+++ b/lib/pk11wrap/pk11load.c
@@ -50,16 +50,21 @@ CK_RV PR_CALLBACK secmodUnlockMutext(CK_
 
 static SECMODModuleID  nextModuleID = 1;
 static const CK_C_INITIALIZE_ARGS secmodLockFunctions = {
     secmodCreateMutext, secmodDestroyMutext, secmodLockMutext, 
     secmodUnlockMutext, CKF_LIBRARY_CANT_CREATE_OS_THREADS|
 	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;
 static PRBool finalizeModules = PR_TRUE;
 
 /* set global options for NSS PKCS#11 module loader */
 SECStatus pk11_setGlobalOptions(PRBool noSingleThreadedModules,
                                 PRBool allowAlreadyInitializedModules,
@@ -204,22 +209,28 @@ secmod_ModuleInit(SECMODModule *mod, SEC
 	*reload = NULL;
     }
 
     if (!mod || !alreadyLoaded) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         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;
     }
     crv = PK11_GETTAB(mod)->C_Initialize(pInitArgs);
     if (CKR_CRYPTOKI_ALREADY_INITIALIZED == crv) {
 	SECMODModule *oldModule = NULL;
 
 	/* Library has already been loaded once, if caller expects it, and it
@@ -246,28 +257,40 @@ secmod_ModuleInit(SECMODModule *mod, SEC
 	}
 	/* reload not possible, fall back to old semantics */
 	if (!enforceAlreadyInitializedError) {
        	    *alreadyLoaded = PR_TRUE;
             return SECSuccess;
 	}
     }
     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;
 	    return SECSuccess;
 	}
     	if (crv != CKR_OK)  {
 	    PORT_SetError(PK11_MapError(crv));
 	    return SECFailure;
--- a/lib/util/oidstring.c
+++ b/lib/util/oidstring.c
@@ -1,16 +1,17 @@
 /* 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 <string.h>
 #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
  * changed to show the actual OID length.
  * Otherwise, memory for the OID will be allocated (from the caller's 
  * PLArenaPool, if pool is non-NULL) and to->data will receive the address
  * of the allocated data, and to->len will receive the OID length.
  * The original value of to->data is not freed when a new buffer is allocated.
@@ -107,8 +108,38 @@ bad_data:
     } else {
     	SECItem result_item = {siBuffer, NULL, 0 };
 	result_item.data = result;
 	result_item.len  = result_bytes;
 	rv = SECITEM_CopyItem(pool, to, &result_item);
     }
     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);
+}
--- a/tests/cert/cert.sh
+++ b/tests/cert/cert.sh
@@ -1171,16 +1171,211 @@ cert_extensions()
             cert_extensions_test
             rm -f ${TARG_FILE}
         else
             echo ${ARG} >> ${TARG_FILE}
         fi
     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
 ########################################################################
 cert_crl_ssl()
 {
     
   ################# Creating Certs ###################################
   #
@@ -1508,16 +1703,17 @@ cert_all_CA
 cert_extended_ssl 
 cert_ssl 
 cert_smime_client        
 if [ -z "$NSS_TEST_DISABLE_FIPS" ]; then
     cert_fips
 fi
 cert_eccurves
 cert_extensions
+cert_san_and_generic_extensions
 cert_test_password
 cert_test_distrust
 cert_test_ocspresp
 
 if [ -z "$NSS_TEST_DISABLE_CRL" ] ; then
     cert_crl_ssl
 else
     echo "$SCRIPTNAME: Skipping CRL Tests"