security/nss/lib/smime/cmsattr.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/nss/lib/smime/cmsattr.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,427 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +/*
     1.9 + * CMS attributes.
    1.10 + */
    1.11 +
    1.12 +#include "cmslocal.h"
    1.13 +
    1.14 +#include "secasn1.h"
    1.15 +#include "secitem.h"
    1.16 +#include "secoid.h"
    1.17 +#include "pk11func.h"
    1.18 +#include "prtime.h"
    1.19 +#include "secerr.h"
    1.20 +
    1.21 +/*
    1.22 + * -------------------------------------------------------------------
    1.23 + * XXX The following Attribute stuff really belongs elsewhere.
    1.24 + * The Attribute type is *not* part of CMS but rather X.501.
    1.25 + * But for now, since CMS is the only customer of attributes,
    1.26 + * we define them here.  Once there is a use outside of CMS,
    1.27 + * then change the attribute types and functions from internal
    1.28 + * to external naming convention, and move them elsewhere!
    1.29 + */
    1.30 +
    1.31 +
    1.32 +/*
    1.33 + * NSS_CMSAttribute_Create - create an attribute
    1.34 + *
    1.35 + * if value is NULL, the attribute won't have a value. It can be added later
    1.36 + * with NSS_CMSAttribute_AddValue.
    1.37 + */
    1.38 +NSSCMSAttribute *
    1.39 +NSS_CMSAttribute_Create(PLArenaPool *poolp, SECOidTag oidtag, SECItem *value, PRBool encoded)
    1.40 +{
    1.41 +    NSSCMSAttribute *attr;
    1.42 +    SECItem *copiedvalue;
    1.43 +    void *mark;
    1.44 +
    1.45 +    PORT_Assert (poolp != NULL);
    1.46 +
    1.47 +    mark = PORT_ArenaMark (poolp);
    1.48 +
    1.49 +    attr = (NSSCMSAttribute *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSAttribute));
    1.50 +    if (attr == NULL)
    1.51 +	goto loser;
    1.52 +
    1.53 +    attr->typeTag = SECOID_FindOIDByTag(oidtag);
    1.54 +    if (attr->typeTag == NULL)
    1.55 +	goto loser;
    1.56 +
    1.57 +    if (SECITEM_CopyItem(poolp, &(attr->type), &(attr->typeTag->oid)) != SECSuccess)
    1.58 +	goto loser;
    1.59 +
    1.60 +    if (value != NULL) {
    1.61 +	if ((copiedvalue = SECITEM_ArenaDupItem(poolp, value)) == NULL)
    1.62 +	    goto loser;
    1.63 +
    1.64 +	if (NSS_CMSArray_Add(poolp, (void ***)&(attr->values), (void *)copiedvalue) != SECSuccess)
    1.65 +	    goto loser;
    1.66 +    }
    1.67 +
    1.68 +    attr->encoded = encoded;
    1.69 +
    1.70 +    PORT_ArenaUnmark (poolp, mark);
    1.71 +
    1.72 +    return attr;
    1.73 +
    1.74 +loser:
    1.75 +    PORT_Assert (mark != NULL);
    1.76 +    PORT_ArenaRelease (poolp, mark);
    1.77 +    return NULL;
    1.78 +}
    1.79 +
    1.80 +/*
    1.81 + * NSS_CMSAttribute_AddValue - add another value to an attribute
    1.82 + */
    1.83 +SECStatus
    1.84 +NSS_CMSAttribute_AddValue(PLArenaPool *poolp, NSSCMSAttribute *attr, SECItem *value)
    1.85 +{
    1.86 +    SECItem *copiedvalue;
    1.87 +    void *mark;
    1.88 +
    1.89 +    PORT_Assert (poolp != NULL);
    1.90 +
    1.91 +    mark = PORT_ArenaMark(poolp);
    1.92 +
    1.93 +    if (value == NULL) {
    1.94 +	PORT_SetError(SEC_ERROR_INVALID_ARGS);
    1.95 +	goto loser;
    1.96 +    }
    1.97 +
    1.98 +    if ((copiedvalue = SECITEM_ArenaDupItem(poolp, value)) == NULL)
    1.99 +	goto loser;
   1.100 +
   1.101 +    if (NSS_CMSArray_Add(poolp, (void ***)&(attr->values), (void *)copiedvalue) != SECSuccess)
   1.102 +	goto loser;
   1.103 +
   1.104 +    PORT_ArenaUnmark(poolp, mark);
   1.105 +    return SECSuccess;
   1.106 +
   1.107 +loser:
   1.108 +    PORT_Assert (mark != NULL);
   1.109 +    PORT_ArenaRelease (poolp, mark);
   1.110 +    return SECFailure;
   1.111 +}
   1.112 +
   1.113 +/*
   1.114 + * NSS_CMSAttribute_GetType - return the OID tag
   1.115 + */
   1.116 +SECOidTag
   1.117 +NSS_CMSAttribute_GetType(NSSCMSAttribute *attr)
   1.118 +{
   1.119 +    SECOidData *typetag;
   1.120 +
   1.121 +    typetag = SECOID_FindOID(&(attr->type));
   1.122 +    if (typetag == NULL)
   1.123 +	return SEC_OID_UNKNOWN;
   1.124 +
   1.125 +    return typetag->offset;
   1.126 +}
   1.127 +
   1.128 +/*
   1.129 + * NSS_CMSAttribute_GetValue - return the first attribute value
   1.130 + *
   1.131 + * We do some sanity checking first:
   1.132 + * - Multiple values are *not* expected.
   1.133 + * - Empty values are *not* expected.
   1.134 + */
   1.135 +SECItem *
   1.136 +NSS_CMSAttribute_GetValue(NSSCMSAttribute *attr)
   1.137 +{
   1.138 +    SECItem *value;
   1.139 +
   1.140 +    if (attr == NULL)
   1.141 +	return NULL;
   1.142 +
   1.143 +    value = attr->values[0];
   1.144 +
   1.145 +    if (value == NULL || value->data == NULL || value->len == 0)
   1.146 +	return NULL;
   1.147 +
   1.148 +    if (attr->values[1] != NULL)
   1.149 +	return NULL;
   1.150 +
   1.151 +    return value;
   1.152 +}
   1.153 +
   1.154 +/*
   1.155 + * NSS_CMSAttribute_CompareValue - compare the attribute's first value against data
   1.156 + */
   1.157 +PRBool
   1.158 +NSS_CMSAttribute_CompareValue(NSSCMSAttribute *attr, SECItem *av)
   1.159 +{
   1.160 +    SECItem *value;
   1.161 +    
   1.162 +    if (attr == NULL)
   1.163 +	return PR_FALSE;
   1.164 +
   1.165 +    value = NSS_CMSAttribute_GetValue(attr);
   1.166 +
   1.167 +    return (value != NULL && value->len == av->len &&
   1.168 +	PORT_Memcmp (value->data, av->data, value->len) == 0);
   1.169 +}
   1.170 +
   1.171 +/*
   1.172 + * templates and functions for separate ASN.1 encoding of attributes
   1.173 + *
   1.174 + * used in NSS_CMSAttributeArray_Reorder
   1.175 + */
   1.176 +
   1.177 +/*
   1.178 + * helper function for dynamic template determination of the attribute value
   1.179 + */
   1.180 +static const SEC_ASN1Template *
   1.181 +cms_attr_choose_attr_value_template(void *src_or_dest, PRBool encoding)
   1.182 +{
   1.183 +    const SEC_ASN1Template *theTemplate;
   1.184 +    NSSCMSAttribute *attribute;
   1.185 +    SECOidData *oiddata;
   1.186 +    PRBool encoded;
   1.187 +
   1.188 +    PORT_Assert (src_or_dest != NULL);
   1.189 +    if (src_or_dest == NULL)
   1.190 +	return NULL;
   1.191 +
   1.192 +    attribute = (NSSCMSAttribute *)src_or_dest;
   1.193 +
   1.194 +    if (encoding && (!attribute->values || !attribute->values[0] ||
   1.195 +        attribute->encoded)) {
   1.196 +        /* we're encoding, and the attribute has no value or the attribute
   1.197 +         * value is already encoded. */
   1.198 +        return SEC_ASN1_GET(SEC_AnyTemplate);
   1.199 +    }
   1.200 +
   1.201 +    /* get attribute's typeTag */
   1.202 +    oiddata = attribute->typeTag;
   1.203 +    if (oiddata == NULL) {
   1.204 +	oiddata = SECOID_FindOID(&attribute->type);
   1.205 +	attribute->typeTag = oiddata;
   1.206 +    }
   1.207 +
   1.208 +    if (oiddata == NULL) {
   1.209 +	/* still no OID tag? OID is unknown then. en/decode value as ANY. */
   1.210 +	encoded = PR_TRUE;
   1.211 +	theTemplate = SEC_ASN1_GET(SEC_AnyTemplate);
   1.212 +    } else {
   1.213 +	switch (oiddata->offset) {
   1.214 +	case SEC_OID_PKCS9_SMIME_CAPABILITIES:
   1.215 +	case SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE:
   1.216 +	    /* these guys need to stay DER-encoded */
   1.217 +	default:
   1.218 +	    /* same goes for OIDs that are not handled here */
   1.219 +	    encoded = PR_TRUE;
   1.220 +	    theTemplate = SEC_ASN1_GET(SEC_AnyTemplate);
   1.221 +	    break;
   1.222 +	    /* otherwise choose proper template */
   1.223 +	case SEC_OID_PKCS9_EMAIL_ADDRESS:
   1.224 +	case SEC_OID_RFC1274_MAIL:
   1.225 +	case SEC_OID_PKCS9_UNSTRUCTURED_NAME:
   1.226 +	    encoded = PR_FALSE;
   1.227 +	    theTemplate = SEC_ASN1_GET(SEC_IA5StringTemplate);
   1.228 +	    break;
   1.229 +	case SEC_OID_PKCS9_CONTENT_TYPE:
   1.230 +	    encoded = PR_FALSE;
   1.231 +	    theTemplate = SEC_ASN1_GET(SEC_ObjectIDTemplate);
   1.232 +	    break;
   1.233 +	case SEC_OID_PKCS9_MESSAGE_DIGEST:
   1.234 +	    encoded = PR_FALSE;
   1.235 +	    theTemplate = SEC_ASN1_GET(SEC_OctetStringTemplate);
   1.236 +	    break;
   1.237 +	case SEC_OID_PKCS9_SIGNING_TIME:
   1.238 +	    encoded = PR_FALSE;
   1.239 +	    theTemplate = SEC_ASN1_GET(CERT_TimeChoiceTemplate);
   1.240 +	    break;
   1.241 +	  /* XXX Want other types here, too */
   1.242 +	}
   1.243 +    }
   1.244 +
   1.245 +    if (encoding) {
   1.246 +	/*
   1.247 +	 * If we are encoding and we think we have an already-encoded value,
   1.248 +	 * then the code which initialized this attribute should have set
   1.249 +	 * the "encoded" property to true (and we would have returned early,
   1.250 +	 * up above).  No devastating error, but that code should be fixed.
   1.251 +	 * (It could indicate that the resulting encoded bytes are wrong.)
   1.252 +	 */
   1.253 +	PORT_Assert (!encoded);
   1.254 +    } else {
   1.255 +	/*
   1.256 +	 * We are decoding; record whether the resulting value is
   1.257 +	 * still encoded or not.
   1.258 +	 */
   1.259 +	attribute->encoded = encoded;
   1.260 +    }
   1.261 +    return theTemplate;
   1.262 +}
   1.263 +
   1.264 +static const SEC_ASN1TemplateChooserPtr cms_attr_chooser
   1.265 +	= cms_attr_choose_attr_value_template;
   1.266 +
   1.267 +const SEC_ASN1Template nss_cms_attribute_template[] = {
   1.268 +    { SEC_ASN1_SEQUENCE,
   1.269 +	  0, NULL, sizeof(NSSCMSAttribute) },
   1.270 +    { SEC_ASN1_OBJECT_ID,
   1.271 +	  offsetof(NSSCMSAttribute,type) },
   1.272 +    { SEC_ASN1_DYNAMIC | SEC_ASN1_SET_OF,
   1.273 +	  offsetof(NSSCMSAttribute,values),
   1.274 +	  &cms_attr_chooser },
   1.275 +    { 0 }
   1.276 +};
   1.277 +
   1.278 +const SEC_ASN1Template nss_cms_set_of_attribute_template[] = {
   1.279 +    { SEC_ASN1_SET_OF, 0, nss_cms_attribute_template },
   1.280 +};
   1.281 +
   1.282 +/* =============================================================================
   1.283 + * Attribute Array methods
   1.284 + */
   1.285 +
   1.286 +/*
   1.287 + * NSS_CMSAttributeArray_Encode - encode an Attribute array as SET OF Attributes
   1.288 + *
   1.289 + * If you are wondering why this routine does not reorder the attributes
   1.290 + * first, and might be tempted to make it do so, see the comment by the
   1.291 + * call to ReorderAttributes in cmsencode.c.  (Or, see who else calls this
   1.292 + * and think long and hard about the implications of making it always
   1.293 + * do the reordering.)
   1.294 + */
   1.295 +SECItem *
   1.296 +NSS_CMSAttributeArray_Encode(PLArenaPool *poolp, NSSCMSAttribute ***attrs, SECItem *dest)
   1.297 +{
   1.298 +    return SEC_ASN1EncodeItem (poolp, dest, (void *)attrs, nss_cms_set_of_attribute_template);
   1.299 +}
   1.300 +
   1.301 +/*
   1.302 + * NSS_CMSAttributeArray_Reorder - sort attribute array by attribute's DER encoding
   1.303 + *
   1.304 + * make sure that the order of the attributes guarantees valid DER (which must be
   1.305 + * in lexigraphically ascending order for a SET OF); if reordering is necessary it
   1.306 + * will be done in place (in attrs).
   1.307 + */
   1.308 +SECStatus
   1.309 +NSS_CMSAttributeArray_Reorder(NSSCMSAttribute **attrs)
   1.310 +{
   1.311 +    return NSS_CMSArray_SortByDER((void **)attrs, nss_cms_attribute_template, NULL);
   1.312 +}
   1.313 +
   1.314 +/*
   1.315 + * NSS_CMSAttributeArray_FindAttrByOidTag - look through a set of attributes and
   1.316 + * find one that matches the specified object ID.
   1.317 + *
   1.318 + * If "only" is true, then make sure that there is not more than one attribute
   1.319 + * of the same type.  Otherwise, just return the first one found. (XXX Does
   1.320 + * anybody really want that first-found behavior?  It was like that when I found it...)
   1.321 + */
   1.322 +NSSCMSAttribute *
   1.323 +NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute **attrs, SECOidTag oidtag, PRBool only)
   1.324 +{
   1.325 +    SECOidData *oid;
   1.326 +    NSSCMSAttribute *attr1, *attr2;
   1.327 +
   1.328 +    if (attrs == NULL)
   1.329 +	return NULL;
   1.330 +
   1.331 +    oid = SECOID_FindOIDByTag(oidtag);
   1.332 +    if (oid == NULL)
   1.333 +	return NULL;
   1.334 +
   1.335 +    while ((attr1 = *attrs++) != NULL) {
   1.336 +	if (attr1->type.len == oid->oid.len && PORT_Memcmp (attr1->type.data,
   1.337 +							    oid->oid.data,
   1.338 +							    oid->oid.len) == 0)
   1.339 +	    break;
   1.340 +    }
   1.341 +
   1.342 +    if (attr1 == NULL)
   1.343 +	return NULL;
   1.344 +
   1.345 +    if (!only)
   1.346 +	return attr1;
   1.347 +
   1.348 +    while ((attr2 = *attrs++) != NULL) {
   1.349 +	if (attr2->type.len == oid->oid.len && PORT_Memcmp (attr2->type.data,
   1.350 +							    oid->oid.data,
   1.351 +							    oid->oid.len) == 0)
   1.352 +	    break;
   1.353 +    }
   1.354 +
   1.355 +    if (attr2 != NULL)
   1.356 +	return NULL;
   1.357 +
   1.358 +    return attr1;
   1.359 +}
   1.360 +
   1.361 +/*
   1.362 + * NSS_CMSAttributeArray_AddAttr - add an attribute to an
   1.363 + * array of attributes. 
   1.364 + */
   1.365 +SECStatus
   1.366 +NSS_CMSAttributeArray_AddAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, NSSCMSAttribute *attr)
   1.367 +{
   1.368 +    NSSCMSAttribute *oattr;
   1.369 +    void *mark;
   1.370 +    SECOidTag type;
   1.371 +
   1.372 +    mark = PORT_ArenaMark(poolp);
   1.373 +
   1.374 +    /* find oidtag of attr */
   1.375 +    type = NSS_CMSAttribute_GetType(attr);
   1.376 +
   1.377 +    /* see if we have one already */
   1.378 +    oattr = NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE);
   1.379 +    PORT_Assert (oattr == NULL);
   1.380 +    if (oattr != NULL)
   1.381 +	goto loser;	/* XXX or would it be better to replace it? */
   1.382 +
   1.383 +    /* no, shove it in */
   1.384 +    if (NSS_CMSArray_Add(poolp, (void ***)attrs, (void *)attr) != SECSuccess)
   1.385 +	goto loser;
   1.386 +
   1.387 +    PORT_ArenaUnmark(poolp, mark);
   1.388 +    return SECSuccess;
   1.389 +
   1.390 +loser:
   1.391 +    PORT_ArenaRelease(poolp, mark);
   1.392 +    return SECFailure;
   1.393 +}
   1.394 +
   1.395 +/*
   1.396 + * NSS_CMSAttributeArray_SetAttr - set an attribute's value in a set of attributes
   1.397 + */
   1.398 +SECStatus
   1.399 +NSS_CMSAttributeArray_SetAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, SECOidTag type, SECItem *value, PRBool encoded)
   1.400 +{
   1.401 +    NSSCMSAttribute *attr;
   1.402 +    void *mark;
   1.403 +
   1.404 +    mark = PORT_ArenaMark(poolp);
   1.405 +
   1.406 +    /* see if we have one already */
   1.407 +    attr = NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE);
   1.408 +    if (attr == NULL) {
   1.409 +	/* not found? create one! */
   1.410 +	attr = NSS_CMSAttribute_Create(poolp, type, value, encoded);
   1.411 +	if (attr == NULL)
   1.412 +	    goto loser;
   1.413 +	/* and add it to the list */
   1.414 +	if (NSS_CMSArray_Add(poolp, (void ***)attrs, (void *)attr) != SECSuccess)
   1.415 +	    goto loser;
   1.416 +    } else {
   1.417 +	/* found, shove it in */
   1.418 +	/* XXX we need a decent memory model @#$#$!#!!! */
   1.419 +	attr->values[0] = value;
   1.420 +	attr->encoded = encoded;
   1.421 +    }
   1.422 +
   1.423 +    PORT_ArenaUnmark (poolp, mark);
   1.424 +    return SECSuccess;
   1.425 +
   1.426 +loser:
   1.427 +    PORT_ArenaRelease (poolp, mark);
   1.428 +    return SECFailure;
   1.429 +}
   1.430 +

mercurial