Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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;
}