tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

certxutl.c (12608B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /*
      6 * Certificate Extensions handling code
      7 *
      8 */
      9 
     10 #include "cert.h"
     11 #include "secitem.h"
     12 #include "secoid.h"
     13 #include "secder.h"
     14 #include "secasn1.h"
     15 #include "certxutl.h"
     16 #include "secerr.h"
     17 
     18 #ifdef OLD
     19 #include "ocspti.h" /* XXX a better extensions interface would not
     20 		 * require knowledge of data structures of callers */
     21 #endif
     22 
     23 static CERTCertExtension *
     24 GetExtension(CERTCertExtension **extensions, SECItem *oid)
     25 {
     26    CERTCertExtension **exts;
     27    CERTCertExtension *ext = NULL;
     28    SECComparison comp;
     29 
     30    exts = extensions;
     31 
     32    if (exts) {
     33        while (*exts) {
     34            ext = *exts;
     35            comp = SECITEM_CompareItem(oid, &ext->id);
     36            if (comp == SECEqual)
     37                break;
     38 
     39            exts++;
     40        }
     41        return (*exts ? ext : NULL);
     42    }
     43    return (NULL);
     44 }
     45 
     46 SECStatus
     47 cert_FindExtensionByOID(CERTCertExtension **extensions, SECItem *oid,
     48                        SECItem *value)
     49 {
     50    CERTCertExtension *ext;
     51    SECStatus rv = SECSuccess;
     52 
     53    ext = GetExtension(extensions, oid);
     54    if (ext == NULL) {
     55        PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND);
     56        return (SECFailure);
     57    }
     58    if (value)
     59        rv = SECITEM_CopyItem(NULL, value, &ext->value);
     60    return (rv);
     61 }
     62 
     63 SECStatus
     64 CERT_GetExtenCriticality(CERTCertExtension **extensions, int tag,
     65                         PRBool *isCritical)
     66 {
     67    CERTCertExtension *ext;
     68    SECOidData *oid;
     69 
     70    if (!isCritical)
     71        return (SECSuccess);
     72 
     73    /* find the extension in the extensions list */
     74    oid = SECOID_FindOIDByTag((SECOidTag)tag);
     75    if (!oid) {
     76        return (SECFailure);
     77    }
     78    ext = GetExtension(extensions, &oid->oid);
     79    if (ext == NULL) {
     80        PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND);
     81        return (SECFailure);
     82    }
     83 
     84    /* If the criticality is omitted, then it is false by default.
     85       ex->critical.data is NULL */
     86    if (ext->critical.data == NULL)
     87        *isCritical = PR_FALSE;
     88    else
     89        *isCritical = (ext->critical.data[0] == 0xff) ? PR_TRUE : PR_FALSE;
     90    return (SECSuccess);
     91 }
     92 
     93 SECStatus
     94 cert_FindExtension(CERTCertExtension **extensions, int tag, SECItem *value)
     95 {
     96    SECOidData *oid;
     97 
     98    oid = SECOID_FindOIDByTag((SECOidTag)tag);
     99    if (!oid) {
    100        return (SECFailure);
    101    }
    102 
    103    return (cert_FindExtensionByOID(extensions, &oid->oid, value));
    104 }
    105 
    106 typedef struct _extNode {
    107    struct _extNode *next;
    108    CERTCertExtension *ext;
    109 } extNode;
    110 
    111 typedef struct {
    112    void (*setExts)(void *object, CERTCertExtension **exts);
    113    void *object;
    114    PLArenaPool *ownerArena;
    115    PLArenaPool *arena;
    116    extNode *head;
    117    int count;
    118 } extRec;
    119 
    120 /*
    121 * cert_StartExtensions
    122 *
    123 * NOTE: This interface changed significantly to remove knowledge
    124 *   about callers data structures (owner objects)
    125 */
    126 void *
    127 cert_StartExtensions(void *owner, PLArenaPool *ownerArena,
    128                     void (*setExts)(void *object, CERTCertExtension **exts))
    129 {
    130    PLArenaPool *arena;
    131    extRec *handle;
    132 
    133    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    134    if (!arena) {
    135        return (0);
    136    }
    137 
    138    handle = (extRec *)PORT_ArenaAlloc(arena, sizeof(extRec));
    139    if (!handle) {
    140        PORT_FreeArena(arena, PR_FALSE);
    141        return (0);
    142    }
    143 
    144    handle->object = owner;
    145    handle->ownerArena = ownerArena;
    146    handle->setExts = setExts;
    147 
    148    handle->arena = arena;
    149    handle->head = 0;
    150    handle->count = 0;
    151 
    152    return (handle);
    153 }
    154 
    155 static unsigned char hextrue = 0xff;
    156 
    157 /*
    158 * Note - assumes that data pointed to by oid->data will not move
    159 */
    160 SECStatus
    161 CERT_AddExtensionByOID(void *exthandle, SECItem *oid, SECItem *value,
    162                       PRBool critical, PRBool copyData)
    163 {
    164    CERTCertExtension *ext;
    165    SECStatus rv;
    166    extNode *node;
    167    extRec *handle;
    168 
    169    handle = (extRec *)exthandle;
    170 
    171    /* allocate space for extension and list node */
    172    ext = (CERTCertExtension *)PORT_ArenaZAlloc(handle->ownerArena,
    173                                                sizeof(CERTCertExtension));
    174    if (!ext) {
    175        return (SECFailure);
    176    }
    177 
    178    node = (extNode *)PORT_ArenaAlloc(handle->arena, sizeof(extNode));
    179    if (!node) {
    180        return (SECFailure);
    181    }
    182 
    183    /* add to list */
    184    node->next = handle->head;
    185    handle->head = node;
    186 
    187    /* point to ext struct */
    188    node->ext = ext;
    189 
    190    /* set critical field */
    191    if (critical) {
    192        ext->critical.data = (unsigned char *)&hextrue;
    193        ext->critical.len = 1;
    194    }
    195 
    196    /* set object ID of the extension and its value */
    197    if (copyData) {
    198        rv = SECITEM_CopyItem(handle->ownerArena, &ext->id, oid);
    199        if (rv) {
    200            return (SECFailure);
    201        }
    202 
    203        rv = SECITEM_CopyItem(handle->ownerArena, &ext->value, value);
    204        if (rv) {
    205            return (SECFailure);
    206        }
    207    } else {
    208        ext->id = *oid;
    209        ext->value = *value;
    210    }
    211 
    212    handle->count++;
    213 
    214    return (SECSuccess);
    215 }
    216 
    217 SECStatus
    218 CERT_AddExtension(void *exthandle, int idtag, SECItem *value, PRBool critical,
    219                  PRBool copyData)
    220 {
    221    SECOidData *oid;
    222 
    223    oid = SECOID_FindOIDByTag((SECOidTag)idtag);
    224    if (!oid) {
    225        return (SECFailure);
    226    }
    227 
    228    return (CERT_AddExtensionByOID(exthandle, &oid->oid, value, critical,
    229                                   copyData));
    230 }
    231 
    232 SECStatus
    233 CERT_EncodeAndAddExtension(void *exthandle, int idtag, void *value,
    234                           PRBool critical, const SEC_ASN1Template *atemplate)
    235 {
    236    extRec *handle;
    237    SECItem *encitem;
    238 
    239    handle = (extRec *)exthandle;
    240 
    241    encitem = SEC_ASN1EncodeItem(handle->ownerArena, NULL, value, atemplate);
    242    if (encitem == NULL) {
    243        return (SECFailure);
    244    }
    245 
    246    return CERT_AddExtension(exthandle, idtag, encitem, critical, PR_FALSE);
    247 }
    248 
    249 void
    250 PrepareBitStringForEncoding(SECItem *bitsmap, SECItem *value)
    251 {
    252    unsigned char onebyte;
    253    unsigned int i, len = 0;
    254 
    255    /* to prevent warning on some platform at compile time */
    256    onebyte = '\0';
    257    /* Get the position of the right-most turn-on bit */
    258    for (i = 0; i < (value->len) * 8; ++i) {
    259        if (i % 8 == 0)
    260            onebyte = value->data[i / 8];
    261        if (onebyte & 0x80)
    262            len = i;
    263        onebyte <<= 1;
    264    }
    265    bitsmap->data = value->data;
    266    /* Add one here since we work with base 1 */
    267    bitsmap->len = len + 1;
    268 }
    269 
    270 SECStatus
    271 CERT_EncodeAndAddBitStrExtension(void *exthandle, int idtag, SECItem *value,
    272                                 PRBool critical)
    273 {
    274    SECItem bitsmap;
    275 
    276    PrepareBitStringForEncoding(&bitsmap, value);
    277    return (CERT_EncodeAndAddExtension(exthandle, idtag, &bitsmap, critical,
    278                                       SEC_ASN1_GET(SEC_BitStringTemplate)));
    279 }
    280 
    281 SECStatus
    282 CERT_FinishExtensions(void *exthandle)
    283 {
    284    extRec *handle;
    285    extNode *node;
    286    CERTCertExtension **exts;
    287    SECStatus rv = SECFailure;
    288 
    289    handle = (extRec *)exthandle;
    290 
    291    /* allocate space for extensions array */
    292    exts = PORT_ArenaNewArray(handle->ownerArena, CERTCertExtension *,
    293                              handle->count + 1);
    294    if (exts == NULL) {
    295        goto loser;
    296    }
    297 
    298    /* put extensions in owner object and update its version number */
    299 
    300 #ifdef OLD
    301    switch (handle->type) {
    302        case CertificateExtensions:
    303            handle->owner.cert->extensions = exts;
    304            DER_SetUInteger(ownerArena, &(handle->owner.cert->version),
    305                            SEC_CERTIFICATE_VERSION_3);
    306            break;
    307        case CrlExtensions:
    308            handle->owner.crl->extensions = exts;
    309            DER_SetUInteger(ownerArena, &(handle->owner.crl->version),
    310                            SEC_CRL_VERSION_2);
    311            break;
    312        case OCSPRequestExtensions:
    313            handle->owner.request->tbsRequest->requestExtensions = exts;
    314            break;
    315        case OCSPSingleRequestExtensions:
    316            handle->owner.singleRequest->singleRequestExtensions = exts;
    317            break;
    318        case OCSPResponseSingleExtensions:
    319            handle->owner.singleResponse->singleExtensions = exts;
    320            break;
    321    }
    322 #endif
    323 
    324    handle->setExts(handle->object, exts);
    325 
    326    /* update the version number */
    327 
    328    /* copy each extension pointer */
    329    node = handle->head;
    330    while (node) {
    331        *exts = node->ext;
    332 
    333        node = node->next;
    334        exts++;
    335    }
    336 
    337    /* terminate the array of extensions */
    338    *exts = 0;
    339 
    340    rv = SECSuccess;
    341 
    342 loser:
    343    /* free working arena */
    344    PORT_FreeArena(handle->arena, PR_FALSE);
    345    return rv;
    346 }
    347 
    348 SECStatus
    349 CERT_MergeExtensions(void *exthandle, CERTCertExtension **extensions)
    350 {
    351    CERTCertExtension *ext;
    352    SECStatus rv = SECSuccess;
    353    SECOidTag tag;
    354    extNode *node;
    355    extRec *handle = exthandle;
    356 
    357    if (!exthandle || !extensions) {
    358        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    359        return SECFailure;
    360    }
    361    while ((ext = *extensions++) != NULL) {
    362        tag = SECOID_FindOIDTag(&ext->id);
    363        for (node = handle->head; node != NULL; node = node->next) {
    364            if (tag == 0) {
    365                if (SECITEM_ItemsAreEqual(&ext->id, &node->ext->id))
    366                    break;
    367            } else {
    368                if (SECOID_FindOIDTag(&node->ext->id) == tag) {
    369                    break;
    370                }
    371            }
    372        }
    373        if (node == NULL) {
    374            PRBool critical = (ext->critical.len != 0 &&
    375                               ext->critical.data[ext->critical.len - 1] != 0);
    376            if (critical && tag == SEC_OID_UNKNOWN) {
    377                PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
    378                rv = SECFailure;
    379                break;
    380            }
    381            /* add to list */
    382            rv = CERT_AddExtensionByOID(exthandle, &ext->id, &ext->value,
    383                                        critical, PR_TRUE);
    384            if (rv != SECSuccess)
    385                break;
    386        }
    387    }
    388    return rv;
    389 }
    390 
    391 /*
    392 * get the value of the Netscape Certificate Type Extension
    393 */
    394 SECStatus
    395 CERT_FindBitStringExtension(CERTCertExtension **extensions, int tag,
    396                            SECItem *retItem)
    397 {
    398    SECItem wrapperItem, tmpItem = { siBuffer, 0 };
    399    SECStatus rv;
    400    PORTCheapArenaPool tmpArena;
    401 
    402    wrapperItem.data = NULL;
    403    tmpItem.data = NULL;
    404 
    405    PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
    406 
    407    rv = cert_FindExtension(extensions, tag, &wrapperItem);
    408    if (rv != SECSuccess) {
    409        goto loser;
    410    }
    411 
    412    rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &tmpItem,
    413                                SEC_ASN1_GET(SEC_BitStringTemplate),
    414                                &wrapperItem);
    415 
    416    if (rv != SECSuccess) {
    417        goto loser;
    418    }
    419 
    420    retItem->data = (unsigned char *)PORT_ZAlloc((tmpItem.len + 7) >> 3);
    421    if (retItem->data == NULL) {
    422        goto loser;
    423    }
    424 
    425    if (tmpItem.len > 0) {
    426        PORT_Memcpy(retItem->data, tmpItem.data, (tmpItem.len + 7) >> 3);
    427    }
    428 
    429    retItem->len = tmpItem.len;
    430 
    431    rv = SECSuccess;
    432    goto done;
    433 
    434 loser:
    435    rv = SECFailure;
    436 
    437 done:
    438    PORT_DestroyCheapArena(&tmpArena);
    439 
    440    if (wrapperItem.data) {
    441        PORT_Free(wrapperItem.data);
    442    }
    443 
    444    return (rv);
    445 }
    446 
    447 PRBool
    448 cert_HasCriticalExtension(CERTCertExtension **extensions)
    449 {
    450    CERTCertExtension **exts;
    451    CERTCertExtension *ext = NULL;
    452    PRBool hasCriticalExten = PR_FALSE;
    453 
    454    exts = extensions;
    455 
    456    if (exts) {
    457        while (*exts) {
    458            ext = *exts;
    459            /* If the criticality is omitted, it's non-critical */
    460            if (ext->critical.data && ext->critical.data[0] == 0xff) {
    461                hasCriticalExten = PR_TRUE;
    462                break;
    463            }
    464            exts++;
    465        }
    466    }
    467    return (hasCriticalExten);
    468 }
    469 
    470 PRBool
    471 cert_HasUnknownCriticalExten(CERTCertExtension **extensions)
    472 {
    473    CERTCertExtension **exts;
    474    CERTCertExtension *ext = NULL;
    475    PRBool hasUnknownCriticalExten = PR_FALSE;
    476 
    477    exts = extensions;
    478 
    479    if (exts) {
    480        while (*exts) {
    481            ext = *exts;
    482            /* If the criticality is omitted, it's non-critical.
    483               If an extension is critical, make sure that we know
    484               how to process the extension.
    485             */
    486            if (ext->critical.data && ext->critical.data[0] == 0xff) {
    487                if (SECOID_KnownCertExtenOID(&ext->id) == PR_FALSE) {
    488                    hasUnknownCriticalExten = PR_TRUE;
    489                    break;
    490                }
    491            }
    492            exts++;
    493        }
    494    }
    495    return (hasUnknownCriticalExten);
    496 }