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