Source code
Revision control
Copy as Markdown
Other Tools
/* 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
/*
* Certificate Extensions handling code
*
*/
#include "cert.h"
#include "secitem.h"
#include "secoid.h"
#include "secder.h"
#include "secasn1.h"
#include "certxutl.h"
#include "secerr.h"
#ifdef OLD
#include "ocspti.h" /* XXX a better extensions interface would not
* require knowledge of data structures of callers */
#endif
static CERTCertExtension *
GetExtension(CERTCertExtension **extensions, SECItem *oid)
{
CERTCertExtension **exts;
CERTCertExtension *ext = NULL;
SECComparison comp;
exts = extensions;
if (exts) {
while (*exts) {
ext = *exts;
comp = SECITEM_CompareItem(oid, &ext->id);
if (comp == SECEqual)
break;
exts++;
}
return (*exts ? ext : NULL);
}
return (NULL);
}
SECStatus
cert_FindExtensionByOID(CERTCertExtension **extensions, SECItem *oid,
SECItem *value)
{
CERTCertExtension *ext;
SECStatus rv = SECSuccess;
ext = GetExtension(extensions, oid);
if (ext == NULL) {
PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND);
return (SECFailure);
}
if (value)
rv = SECITEM_CopyItem(NULL, value, &ext->value);
return (rv);
}
SECStatus
CERT_GetExtenCriticality(CERTCertExtension **extensions, int tag,
PRBool *isCritical)
{
CERTCertExtension *ext;
SECOidData *oid;
if (!isCritical)
return (SECSuccess);
/* find the extension in the extensions list */
oid = SECOID_FindOIDByTag((SECOidTag)tag);
if (!oid) {
return (SECFailure);
}
ext = GetExtension(extensions, &oid->oid);
if (ext == NULL) {
PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND);
return (SECFailure);
}
/* If the criticality is omitted, then it is false by default.
ex->critical.data is NULL */
if (ext->critical.data == NULL)
*isCritical = PR_FALSE;
else
*isCritical = (ext->critical.data[0] == 0xff) ? PR_TRUE : PR_FALSE;
return (SECSuccess);
}
SECStatus
cert_FindExtension(CERTCertExtension **extensions, int tag, SECItem *value)
{
SECOidData *oid;
oid = SECOID_FindOIDByTag((SECOidTag)tag);
if (!oid) {
return (SECFailure);
}
return (cert_FindExtensionByOID(extensions, &oid->oid, value));
}
typedef struct _extNode {
struct _extNode *next;
CERTCertExtension *ext;
} extNode;
typedef struct {
void (*setExts)(void *object, CERTCertExtension **exts);
void *object;
PLArenaPool *ownerArena;
PLArenaPool *arena;
extNode *head;
int count;
} extRec;
/*
* cert_StartExtensions
*
* NOTE: This interface changed significantly to remove knowledge
* about callers data structures (owner objects)
*/
void *
cert_StartExtensions(void *owner, PLArenaPool *ownerArena,
void (*setExts)(void *object, CERTCertExtension **exts))
{
PLArenaPool *arena;
extRec *handle;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) {
return (0);
}
handle = (extRec *)PORT_ArenaAlloc(arena, sizeof(extRec));
if (!handle) {
PORT_FreeArena(arena, PR_FALSE);
return (0);
}
handle->object = owner;
handle->ownerArena = ownerArena;
handle->setExts = setExts;
handle->arena = arena;
handle->head = 0;
handle->count = 0;
return (handle);
}
static unsigned char hextrue = 0xff;
/*
* Note - assumes that data pointed to by oid->data will not move
*/
SECStatus
CERT_AddExtensionByOID(void *exthandle, SECItem *oid, SECItem *value,
PRBool critical, PRBool copyData)
{
CERTCertExtension *ext;
SECStatus rv;
extNode *node;
extRec *handle;
handle = (extRec *)exthandle;
/* allocate space for extension and list node */
ext = (CERTCertExtension *)PORT_ArenaZAlloc(handle->ownerArena,
sizeof(CERTCertExtension));
if (!ext) {
return (SECFailure);
}
node = (extNode *)PORT_ArenaAlloc(handle->arena, sizeof(extNode));
if (!node) {
return (SECFailure);
}
/* add to list */
node->next = handle->head;
handle->head = node;
/* point to ext struct */
node->ext = ext;
/* set critical field */
if (critical) {
ext->critical.data = (unsigned char *)&hextrue;
ext->critical.len = 1;
}
/* set object ID of the extension and its value */
if (copyData) {
rv = SECITEM_CopyItem(handle->ownerArena, &ext->id, oid);
if (rv) {
return (SECFailure);
}
rv = SECITEM_CopyItem(handle->ownerArena, &ext->value, value);
if (rv) {
return (SECFailure);
}
} else {
ext->id = *oid;
ext->value = *value;
}
handle->count++;
return (SECSuccess);
}
SECStatus
CERT_AddExtension(void *exthandle, int idtag, SECItem *value, PRBool critical,
PRBool copyData)
{
SECOidData *oid;
oid = SECOID_FindOIDByTag((SECOidTag)idtag);
if (!oid) {
return (SECFailure);
}
return (CERT_AddExtensionByOID(exthandle, &oid->oid, value, critical,
copyData));
}
SECStatus
CERT_EncodeAndAddExtension(void *exthandle, int idtag, void *value,
PRBool critical, const SEC_ASN1Template *atemplate)
{
extRec *handle;
SECItem *encitem;
handle = (extRec *)exthandle;
encitem = SEC_ASN1EncodeItem(handle->ownerArena, NULL, value, atemplate);
if (encitem == NULL) {
return (SECFailure);
}
return CERT_AddExtension(exthandle, idtag, encitem, critical, PR_FALSE);
}
void
PrepareBitStringForEncoding(SECItem *bitsmap, SECItem *value)
{
unsigned char onebyte;
unsigned int i, len = 0;
/* to prevent warning on some platform at compile time */
onebyte = '\0';
/* Get the position of the right-most turn-on bit */
for (i = 0; i < (value->len) * 8; ++i) {
if (i % 8 == 0)
onebyte = value->data[i / 8];
if (onebyte & 0x80)
len = i;
onebyte <<= 1;
}
bitsmap->data = value->data;
/* Add one here since we work with base 1 */
bitsmap->len = len + 1;
}
SECStatus
CERT_EncodeAndAddBitStrExtension(void *exthandle, int idtag, SECItem *value,
PRBool critical)
{
SECItem bitsmap;
PrepareBitStringForEncoding(&bitsmap, value);
return (CERT_EncodeAndAddExtension(exthandle, idtag, &bitsmap, critical,
SEC_ASN1_GET(SEC_BitStringTemplate)));
}
SECStatus
CERT_FinishExtensions(void *exthandle)
{
extRec *handle;
extNode *node;
CERTCertExtension **exts;
SECStatus rv = SECFailure;
handle = (extRec *)exthandle;
/* allocate space for extensions array */
exts = PORT_ArenaNewArray(handle->ownerArena, CERTCertExtension *,
handle->count + 1);
if (exts == NULL) {
goto loser;
}
/* put extensions in owner object and update its version number */
#ifdef OLD
switch (handle->type) {
case CertificateExtensions:
handle->owner.cert->extensions = exts;
DER_SetUInteger(ownerArena, &(handle->owner.cert->version),
SEC_CERTIFICATE_VERSION_3);
break;
case CrlExtensions:
handle->owner.crl->extensions = exts;
DER_SetUInteger(ownerArena, &(handle->owner.crl->version),
SEC_CRL_VERSION_2);
break;
case OCSPRequestExtensions:
handle->owner.request->tbsRequest->requestExtensions = exts;
break;
case OCSPSingleRequestExtensions:
handle->owner.singleRequest->singleRequestExtensions = exts;
break;
case OCSPResponseSingleExtensions:
handle->owner.singleResponse->singleExtensions = exts;
break;
}
#endif
handle->setExts(handle->object, exts);
/* update the version number */
/* copy each extension pointer */
node = handle->head;
while (node) {
*exts = node->ext;
node = node->next;
exts++;
}
/* terminate the array of extensions */
*exts = 0;
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;
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 };
SECStatus rv;
PORTCheapArenaPool tmpArena;
wrapperItem.data = NULL;
tmpItem.data = NULL;
PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
rv = cert_FindExtension(extensions, tag, &wrapperItem);
if (rv != SECSuccess) {
goto loser;
}
rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &tmpItem,
SEC_ASN1_GET(SEC_BitStringTemplate),
&wrapperItem);
if (rv != SECSuccess) {
goto loser;
}
retItem->data = (unsigned char *)PORT_ZAlloc((tmpItem.len + 7) >> 3);
if (retItem->data == NULL) {
goto loser;
}
if (tmpItem.len > 0) {
PORT_Memcpy(retItem->data, tmpItem.data, (tmpItem.len + 7) >> 3);
}
retItem->len = tmpItem.len;
rv = SECSuccess;
goto done;
loser:
rv = SECFailure;
done:
PORT_DestroyCheapArena(&tmpArena);
if (wrapperItem.data) {
PORT_Free(wrapperItem.data);
}
return (rv);
}
PRBool
cert_HasCriticalExtension(CERTCertExtension **extensions)
{
CERTCertExtension **exts;
CERTCertExtension *ext = NULL;
PRBool hasCriticalExten = PR_FALSE;
exts = extensions;
if (exts) {
while (*exts) {
ext = *exts;
/* If the criticality is omitted, it's non-critical */
if (ext->critical.data && ext->critical.data[0] == 0xff) {
hasCriticalExten = PR_TRUE;
break;
}
exts++;
}
}
return (hasCriticalExten);
}
PRBool
cert_HasUnknownCriticalExten(CERTCertExtension **extensions)
{
CERTCertExtension **exts;
CERTCertExtension *ext = NULL;
PRBool hasUnknownCriticalExten = PR_FALSE;
exts = extensions;
if (exts) {
while (*exts) {
ext = *exts;
/* If the criticality is omitted, it's non-critical.
If an extension is critical, make sure that we know
how to process the extension.
*/
if (ext->critical.data && ext->critical.data[0] == 0xff) {
if (SECOID_KnownCertExtenOID(&ext->id) == PR_FALSE) {
hasUnknownCriticalExten = PR_TRUE;
break;
}
}
exts++;
}
}
return (hasUnknownCriticalExten);
}