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