r=nelson Bug=263779
authorneil.williams%sun.com
Wed, 09 Mar 2005 23:02:48 +0000
changeset 5525 d678bd5c27dab5c93ffd31c1d0704a1bc9203e2d
parent 5523 5666b52d3423044db3d5c3e1c060067c72b3a9b2
child 5526 b74db8a01588d471bc12f4b02cc9b5245322252c
push idunknown
push userunknown
push dateunknown
bugs263779
r=nelson Bug=263779
security/nss/cmd/certutil/certutil.c
security/nss/lib/certdb/cert.h
security/nss/lib/certdb/certxutl.c
security/nss/lib/certhigh/certreq.c
security/nss/lib/nss/nss.def
--- a/security/nss/cmd/certutil/certutil.c
+++ b/security/nss/cmd/certutil/certutil.c
@@ -394,49 +394,75 @@ getSignatureOidTag(KeyType keyType, SECO
 #endif /* NSS_ENABLE_ECC */
     default:
     	break;
     }
     return sigTag;
 }
 
 static SECStatus
+AddExtensions(void *, const char *, const char *, PRBool, PRBool, PRBool, PRBool,
+              PRBool, PRBool);
+
+static SECStatus
 CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType,
         SECOidTag hashAlgTag, CERTName *subject, char *phone, int ascii, 
-	const char *emailAddrs, const char *dnsNames, PRFileDesc *outFile)
+	const char *emailAddrs, const char *dnsNames,
+        PRBool	keyUsage, 
+	PRBool  extKeyUsage,
+	PRBool  basicConstraint, 
+	PRBool  authKeyID,
+	PRBool  crlDistPoints, 
+	PRBool  nscpCertType,
+        PRFileDesc *outFile)
 {
     CERTSubjectPublicKeyInfo *spki;
     CERTCertificateRequest *cr;
     SECItem *encoding;
     SECOidTag signAlgTag;
     SECItem result;
     SECStatus rv;
     PRArenaPool *arena;
     PRInt32 numBytes;
+    void *extHandle;
 
     /* Create info about public key */
     spki = SECKEY_CreateSubjectPublicKeyInfo(pubk);
     if (!spki) {
 	SECU_PrintError(progName, "unable to create subject public key");
 	return SECFailure;
     }
-
+    
     /* Generate certificate request */
-    cr = CERT_CreateCertificateRequest(subject, spki, 0);
+    cr = CERT_CreateCertificateRequest(subject, spki, NULL);
     if (!cr) {
 	SECU_PrintError(progName, "unable to make certificate request");
 	return SECFailure;
     }
 
     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
     if ( !arena ) {
 	SECU_PrintError(progName, "out of memory");
 	return SECFailure;
     }
     
+    extHandle = CERT_StartCertificateRequestAttributes(cr);
+    if (extHandle == NULL) {
+        PORT_FreeArena (arena, PR_FALSE);
+	return SECFailure;
+    }
+    if (AddExtensions(extHandle, emailAddrs, PR_FALSE, PR_FALSE, PR_FALSE,
+                      PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE)
+                  != SECSuccess) {
+        PORT_FreeArena (arena, PR_FALSE);
+        return SECFailure;
+    }
+    CERT_FinishExtensions(extHandle);
+    CERT_FinishCertificateRequestAttributes(cr);
+
     /* Der encode the request */
     encoding = SEC_ASN1EncodeItem(arena, NULL, cr,
 		  SEC_ASN1_GET(CERT_CertificateRequestTemplate));
     if (encoding == NULL) {
 	SECU_PrintError(progName, "der encoding of request failed");
 	return SECFailure;
     }
 
@@ -2076,16 +2102,81 @@ AddCrlDistPoint(void *extHandle)
 	      (EXTEN_VALUE_ENCODER)  CERT_EncodeCRLDistributionPoints);
     }
     if (arena)
 	PORT_FreeArena (arena, PR_FALSE);
     return (rv);
 }
 
 static SECStatus
