diff options
Diffstat (limited to 'security/nss/lib/crmf/crmfreq.c')
-rw-r--r-- | security/nss/lib/crmf/crmfreq.c | 663 |
1 files changed, 663 insertions, 0 deletions
diff --git a/security/nss/lib/crmf/crmfreq.c b/security/nss/lib/crmf/crmfreq.c new file mode 100644 index 0000000000..e89f18228c --- /dev/null +++ b/security/nss/lib/crmf/crmfreq.c @@ -0,0 +1,663 @@ +/* -*- Mode: C; tab-width: 8 -*-*/ +/* 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 "crmf.h" +#include "crmfi.h" +#include "keyhi.h" +#include "secder.h" + +/* + * Macro that returns PR_TRUE if the pointer is not NULL. + * If the pointer is NULL, then the macro will return PR_FALSE. + */ +#define IS_NOT_NULL(ptr) ((ptr) == NULL) ? PR_FALSE : PR_TRUE + +const unsigned char hexTrue = 0xff; +const unsigned char hexFalse = 0x00; + +SECStatus +crmf_encode_integer(PLArenaPool *poolp, SECItem *dest, long value) +{ + SECItem *dummy; + + dummy = SEC_ASN1EncodeInteger(poolp, dest, value); + PORT_Assert(dummy == dest); + if (dummy == NULL) { + return SECFailure; + } + return SECSuccess; +} + +SECStatus +crmf_encode_unsigned_integer(PLArenaPool *poolp, SECItem *dest, + unsigned long value) +{ + SECItem *dummy; + + dummy = SEC_ASN1EncodeUnsignedInteger(poolp, dest, value); + PORT_Assert(dummy == dest); + if (dummy != dest) { + return SECFailure; + } + return SECSuccess; +} + +static SECStatus +crmf_copy_secitem(PLArenaPool *poolp, SECItem *dest, SECItem *src) +{ + return SECITEM_CopyItem(poolp, dest, src); +} + +PRBool +CRMF_DoesRequestHaveField(CRMFCertRequest *inCertReq, + CRMFCertTemplateField inField) +{ + + PORT_Assert(inCertReq != NULL); + if (inCertReq == NULL) { + return PR_FALSE; + } + switch (inField) { + case crmfVersion: + return inCertReq->certTemplate.version.data != NULL; + case crmfSerialNumber: + return inCertReq->certTemplate.serialNumber.data != NULL; + case crmfSigningAlg: + return inCertReq->certTemplate.signingAlg != NULL; + case crmfIssuer: + return inCertReq->certTemplate.issuer != NULL; + case crmfValidity: + return inCertReq->certTemplate.validity != NULL; + case crmfSubject: + return inCertReq->certTemplate.subject != NULL; + case crmfPublicKey: + return inCertReq->certTemplate.publicKey != NULL; + case crmfIssuerUID: + return inCertReq->certTemplate.issuerUID.data != NULL; + case crmfSubjectUID: + return inCertReq->certTemplate.subjectUID.data != NULL; + case crmfExtension: + return CRMF_CertRequestGetNumberOfExtensions(inCertReq) != 0; + } + return PR_FALSE; +} + +CRMFCertRequest * +CRMF_CreateCertRequest(PRUint32 inRequestID) +{ + PLArenaPool *poolp; + CRMFCertRequest *certReq; + SECStatus rv; + + poolp = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE); + if (poolp == NULL) { + goto loser; + } + + certReq = PORT_ArenaZNew(poolp, CRMFCertRequest); + if (certReq == NULL) { + goto loser; + } + + certReq->poolp = poolp; + certReq->requestID = inRequestID; + + rv = crmf_encode_unsigned_integer(poolp, &(certReq->certReqId), + inRequestID); + if (rv != SECSuccess) { + goto loser; + } + + return certReq; +loser: + if (poolp) { + PORT_FreeArena(poolp, PR_FALSE); + } + return NULL; +} + +SECStatus +CRMF_DestroyCertRequest(CRMFCertRequest *inCertReq) +{ + PORT_Assert(inCertReq != NULL); + if (inCertReq != NULL) { + if (inCertReq->certTemplate.extensions) { + PORT_Free(inCertReq->certTemplate.extensions); + } + if (inCertReq->controls) { + /* Right now we don't support EnveloppedData option, + * so we won't go through and delete each occurrence of + * an EnveloppedData in the control. + */ + PORT_Free(inCertReq->controls); + } + if (inCertReq->poolp) { + PORT_FreeArena(inCertReq->poolp, PR_TRUE); + } + } + return SECSuccess; +} + +static SECStatus +crmf_template_add_version(PLArenaPool *poolp, SECItem *dest, long version) +{ + return (crmf_encode_integer(poolp, dest, version)); +} + +static SECStatus +crmf_template_add_serialnumber(PLArenaPool *poolp, SECItem *dest, long serial) +{ + return (crmf_encode_integer(poolp, dest, serial)); +} + +SECStatus +crmf_template_copy_secalg(PLArenaPool *poolp, SECAlgorithmID **dest, + SECAlgorithmID *src) +{ + SECStatus rv; + void *mark = NULL; + SECAlgorithmID *mySecAlg; + + if (!poolp) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + mark = PORT_ArenaMark(poolp); + *dest = mySecAlg = PORT_ArenaZNew(poolp, SECAlgorithmID); + if (mySecAlg == NULL) { + goto loser; + } + rv = SECOID_CopyAlgorithmID(poolp, mySecAlg, src); + if (rv != SECSuccess) { + goto loser; + } + if (mark) { + PORT_ArenaUnmark(poolp, mark); + } + return SECSuccess; + +loser: + *dest = NULL; + if (mark) { + PORT_ArenaRelease(poolp, mark); + } + return SECFailure; +} + +SECStatus +crmf_copy_cert_name(PLArenaPool *poolp, CERTName **dest, + CERTName *src) +{ + CERTName *newName; + SECStatus rv; + void *mark; + + mark = PORT_ArenaMark(poolp); + *dest = newName = PORT_ArenaZNew(poolp, CERTName); + if (newName == NULL) { + goto loser; + } + + rv = CERT_CopyName(poolp, newName, src); + if (rv != SECSuccess) { + goto loser; + } + PORT_ArenaUnmark(poolp, mark); + return SECSuccess; +loser: + PORT_ArenaRelease(poolp, mark); + *dest = NULL; + return SECFailure; +} + +static SECStatus +crmf_template_add_issuer(PLArenaPool *poolp, CERTName **dest, + CERTName *issuerName) +{ + return crmf_copy_cert_name(poolp, dest, issuerName); +} + +static SECStatus +crmf_template_add_validity(PLArenaPool *poolp, CRMFOptionalValidity **dest, + CRMFValidityCreationInfo *info) +{ + SECStatus rv; + void *mark; + CRMFOptionalValidity *myValidity; + + /*First off, let's make sure at least one of the two fields is present*/ + if (!info || (!info->notBefore && !info->notAfter)) { + return SECFailure; + } + mark = PORT_ArenaMark(poolp); + *dest = myValidity = PORT_ArenaZNew(poolp, CRMFOptionalValidity); + if (myValidity == NULL) { + goto loser; + } + + if (info->notBefore) { + rv = DER_EncodeTimeChoice(poolp, &myValidity->notBefore, + *info->notBefore); + if (rv != SECSuccess) { + goto loser; + } + } + if (info->notAfter) { + rv = DER_EncodeTimeChoice(poolp, &myValidity->notAfter, + *info->notAfter); + if (rv != SECSuccess) { + goto loser; + } + } + PORT_ArenaUnmark(poolp, mark); + return SECSuccess; +loser: + PORT_ArenaRelease(poolp, mark); + *dest = NULL; + return SECFailure; +} + +static SECStatus +crmf_template_add_subject(PLArenaPool *poolp, CERTName **dest, + CERTName *subject) +{ + return crmf_copy_cert_name(poolp, dest, subject); +} + +SECStatus +crmf_template_add_public_key(PLArenaPool *poolp, + CERTSubjectPublicKeyInfo **dest, + CERTSubjectPublicKeyInfo *pubKey) +{ + CERTSubjectPublicKeyInfo *spki; + SECStatus rv; + + *dest = spki = (poolp == NULL) ? PORT_ZNew(CERTSubjectPublicKeyInfo) : PORT_ArenaZNew(poolp, CERTSubjectPublicKeyInfo); + if (spki == NULL) { + goto loser; + } + rv = SECKEY_CopySubjectPublicKeyInfo(poolp, spki, pubKey); + if (rv != SECSuccess) { + goto loser; + } + return SECSuccess; +loser: + if (poolp == NULL && spki != NULL) { + SECKEY_DestroySubjectPublicKeyInfo(spki); + } + *dest = NULL; + return SECFailure; +} + +static SECStatus +crmf_copy_bitstring(PLArenaPool *poolp, SECItem *dest, const SECItem *src) +{ + SECStatus rv; + SECItem byteSrc; + + byteSrc = *src; + byteSrc.len = CRMF_BITS_TO_BYTES(byteSrc.len); + rv = crmf_copy_secitem(poolp, dest, &byteSrc); + dest->len = src->len; + return rv; +} + +static SECStatus +crmf_template_add_issuer_uid(PLArenaPool *poolp, SECItem *dest, + const SECItem *issuerUID) +{ + return crmf_copy_bitstring(poolp, dest, issuerUID); +} + +static SECStatus +crmf_template_add_subject_uid(PLArenaPool *poolp, SECItem *dest, + const SECItem *subjectUID) +{ + return crmf_copy_bitstring(poolp, dest, subjectUID); +} + +static void +crmf_zeroize_new_extensions(CRMFCertExtension **extensions, + int numToZeroize) +{ + PORT_Memset((void *)extensions, 0, sizeof(CERTCertExtension *) * numToZeroize); +} + +/* + * The strategy for adding templates will differ from all the other + * attributes in the template. First, we want to allow the client + * of this API to set extensions more than just once. So we will + * need the ability grow the array of extensions. Since arenas don't + * give us the realloc function, we'll use the generic PORT_* functions + * to allocate the array of pointers *ONLY*. Then we will allocate each + * individual extension from the arena that comes along with the certReq + * structure that owns this template. + */ +static SECStatus +crmf_template_add_extensions(PLArenaPool *poolp, CRMFCertTemplate *inTemplate, + CRMFCertExtCreationInfo *extensions) +{ + void *mark; + int newSize, oldSize, i; + SECStatus rv; + CRMFCertExtension **extArray; + CRMFCertExtension *newExt, *currExt; + + mark = PORT_ArenaMark(poolp); + if (inTemplate->extensions == NULL) { + newSize = extensions->numExtensions; + extArray = PORT_ZNewArray(CRMFCertExtension *, newSize + 1); + } else { + newSize = inTemplate->numExtensions + extensions->numExtensions; + extArray = PORT_Realloc(inTemplate->extensions, + sizeof(CRMFCertExtension *) * (newSize + 1)); + } + if (extArray == NULL) { + goto loser; + } + oldSize = inTemplate->numExtensions; + inTemplate->extensions = extArray; + inTemplate->numExtensions = newSize; + for (i = oldSize; i < newSize; i++) { + newExt = PORT_ArenaZNew(poolp, CRMFCertExtension); + if (newExt == NULL) { + goto loser2; + } + currExt = extensions->extensions[i - oldSize]; + rv = crmf_copy_secitem(poolp, &(newExt->id), &(currExt->id)); + if (rv != SECSuccess) { + goto loser2; + } + rv = crmf_copy_secitem(poolp, &(newExt->critical), + &(currExt->critical)); + if (rv != SECSuccess) { + goto loser2; + } + rv = crmf_copy_secitem(poolp, &(newExt->value), &(currExt->value)); + if (rv != SECSuccess) { + goto loser2; + } + extArray[i] = newExt; + } + extArray[newSize] = NULL; + PORT_ArenaUnmark(poolp, mark); + return SECSuccess; +loser2: + crmf_zeroize_new_extensions(&(inTemplate->extensions[oldSize]), + extensions->numExtensions); + inTemplate->numExtensions = oldSize; +loser: + PORT_ArenaRelease(poolp, mark); + return SECFailure; +} + +SECStatus +CRMF_CertRequestSetTemplateField(CRMFCertRequest *inCertReq, + CRMFCertTemplateField inTemplateField, + void *data) +{ + CRMFCertTemplate *certTemplate; + PLArenaPool *poolp; + SECStatus rv = SECFailure; + void *mark; + + if (inCertReq == NULL) { + return SECFailure; + } + + certTemplate = &(inCertReq->certTemplate); + + poolp = inCertReq->poolp; + mark = PORT_ArenaMark(poolp); + switch (inTemplateField) { + case crmfVersion: + rv = crmf_template_add_version(poolp, &(certTemplate->version), + *(long *)data); + break; + case crmfSerialNumber: + rv = crmf_template_add_serialnumber(poolp, + &(certTemplate->serialNumber), + *(long *)data); + break; + case crmfSigningAlg: + rv = crmf_template_copy_secalg(poolp, &(certTemplate->signingAlg), + (SECAlgorithmID *)data); + break; + case crmfIssuer: + rv = crmf_template_add_issuer(poolp, &(certTemplate->issuer), + (CERTName *)data); + break; + case crmfValidity: + rv = crmf_template_add_validity(poolp, &(certTemplate->validity), + (CRMFValidityCreationInfo *)data); + break; + case crmfSubject: + rv = crmf_template_add_subject(poolp, &(certTemplate->subject), + (CERTName *)data); + break; + case crmfPublicKey: + rv = crmf_template_add_public_key(poolp, &(certTemplate->publicKey), + (CERTSubjectPublicKeyInfo *)data); + break; + case crmfIssuerUID: + rv = crmf_template_add_issuer_uid(poolp, &(certTemplate->issuerUID), + (SECItem *)data); + break; + case crmfSubjectUID: + rv = crmf_template_add_subject_uid(poolp, &(certTemplate->subjectUID), + (SECItem *)data); + break; + case crmfExtension: + rv = crmf_template_add_extensions(poolp, certTemplate, + (CRMFCertExtCreationInfo *)data); + break; + } + if (rv != SECSuccess) { + PORT_ArenaRelease(poolp, mark); + } else { + PORT_ArenaUnmark(poolp, mark); + } + return rv; +} + +SECStatus +CRMF_CertReqMsgSetCertRequest(CRMFCertReqMsg *inCertReqMsg, + CRMFCertRequest *inCertReq) +{ + PORT_Assert(inCertReqMsg != NULL && inCertReq != NULL); + if (inCertReqMsg == NULL || inCertReq == NULL) { + return SECFailure; + } + inCertReqMsg->certReq = crmf_copy_cert_request(inCertReqMsg->poolp, + inCertReq); + return (inCertReqMsg->certReq == NULL) ? SECFailure : SECSuccess; +} + +CRMFCertReqMsg * +CRMF_CreateCertReqMsg(void) +{ + PLArenaPool *poolp; + CRMFCertReqMsg *reqMsg; + + poolp = PORT_NewArena(CRMF_DEFAULT_ARENA_SIZE); + if (poolp == NULL) { + goto loser; + } + reqMsg = PORT_ArenaZNew(poolp, CRMFCertReqMsg); + if (reqMsg == NULL) { + goto loser; + } + reqMsg->poolp = poolp; + return reqMsg; + +loser: + if (poolp) { + PORT_FreeArena(poolp, PR_FALSE); + } + return NULL; +} + +SECStatus +CRMF_DestroyCertReqMsg(CRMFCertReqMsg *inCertReqMsg) +{ + PORT_Assert(inCertReqMsg != NULL && inCertReqMsg->poolp != NULL); + if (!inCertReqMsg->isDecoded) { + if (inCertReqMsg->certReq->certTemplate.extensions != NULL) { + PORT_Free(inCertReqMsg->certReq->certTemplate.extensions); + } + if (inCertReqMsg->certReq->controls != NULL) { + PORT_Free(inCertReqMsg->certReq->controls); + } + } + PORT_FreeArena(inCertReqMsg->poolp, PR_TRUE); + return SECSuccess; +} + +CRMFCertExtension * +crmf_create_cert_extension(PLArenaPool *poolp, + SECOidTag id, + PRBool isCritical, + SECItem *data) +{ + CRMFCertExtension *newExt; + SECOidData *oidData; + SECStatus rv; + + newExt = (poolp == NULL) ? PORT_ZNew(CRMFCertExtension) : PORT_ArenaZNew(poolp, CRMFCertExtension); + if (newExt == NULL) { + goto loser; + } + oidData = SECOID_FindOIDByTag(id); + if (oidData == NULL || + oidData->supportedExtension != SUPPORTED_CERT_EXTENSION) { + goto loser; + } + + rv = SECITEM_CopyItem(poolp, &(newExt->id), &(oidData->oid)); + if (rv != SECSuccess) { + goto loser; + } + + rv = SECITEM_CopyItem(poolp, &(newExt->value), data); + if (rv != SECSuccess) { + goto loser; + } + + if (isCritical) { + newExt->critical.data = (poolp == NULL) ? PORT_New(unsigned char) + : PORT_ArenaNew(poolp, unsigned char); + if (newExt->critical.data == NULL) { + goto loser; + } + newExt->critical.data[0] = hexTrue; + newExt->critical.len = 1; + } + return newExt; +loser: + if (newExt != NULL && poolp == NULL) { + CRMF_DestroyCertExtension(newExt); + } + return NULL; +} + +CRMFCertExtension * +CRMF_CreateCertExtension(SECOidTag id, + PRBool isCritical, + SECItem *data) +{ + return crmf_create_cert_extension(NULL, id, isCritical, data); +} + +static SECStatus +crmf_destroy_cert_extension(CRMFCertExtension *inExtension, PRBool freeit) +{ + if (inExtension != NULL) { + SECITEM_FreeItem(&(inExtension->id), PR_FALSE); + SECITEM_FreeItem(&(inExtension->value), PR_FALSE); + SECITEM_FreeItem(&(inExtension->critical), PR_FALSE); + if (freeit) { + PORT_Free(inExtension); + } + } + return SECSuccess; +} + +SECStatus +CRMF_DestroyCertExtension(CRMFCertExtension *inExtension) +{ + return crmf_destroy_cert_extension(inExtension, PR_TRUE); +} + +SECStatus +CRMF_DestroyCertReqMessages(CRMFCertReqMessages *inCertReqMsgs) +{ + PORT_Assert(inCertReqMsgs != NULL); + if (inCertReqMsgs != NULL) { + PORT_FreeArena(inCertReqMsgs->poolp, PR_TRUE); + } + return SECSuccess; +} + +static PRBool +crmf_item_has_data(SECItem *item) +{ + if (item != NULL && item->data != NULL) { + return PR_TRUE; + } + return PR_FALSE; +} + +PRBool +CRMF_CertRequestIsFieldPresent(CRMFCertRequest *inCertReq, + CRMFCertTemplateField inTemplateField) +{ + PRBool retVal; + CRMFCertTemplate *certTemplate; + + PORT_Assert(inCertReq != NULL); + if (inCertReq == NULL) { + /* This is probably some kind of error, but this is + * the safest return value for this function. + */ + return PR_FALSE; + } + certTemplate = &inCertReq->certTemplate; + switch (inTemplateField) { + case crmfVersion: + retVal = crmf_item_has_data(&certTemplate->version); + break; + case crmfSerialNumber: + retVal = crmf_item_has_data(&certTemplate->serialNumber); + break; + case crmfSigningAlg: + retVal = IS_NOT_NULL(certTemplate->signingAlg); + break; + case crmfIssuer: + retVal = IS_NOT_NULL(certTemplate->issuer); + break; + case crmfValidity: + retVal = IS_NOT_NULL(certTemplate->validity); + break; + case crmfSubject: + retVal = IS_NOT_NULL(certTemplate->subject); + break; + case crmfPublicKey: + retVal = IS_NOT_NULL(certTemplate->publicKey); + break; + case crmfIssuerUID: + retVal = crmf_item_has_data(&certTemplate->issuerUID); + break; + case crmfSubjectUID: + retVal = crmf_item_has_data(&certTemplate->subjectUID); + break; + case crmfExtension: + retVal = IS_NOT_NULL(certTemplate->extensions); + break; + default: + retVal = PR_FALSE; + } + return retVal; +} |