diff options
Diffstat (limited to 'security/nss/lib/certdb/certxutl.c')
-rw-r--r-- | security/nss/lib/certdb/certxutl.c | 493 |
1 files changed, 493 insertions, 0 deletions
diff --git a/security/nss/lib/certdb/certxutl.c b/security/nss/lib/certdb/certxutl.c new file mode 100644 index 000000000..c53f15cdf --- /dev/null +++ b/security/nss/lib/certdb/certxutl.c @@ -0,0 +1,493 @@ +/* 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/. */ + +/* + * 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_Alloc((tmpItem.len + 7) >> 3); + if (retItem->data == NULL) { + goto loser; + } + + 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); +} |