+AddExtensions(void *extHandle, const char *emailAddrs, const char *dnsNames,
+	PRBool	keyUsage, 
+	PRBool  extKeyUsage,
+	PRBool  basicConstraint, 
+	PRBool  authKeyID,
+	PRBool  crlDistPoints, 
+	PRBool  nscpCertType)
+{
+    SECStatus 	rv = SECSuccess;
+    do {
+	/* Add key usage extension */
+	if (keyUsage) {
+	    rv = AddKeyUsage(extHandle);
+	    if (rv)
+		break;
+	}
+
+	/* Add extended key usage extension */
+	if (extKeyUsage) {
+	    rv = AddExtKeyUsage(extHandle);
+	    if (rv)
+		break;
+	}
+
+	/* Add basic constraint extension */
+	if (basicConstraint) {
+	    rv = AddBasicConstraint(extHandle);
+	    if (rv)
+		break;
+	}
+
+	if (authKeyID) {
+	    rv = AddAuthKeyID (extHandle);
+	    if (rv)
+		break;
+	}    
+
+	if (crlDistPoints) {
+	    rv = AddCrlDistPoint (extHandle);
+	    if (rv)
+		break;
+	}
+	
+	if (nscpCertType) {
+	    rv = AddNscpCertType(extHandle);
+	    if (rv)
+		break;
+	}
+
+	if (emailAddrs != NULL) {
+	    rv = AddEmailSubjectAlt(extHandle,emailAddrs);
+	    if (rv)
+		break;
+	}
+
+	if (dnsNames != NULL) {
+	    rv = AddDNSSubjectAlt(extHandle,dnsNames);
+	    if (rv)
+		break;
+	}
+    } while (0);
+    return rv;
+}
+
+static SECStatus
 CreateCert(
 	CERTCertDBHandle *handle, 
 	char *  issuerNickName, 
 	PRFileDesc *inFile,
 	PRFileDesc *outFile, 
 	SECKEYPrivateKey *selfsignprivkey,
 	void 	*pwarg,
 	SECOidTag hashAlgTag,
@@ -2105,16 +2196,17 @@ CreateCert(
 {
     void *	extHandle;
     SECItem *	certDER;
     PRArenaPool *arena			= NULL;
     CERTCertificate *subjectCert 	= NULL;
     CERTCertificateRequest *certReq	= NULL;
     SECStatus 	rv 			= SECSuccess;
     SECItem 	reqDER;
+    CERTCertExtension **CRexts;
 
     reqDER.data = NULL;
     do {
 	arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
 	if (!arena) {
 	    GEN_BREAK (SECFailure);
 	}
 	
@@ -2124,73 +2216,39 @@ CreateCert(
 	    GEN_BREAK (SECFailure)
 	}
 
 	subjectCert = MakeV1Cert (handle, certReq, issuerNickName, selfsign,
 				  serialNumber, warpmonths, validitylength);
 	if (subjectCert == NULL) {
 	    GEN_BREAK (SECFailure)
 	}
-
+        
+        
 	extHandle = CERT_StartCertExtensions (subjectCert);
 	if (extHandle == NULL) {
 	    GEN_BREAK (SECFailure)
 	}
-
-	/* Add key usage extension */
-	if (keyUsage) {
-	    rv = AddKeyUsage(extHandle);
-	    if (rv)
-		break;
-	}
-
-	/* Add extended key usage extension */
-	if (extKeyUsage) {
-	    rv = AddExtKeyUsage(extHandle);
-	    if (rv)
-		break;
-	}
-
-	/* Add basic constraint extension */
-	if (basicConstraint) {
-	    rv = AddBasicConstraint(extHandle);
-	    if (rv)
-		break;
+        
+        rv = AddExtensions(extHandle, emailAddrs, dnsNames, keyUsage, extKeyUsage,
+                          basicConstraint, authKeyID, crlDistPoints, nscpCertType);
+        if (rv != SECSuccess) {
+	    GEN_BREAK (SECFailure)
 	}
-
-	if (authKeyID) {
-	    rv = AddAuthKeyID (extHandle);
-	    if (rv)
-		break;
-	}    
-
-
-	if (crlDistPoints) {
-	    rv = AddCrlDistPoint (extHandle);
-	    if (rv)
-		break;
-	}
-	
-	if (nscpCertType) {
-	    rv = AddNscpCertType(extHandle);
-	    if (rv)
-		break;
-	}
-
-	if (emailAddrs != NULL) {
-	    rv = AddEmailSubjectAlt(extHandle,emailAddrs);
-	    if (rv)
-		break;
-	}
-
-	if (dnsNames != NULL) {
-	    rv = AddDNSSubjectAlt(extHandle,dnsNames);
-	    if (rv)
-		break;
-	}
+        
+        if (certReq->attributes != NULL &&
+            SECOID_FindOIDTag(&(*certReq->attributes)->attrType)
+                == SEC_OID_PKCS9_EXTENSION_REQUEST) {
+            rv = CERT_GetCertificateRequestExtensions(certReq, &CRexts);
+            if (rv != SECSuccess)
+                break;
+            rv = CERT_MergeExtensions(extHandle, CRexts);
+            if (rv != SECSuccess)
+                break;
+        }
 
 	CERT_FinishExtensions(extHandle);
 
 	certDER = SignCert(handle, subjectCert, selfsign, hashAlgTag,
 	                   selfsignprivkey, issuerNickName,pwarg);
 
 	if (certDER) {
 	   if (ascii) {
@@ -2872,17 +2930,23 @@ secuCommandFlag certutil_options[] =
     /*  Make a cert request (-R or -S).  */
     if (certutil.commands[cmd_CreateAndAddCert].activated ||
          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,
-		     outFile ? outFile : PR_STDOUT);
+                     certutil.options[opt_AddKeyUsageExt].activated,
+                     certutil.options[opt_AddExtKeyUsageExt].activated,
+                     certutil.options[opt_AddBasicConstraintExt].activated,
+                     certutil.options[opt_AddAuthorityKeyIDExt].activated,
+                     certutil.options[opt_AddCRLDistPtsExt].activated,
+                     certutil.options[opt_AddNSCertTypeExt].activated,
+                     outFile ? outFile : PR_STDOUT);
 	if (rv) 
 	    goto shutdown;
 	privkey->wincx = &pwdata;
     }
 
     /*
      *  Certificate creation
      */
--- a/security/nss/lib/certdb/cert.h
+++ b/security/nss/lib/certdb/cert.h
@@ -246,16 +246,36 @@ CERT_CreateCertificateRequest (CERTName 
 /*
 ** Destroy a certificate-request object
 **	"r" the certificate-request to destroy
 **	"freeit" if PR_TRUE then free the object as well as its sub-objects
 */
 extern void CERT_DestroyCertificateRequest(CERTCertificateRequest *r);
 
 /*
+** Start adding extensions to a certificate request.
+*/
+void *
+CERT_StartCertificateRequestAttributes(CERTCertificateRequest *req);
+
+/*
+** Reformat the certifcate extension list into a CertificateRequest
+** attribute list.
+*/
+SECStatus
+CERT_FinishCertificateRequestAttributes(CERTCertificateRequest *req);
+
+/*
+** Extract the Extension Requests from a DER CertRequest attribute list.
+*/
+SECStatus
+CERT_GetCertificateRequestExtensions(CERTCertificateRequest *req,
+                                     CERTCertExtension ***exts);
+
+/*
 ** Extract a public key object from a certificate
 */
 extern SECKEYPublicKey *CERT_ExtractPublicKey(CERTCertificate *cert);
 
 /*
  * used to get a public key with Key Material ID. Only used for fortezza V1
  * certificates.
  */
@@ -818,16 +838,23 @@ CERT_EncodeAltNameExtension(PRArenaPool 
 /*
 ** Finish adding cert extensions.  Does final processing on extension
 ** data, putting it in the right format, and freeing any temporary
 ** storage.
 **	"exthandle" is the handle used to add extensions to a certificate
 */
 extern SECStatus CERT_FinishExtensions(void *exthandle);
 
+/*
+** Merge an external list of extensions into a cert's extension list, adding one
+** only when its OID matches none of the cert's existing extensions. Call this
+** immediately before calling CERT_FinishExtensions().
+*/
+SECStatus
+CERT_MergeExtensions(void *exthandle, CERTCertExtension **exts);
 
 /* If the extension is found, return its criticality and value.
 ** This allocate storage for the returning extension value.
 */
 extern SECStatus CERT_GetExtenCriticality
    (CERTCertExtension **extensions, int tag, PRBool *isCritical);
 
 extern void
--- a/security/nss/lib/certdb/certxutl.c
+++ b/security/nss/lib/certdb/certxutl.c
@@ -370,16 +370,61 @@ CERT_FinishExtensions(void *exthandle)
     rv = SECSuccess;
 
 loser:
     /* free working arena */
     PORT_FreeArena(handle->arena, PR_FALSE);
     return rv;
 }
 
+SECStatus
+CERT_MergeExtensions(void *exthandle, CERTCertExtension **extensions)
+{
+    CERTCertExtension *ext;
+    SECStatus rv = SECSuccess;
+    SECOidTag tag;
+    extNode *node;
+    extRec *handle = exthandle;
+    PRBool critical;
+    
+    if (!exthandle || !extensions) {
+	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+    while ((ext = *extensions++) != NULL) {
+        tag = SECOID_FindOIDTag(&ext->id);
+        for (node=handle->head; node != NULL; node=node->next) {
+            if (tag == 0) {
+                if (SECITEM_ItemsAreEqual(&ext->id, &node->ext->id))
+                    break;
+            }
+            else {
+                if (SECOID_FindOIDTag(&node->ext->id) == tag) {
+                    break;
+                }
+            }
+        }
+        if (node == NULL) {
+            PRBool critical = (ext->critical.len != 0 &&
+                            ext->critical.data[ext->critical.len - 1] != 0);
+            if (critical && tag == SEC_OID_UNKNOWN) {
+               PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
+               rv = SECFailure;
+               break;
+            }
+            /* add to list */
+            rv = CERT_AddExtensionByOID (exthandle, &ext->id, &ext->value,
+                                         critical, PR_TRUE);
+            if (rv != SECSuccess)
+                break;
+        }
+    }
+    return rv;
+}
+
 /*
  * get the value of the Netscape Certificate Type Extension
  */
 SECStatus
 CERT_FindBitStringExtension (CERTCertExtension **extensions, int tag,
 			     SECItem *retItem)
 {
     SECItem wrapperItem, tmpItem = {siBuffer,0};
--- a/security/nss/lib/certhigh/certreq.c
+++ b/security/nss/lib/certhigh/certreq.c
@@ -30,20 +30,22 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "cert.h"
+#include "certt.h"
 #include "secder.h"
 #include "key.h"
 #include "secitem.h"
 #include "secasn1.h"
+#include "secerr.h"
 
 const SEC_ASN1Template CERT_AttributeTemplate[] = {
     { SEC_ASN1_SEQUENCE,
 	0, NULL, sizeof(CERTAttribute) },
     { SEC_ASN1_OBJECT_ID, offsetof(CERTAttribute, attrType) },
     { SEC_ASN1_SET_OF, offsetof(CERTAttribute, attrValue),
 	SEC_AnyTemplate },
     { 0 }
@@ -161,18 +163,17 @@ CERT_CreateCertificateRequest(CERTName *
 	    goto loser;
 
 	/* Copy over attribute information */
 	if (attributes) {
 	    int i = 0;
 
 	    /* allocate space for attributes */
 	    while(attributes[i] != NULL) i++;
-	    certreq->attributes = (SECItem**)PORT_ArenaZAlloc(arena, 
-						   	      sizeof(SECItem *) * (i + 1));
+	    certreq->attributes = PORT_ArenaZNewArray(arena,CERTAttribute*,i+1);
 	    if(!certreq->attributes) {
 		goto loser;
 	    }
 
 	    /* copy attributes */
 	    i = 0;
 	    while(attributes[i]) {
 		/*
@@ -226,8 +227,101 @@ void
 CERT_DestroyCertificateRequest(CERTCertificateRequest *req)
 {
     if (req && req->arena) {
 	PORT_FreeArena(req->arena, PR_FALSE);
     }
     return;
 }
 
+static void
+setCRExt(void *o, CERTCertExtension **exts)
+{
+    ((CERTCertificateRequest *)o)->attributes = (struct CERTAttributeStr **)exts;
+}
+
+/*
+** Set up to start gathering cert extensions for a cert request.
+** The list is created as CertExtensions and converted to an
+** attribute list by CERT_FinishCRAttributes().
+ */
+extern void *cert_StartExtensions(void *owner, PRArenaPool *ownerArena,
+                       void (*setExts)(void *object, CERTCertExtension **exts));
+void *
+CERT_StartCertificateRequestAttributes(CERTCertificateRequest *req)
+{
+    return (cert_StartExtensions ((void *)req, req->arena, setCRExt));
+}
+
+/*
+** At entry req->attributes actually contains an list of cert extensions--
+** req-attributes is overloaded until the list is DER encoded (the first
+** ...EncodeItem() below).
+** We turn this into an attribute list by encapsulating it
+** in a PKCS 10 Attribute structure
+ */
+SECStatus
+CERT_FinishCertificateRequestAttributes(CERTCertificateRequest *req)
+{   SECItem *extlist;
+    SECOidData *oidrec;
+    CERTAttribute *attribute;
+   
+    if (!req || !req->arena) {
+	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+    if (req->attributes == NULL)
+        return SECSuccess;
+
+    extlist = SEC_ASN1EncodeItem(req->arena, NULL, &req->attributes,
+                            SEC_ASN1_GET(CERT_SequenceOfCertExtensionTemplate));
+    if (extlist == NULL)
+        return(SECFailure);
+
+    oidrec = SECOID_FindOIDByTag(SEC_OID_PKCS9_EXTENSION_REQUEST);
+    if (oidrec == NULL)
+	return SECFailure;
+
+    /* now change the list of cert extensions into a list of attributes
+     */
+    req->attributes = PORT_ArenaZNewArray(req->arena, CERTAttribute*, 2);
+
+    attribute = PORT_ArenaZNew(req->arena, CERTAttribute);
+    
+    if (req->attributes == NULL || attribute == NULL ||
+        SECITEM_CopyItem(req->arena, &attribute->attrType, &oidrec->oid) != 0) {
+        PORT_SetError(SEC_ERROR_NO_MEMORY);
+	return SECFailure;
+    }
+    attribute->attrValue = PORT_ArenaZNewArray(req->arena, SECItem*, 2);
+
+    if (attribute->attrValue == NULL)
+        return SECFailure;
+
+    attribute->attrValue[0] = extlist;
+    attribute->attrValue[1] = NULL;
+    req->attributes[0] = attribute;
+    req->attributes[1] = NULL;
+
+    return SECSuccess;
+}
+
+SECStatus
+CERT_GetCertificateRequestExtensions(CERTCertificateRequest *req,
+                                        CERTCertExtension ***exts)
+{
+    if (req == NULL || exts == NULL) {
+	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+    
+    if (req->attributes == NULL || *req->attributes == NULL)
+        return SECSuccess;
+    
+    if ((*req->attributes)->attrValue == NULL) {
+	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+
+    return(SEC_ASN1DecodeItem(req->arena, exts, 
+            SEC_ASN1_GET(CERT_SequenceOfCertExtensionTemplate),
+            (*req->attributes)->attrValue[0]));
+}
--- a/security/nss/lib/nss/nss.def
+++ b/security/nss/lib/nss/nss.def
@@ -815,21 +815,25 @@ SECMOD_WaitForAnyTokenEvent;
 CERT_CacheCRL;
 CERT_DecodeAltNameExtension;
 CERT_DecodeAuthInfoAccessExtension;
 CERT_DecodeAuthKeyID;
 CERT_DecodeCRLDistributionPoints;
 CERT_DecodeNameConstraintsExtension;
 CERT_DecodePrivKeyUsagePeriodExtension;
 CERT_DestroyUserNotice;
+CERT_FinishCertificateRequestAttributes;
 CERT_GetCertificateNames;
+CERT_GetCertificateRequestExtensions;
 CERT_GetNextGeneralName;
 CERT_GetNextNameConstraint;
 CERT_GetPrevGeneralName;
 CERT_GetPrevNameConstraint;
+CERT_MergeExtensions;
+CERT_StartCertificateRequestAttributes;
 CERT_StartCRLEntryExtensions;
 CERT_StartCRLExtensions;
 CERT_UncacheCRL;
 HASH_Clone;
 HASH_HashBuf;
 HASH_ResultLenByOidTag;
 HASH_ResultLenContext;
 SEC_GetSignatureAlgorithmOidTag;