tor-browser

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

cmsattr.c (12053B)


      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 * CMS attributes.
      7 */
      8 
      9 #include "cmslocal.h"
     10 
     11 #include "secasn1.h"
     12 #include "secitem.h"
     13 #include "secoid.h"
     14 #include "pk11func.h"
     15 #include "prtime.h"
     16 #include "secerr.h"
     17 
     18 /*
     19 * -------------------------------------------------------------------
     20 * XXX The following Attribute stuff really belongs elsewhere.
     21 * The Attribute type is *not* part of CMS but rather X.501.
     22 * But for now, since CMS is the only customer of attributes,
     23 * we define them here.  Once there is a use outside of CMS,
     24 * then change the attribute types and functions from internal
     25 * to external naming convention, and move them elsewhere!
     26 */
     27 
     28 /*
     29 * NSS_CMSAttribute_Create - create an attribute
     30 *
     31 * if value is NULL, the attribute won't have a value. It can be added later
     32 * with NSS_CMSAttribute_AddValue.
     33 */
     34 NSSCMSAttribute *
     35 NSS_CMSAttribute_Create(PLArenaPool *poolp, SECOidTag oidtag, SECItem *value,
     36                        PRBool encoded)
     37 {
     38    NSSCMSAttribute *attr;
     39    SECItem *copiedvalue;
     40    void *mark;
     41 
     42    PORT_Assert(poolp != NULL);
     43 
     44    mark = PORT_ArenaMark(poolp);
     45 
     46    attr = (NSSCMSAttribute *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSAttribute));
     47    if (attr == NULL)
     48        goto loser;
     49 
     50    attr->typeTag = SECOID_FindOIDByTag(oidtag);
     51    if (attr->typeTag == NULL)
     52        goto loser;
     53 
     54    if (SECITEM_CopyItem(poolp, &(attr->type), &(attr->typeTag->oid)) != SECSuccess)
     55        goto loser;
     56 
     57    if (value != NULL) {
     58        if ((copiedvalue = SECITEM_ArenaDupItem(poolp, value)) == NULL)
     59            goto loser;
     60 
     61        if (NSS_CMSArray_Add(poolp, (void ***)&(attr->values), (void *)copiedvalue) != SECSuccess)
     62            goto loser;
     63    }
     64 
     65    attr->encoded = encoded;
     66 
     67    PORT_ArenaUnmark(poolp, mark);
     68 
     69    return attr;
     70 
     71 loser:
     72    PORT_Assert(mark != NULL);
     73    PORT_ArenaRelease(poolp, mark);
     74    return NULL;
     75 }
     76 
     77 /*
     78 * NSS_CMSAttribute_AddValue - add another value to an attribute
     79 */
     80 SECStatus
     81 NSS_CMSAttribute_AddValue(PLArenaPool *poolp, NSSCMSAttribute *attr, SECItem *value)
     82 {
     83    SECItem *copiedvalue;
     84    void *mark;
     85 
     86    PORT_Assert(poolp != NULL);
     87 
     88    mark = PORT_ArenaMark(poolp);
     89 
     90    if (value == NULL) {
     91        PORT_SetError(SEC_ERROR_INVALID_ARGS);
     92        goto loser;
     93    }
     94 
     95    if ((copiedvalue = SECITEM_ArenaDupItem(poolp, value)) == NULL)
     96        goto loser;
     97 
     98    if (NSS_CMSArray_Add(poolp, (void ***)&(attr->values), (void *)copiedvalue) != SECSuccess)
     99        goto loser;
    100 
    101    PORT_ArenaUnmark(poolp, mark);
    102    return SECSuccess;
    103 
    104 loser:
    105    PORT_Assert(mark != NULL);
    106    PORT_ArenaRelease(poolp, mark);
    107    return SECFailure;
    108 }
    109 
    110 /*
    111 * NSS_CMSAttribute_GetType - return the OID tag
    112 */
    113 SECOidTag
    114 NSS_CMSAttribute_GetType(NSSCMSAttribute *attr)
    115 {
    116    SECOidData *typetag;
    117 
    118    typetag = SECOID_FindOID(&(attr->type));
    119    if (typetag == NULL)
    120        return SEC_OID_UNKNOWN;
    121 
    122    return typetag->offset;
    123 }
    124 
    125 /*
    126 * NSS_CMSAttribute_GetValue - return the first attribute value
    127 *
    128 * We do some sanity checking first:
    129 * - Multiple values are *not* expected.
    130 * - Empty values are *not* expected.
    131 */
    132 SECItem *
    133 NSS_CMSAttribute_GetValue(NSSCMSAttribute *attr)
    134 {
    135    SECItem *value;
    136 
    137    if (attr == NULL)
    138        return NULL;
    139 
    140    value = attr->values[0];
    141 
    142    if (value == NULL || value->data == NULL || value->len == 0)
    143        return NULL;
    144 
    145    if (attr->values[1] != NULL)
    146        return NULL;
    147 
    148    return value;
    149 }
    150 
    151 /*
    152 * NSS_CMSAttribute_CompareValue - compare the attribute's first value against data
    153 */
    154 PRBool
    155 NSS_CMSAttribute_CompareValue(NSSCMSAttribute *attr, SECItem *av)
    156 {
    157    SECItem *value;
    158 
    159    if (attr == NULL)
    160        return PR_FALSE;
    161 
    162    value = NSS_CMSAttribute_GetValue(attr);
    163 
    164    return (value != NULL && value->len == av->len &&
    165            PORT_Memcmp(value->data, av->data, value->len) == 0);
    166 }
    167 
    168 /*
    169 * templates and functions for separate ASN.1 encoding of attributes
    170 *
    171 * used in NSS_CMSAttributeArray_Reorder
    172 */
    173 
    174 /*
    175 * helper function for dynamic template determination of the attribute value
    176 */
    177 static const SEC_ASN1Template *
    178 cms_attr_choose_attr_value_template(void *src_or_dest, PRBool encoding)
    179 {
    180    const SEC_ASN1Template *theTemplate;
    181    NSSCMSAttribute *attribute;
    182    SECOidData *oiddata;
    183    PRBool encoded;
    184 
    185    PORT_Assert(src_or_dest != NULL);
    186    if (src_or_dest == NULL)
    187        return NULL;
    188 
    189    attribute = (NSSCMSAttribute *)src_or_dest;
    190 
    191    if (encoding && (!attribute->values || !attribute->values[0] ||
    192                     attribute->encoded)) {
    193        /* we're encoding, and the attribute has no value or the attribute
    194         * value is already encoded. */
    195        return SEC_ASN1_GET(SEC_AnyTemplate);
    196    }
    197 
    198    /* get attribute's typeTag */
    199    oiddata = attribute->typeTag;
    200    if (oiddata == NULL) {
    201        oiddata = SECOID_FindOID(&attribute->type);
    202        attribute->typeTag = oiddata;
    203    }
    204 
    205    if (oiddata == NULL) {
    206        /* still no OID tag? OID is unknown then. en/decode value as ANY. */
    207        encoded = PR_TRUE;
    208        theTemplate = SEC_ASN1_GET(SEC_AnyTemplate);
    209    } else {
    210        switch (oiddata->offset) {
    211            case SEC_OID_PKCS9_SMIME_CAPABILITIES:
    212            case SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE:
    213            /* these guys need to stay DER-encoded */
    214            default:
    215                /* same goes for OIDs that are not handled here */
    216                encoded = PR_TRUE;
    217                theTemplate = SEC_ASN1_GET(SEC_AnyTemplate);
    218                break;
    219            /* otherwise choose proper template */
    220            case SEC_OID_PKCS9_EMAIL_ADDRESS:
    221            case SEC_OID_RFC1274_MAIL:
    222            case SEC_OID_PKCS9_UNSTRUCTURED_NAME:
    223                encoded = PR_FALSE;
    224                theTemplate = SEC_ASN1_GET(SEC_IA5StringTemplate);
    225                break;
    226            case SEC_OID_PKCS9_CONTENT_TYPE:
    227                encoded = PR_FALSE;
    228                theTemplate = SEC_ASN1_GET(SEC_ObjectIDTemplate);
    229                break;
    230            case SEC_OID_PKCS9_MESSAGE_DIGEST:
    231                encoded = PR_FALSE;
    232                theTemplate = SEC_ASN1_GET(SEC_OctetStringTemplate);
    233                break;
    234            case SEC_OID_PKCS9_SIGNING_TIME:
    235                encoded = PR_FALSE;
    236                theTemplate = SEC_ASN1_GET(CERT_TimeChoiceTemplate);
    237                break;
    238                /* XXX Want other types here, too */
    239        }
    240    }
    241 
    242    if (encoding) {
    243        /*
    244         * If we are encoding and we think we have an already-encoded value,
    245         * then the code which initialized this attribute should have set
    246         * the "encoded" property to true (and we would have returned early,
    247         * up above).  No devastating error, but that code should be fixed.
    248         * (It could indicate that the resulting encoded bytes are wrong.)
    249         */
    250        PORT_Assert(!encoded);
    251    } else {
    252        /*
    253         * We are decoding; record whether the resulting value is
    254         * still encoded or not.
    255         */
    256        attribute->encoded = encoded;
    257    }
    258    return theTemplate;
    259 }
    260 
    261 static const SEC_ASN1TemplateChooserPtr cms_attr_chooser = cms_attr_choose_attr_value_template;
    262 
    263 const SEC_ASN1Template nss_cms_attribute_template[] = {
    264    { SEC_ASN1_SEQUENCE,
    265      0, NULL, sizeof(NSSCMSAttribute) },
    266    { SEC_ASN1_OBJECT_ID,
    267      offsetof(NSSCMSAttribute, type) },
    268    { SEC_ASN1_DYNAMIC | SEC_ASN1_SET_OF,
    269      offsetof(NSSCMSAttribute, values),
    270      &cms_attr_chooser },
    271    { 0 }
    272 };
    273 
    274 const SEC_ASN1Template nss_cms_set_of_attribute_template[] = {
    275    { SEC_ASN1_SET_OF, 0, nss_cms_attribute_template },
    276 };
    277 
    278 /* =============================================================================
    279 * Attribute Array methods
    280 */
    281 
    282 /*
    283 * NSS_CMSAttributeArray_Encode - encode an Attribute array as SET OF Attributes
    284 *
    285 * If you are wondering why this routine does not reorder the attributes
    286 * first, and might be tempted to make it do so, see the comment by the
    287 * call to ReorderAttributes in cmsencode.c.  (Or, see who else calls this
    288 * and think long and hard about the implications of making it always
    289 * do the reordering.)
    290 */
    291 SECItem *
    292 NSS_CMSAttributeArray_Encode(PLArenaPool *poolp, NSSCMSAttribute ***attrs, SECItem *dest)
    293 {
    294    return SEC_ASN1EncodeItem(poolp, dest, (void *)attrs, nss_cms_set_of_attribute_template);
    295 }
    296 
    297 /*
    298 * NSS_CMSAttributeArray_Reorder - sort attribute array by attribute's DER encoding
    299 *
    300 * make sure that the order of the attributes guarantees valid DER (which must be
    301 * in lexigraphically ascending order for a SET OF); if reordering is necessary it
    302 * will be done in place (in attrs).
    303 */
    304 SECStatus
    305 NSS_CMSAttributeArray_Reorder(NSSCMSAttribute **attrs)
    306 {
    307    return NSS_CMSArray_SortByDER((void **)attrs, nss_cms_attribute_template, NULL);
    308 }
    309 
    310 /*
    311 * NSS_CMSAttributeArray_FindAttrByOidTag - look through a set of attributes and
    312 * find one that matches the specified object ID.
    313 *
    314 * If "only" is true, then make sure that there is not more than one attribute
    315 * of the same type.  Otherwise, just return the first one found. (XXX Does
    316 * anybody really want that first-found behavior?  It was like that when I found it...)
    317 */
    318 NSSCMSAttribute *
    319 NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute **attrs, SECOidTag oidtag, PRBool only)
    320 {
    321    SECOidData *oid;
    322    NSSCMSAttribute *attr1, *attr2;
    323 
    324    if (attrs == NULL)
    325        return NULL;
    326 
    327    oid = SECOID_FindOIDByTag(oidtag);
    328    if (oid == NULL)
    329        return NULL;
    330 
    331    while ((attr1 = *attrs++) != NULL) {
    332        if (attr1->type.len == oid->oid.len &&
    333            PORT_Memcmp(attr1->type.data, oid->oid.data, oid->oid.len) == 0)
    334            break;
    335    }
    336 
    337    if (attr1 == NULL)
    338        return NULL;
    339 
    340    if (!only)
    341        return attr1;
    342 
    343    while ((attr2 = *attrs++) != NULL) {
    344        if (attr2->type.len == oid->oid.len &&
    345            PORT_Memcmp(attr2->type.data, oid->oid.data, oid->oid.len) == 0)
    346            break;
    347    }
    348 
    349    if (attr2 != NULL)
    350        return NULL;
    351 
    352    return attr1;
    353 }
    354 
    355 /*
    356 * NSS_CMSAttributeArray_AddAttr - add an attribute to an
    357 * array of attributes.
    358 */
    359 SECStatus
    360 NSS_CMSAttributeArray_AddAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs,
    361                              NSSCMSAttribute *attr)
    362 {
    363    NSSCMSAttribute *oattr;
    364    void *mark;
    365    SECOidTag type;
    366 
    367    mark = PORT_ArenaMark(poolp);
    368 
    369    /* find oidtag of attr */
    370    type = NSS_CMSAttribute_GetType(attr);
    371 
    372    /* see if we have one already */
    373    oattr = NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE);
    374    PORT_Assert(oattr == NULL);
    375    if (oattr != NULL)
    376        goto loser; /* XXX or would it be better to replace it? */
    377 
    378    /* no, shove it in */
    379    if (NSS_CMSArray_Add(poolp, (void ***)attrs, (void *)attr) != SECSuccess)
    380        goto loser;
    381 
    382    PORT_ArenaUnmark(poolp, mark);
    383    return SECSuccess;
    384 
    385 loser:
    386    PORT_ArenaRelease(poolp, mark);
    387    return SECFailure;
    388 }
    389 
    390 /*
    391 * NSS_CMSAttributeArray_SetAttr - set an attribute's value in a set of attributes
    392 */
    393 SECStatus
    394 NSS_CMSAttributeArray_SetAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs,
    395                              SECOidTag type, SECItem *value, PRBool encoded)
    396 {
    397    NSSCMSAttribute *attr;
    398    void *mark;
    399 
    400    mark = PORT_ArenaMark(poolp);
    401 
    402    /* see if we have one already */
    403    attr = NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE);
    404    if (attr == NULL) {
    405        /* not found? create one! */
    406        attr = NSS_CMSAttribute_Create(poolp, type, value, encoded);
    407        if (attr == NULL)
    408            goto loser;
    409        /* and add it to the list */
    410        if (NSS_CMSArray_Add(poolp, (void ***)attrs, (void *)attr) != SECSuccess)
    411            goto loser;
    412    } else {
    413        /* found, shove it in */
    414        /* XXX we need a decent memory model @#$#$!#!!! */
    415        attr->values[0] = value;
    416        attr->encoded = encoded;
    417    }
    418 
    419    PORT_ArenaUnmark(poolp, mark);
    420    return SECSuccess;
    421 
    422 loser:
    423    PORT_ArenaRelease(poolp, mark);
    424    return SECFailure;
    425 }