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