security/nss/lib/smime/cmsattr.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     5 /*
     6  * CMS attributes.
     7  */
     9 #include "cmslocal.h"
    11 #include "secasn1.h"
    12 #include "secitem.h"
    13 #include "secoid.h"
    14 #include "pk11func.h"
    15 #include "prtime.h"
    16 #include "secerr.h"
    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  */
    29 /*
    30  * NSS_CMSAttribute_Create - create an attribute
    31  *
    32  * if value is NULL, the attribute won't have a value. It can be added later
    33  * with NSS_CMSAttribute_AddValue.
    34  */
    35 NSSCMSAttribute *
    36 NSS_CMSAttribute_Create(PLArenaPool *poolp, SECOidTag oidtag, SECItem *value, PRBool encoded)
    37 {
    38     NSSCMSAttribute *attr;
    39     SECItem *copiedvalue;
    40     void *mark;
    42     PORT_Assert (poolp != NULL);
    44     mark = PORT_ArenaMark (poolp);
    46     attr = (NSSCMSAttribute *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSAttribute));
    47     if (attr == NULL)
    48 	goto loser;
    50     attr->typeTag = SECOID_FindOIDByTag(oidtag);
    51     if (attr->typeTag == NULL)
    52 	goto loser;
    54     if (SECITEM_CopyItem(poolp, &(attr->type), &(attr->typeTag->oid)) != SECSuccess)
    55 	goto loser;
    57     if (value != NULL) {
    58 	if ((copiedvalue = SECITEM_ArenaDupItem(poolp, value)) == NULL)
    59 	    goto loser;
    61 	if (NSS_CMSArray_Add(poolp, (void ***)&(attr->values), (void *)copiedvalue) != SECSuccess)
    62 	    goto loser;
    63     }
    65     attr->encoded = encoded;
    67     PORT_ArenaUnmark (poolp, mark);
    69     return attr;
    71 loser:
    72     PORT_Assert (mark != NULL);
    73     PORT_ArenaRelease (poolp, mark);
    74     return NULL;
    75 }
    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;
    86     PORT_Assert (poolp != NULL);
    88     mark = PORT_ArenaMark(poolp);
    90     if (value == NULL) {
    91 	PORT_SetError(SEC_ERROR_INVALID_ARGS);
    92 	goto loser;
    93     }
    95     if ((copiedvalue = SECITEM_ArenaDupItem(poolp, value)) == NULL)
    96 	goto loser;
    98     if (NSS_CMSArray_Add(poolp, (void ***)&(attr->values), (void *)copiedvalue) != SECSuccess)
    99 	goto loser;
   101     PORT_ArenaUnmark(poolp, mark);
   102     return SECSuccess;
   104 loser:
   105     PORT_Assert (mark != NULL);
   106     PORT_ArenaRelease (poolp, mark);
   107     return SECFailure;
   108 }
   110 /*
   111  * NSS_CMSAttribute_GetType - return the OID tag
   112  */
   113 SECOidTag
   114 NSS_CMSAttribute_GetType(NSSCMSAttribute *attr)
   115 {
   116     SECOidData *typetag;
   118     typetag = SECOID_FindOID(&(attr->type));
   119     if (typetag == NULL)
   120 	return SEC_OID_UNKNOWN;
   122     return typetag->offset;
   123 }
   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;
   137     if (attr == NULL)
   138 	return NULL;
   140     value = attr->values[0];
   142     if (value == NULL || value->data == NULL || value->len == 0)
   143 	return NULL;
   145     if (attr->values[1] != NULL)
   146 	return NULL;
   148     return value;
   149 }
   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;
   159     if (attr == NULL)
   160 	return PR_FALSE;
   162     value = NSS_CMSAttribute_GetValue(attr);
   164     return (value != NULL && value->len == av->len &&
   165 	PORT_Memcmp (value->data, av->data, value->len) == 0);
   166 }
   168 /*
   169  * templates and functions for separate ASN.1 encoding of attributes
   170  *
   171  * used in NSS_CMSAttributeArray_Reorder
   172  */
   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;
   185     PORT_Assert (src_or_dest != NULL);
   186     if (src_or_dest == NULL)
   187 	return NULL;
   189     attribute = (NSSCMSAttribute *)src_or_dest;
   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     }
   198     /* get attribute's typeTag */
   199     oiddata = attribute->typeTag;
   200     if (oiddata == NULL) {
   201 	oiddata = SECOID_FindOID(&attribute->type);
   202 	attribute->typeTag = oiddata;
   203     }
   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     }
   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 }
   261 static const SEC_ASN1TemplateChooserPtr cms_attr_chooser
   262 	= cms_attr_choose_attr_value_template;
   264 const SEC_ASN1Template nss_cms_attribute_template[] = {
   265     { SEC_ASN1_SEQUENCE,
   266 	  0, NULL, sizeof(NSSCMSAttribute) },
   267     { SEC_ASN1_OBJECT_ID,
   268 	  offsetof(NSSCMSAttribute,type) },
   269     { SEC_ASN1_DYNAMIC | SEC_ASN1_SET_OF,
   270 	  offsetof(NSSCMSAttribute,values),
   271 	  &cms_attr_chooser },
   272     { 0 }
   273 };
   275 const SEC_ASN1Template nss_cms_set_of_attribute_template[] = {
   276     { SEC_ASN1_SET_OF, 0, nss_cms_attribute_template },
   277 };
   279 /* =============================================================================
   280  * Attribute Array methods
   281  */
   283 /*
   284  * NSS_CMSAttributeArray_Encode - encode an Attribute array as SET OF Attributes
   285  *
   286  * If you are wondering why this routine does not reorder the attributes
   287  * first, and might be tempted to make it do so, see the comment by the
   288  * call to ReorderAttributes in cmsencode.c.  (Or, see who else calls this
   289  * and think long and hard about the implications of making it always
   290  * do the reordering.)
   291  */
   292 SECItem *
   293 NSS_CMSAttributeArray_Encode(PLArenaPool *poolp, NSSCMSAttribute ***attrs, SECItem *dest)
   294 {
   295     return SEC_ASN1EncodeItem (poolp, dest, (void *)attrs, nss_cms_set_of_attribute_template);
   296 }
   298 /*
   299  * NSS_CMSAttributeArray_Reorder - sort attribute array by attribute's DER encoding
   300  *
   301  * make sure that the order of the attributes guarantees valid DER (which must be
   302  * in lexigraphically ascending order for a SET OF); if reordering is necessary it
   303  * will be done in place (in attrs).
   304  */
   305 SECStatus
   306 NSS_CMSAttributeArray_Reorder(NSSCMSAttribute **attrs)
   307 {
   308     return NSS_CMSArray_SortByDER((void **)attrs, nss_cms_attribute_template, NULL);
   309 }
   311 /*
   312  * NSS_CMSAttributeArray_FindAttrByOidTag - look through a set of attributes and
   313  * find one that matches the specified object ID.
   314  *
   315  * If "only" is true, then make sure that there is not more than one attribute
   316  * of the same type.  Otherwise, just return the first one found. (XXX Does
   317  * anybody really want that first-found behavior?  It was like that when I found it...)
   318  */
   319 NSSCMSAttribute *
   320 NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute **attrs, SECOidTag oidtag, PRBool only)
   321 {
   322     SECOidData *oid;
   323     NSSCMSAttribute *attr1, *attr2;
   325     if (attrs == NULL)
   326 	return NULL;
   328     oid = SECOID_FindOIDByTag(oidtag);
   329     if (oid == NULL)
   330 	return NULL;
   332     while ((attr1 = *attrs++) != NULL) {
   333 	if (attr1->type.len == oid->oid.len && PORT_Memcmp (attr1->type.data,
   334 							    oid->oid.data,
   335 							    oid->oid.len) == 0)
   336 	    break;
   337     }
   339     if (attr1 == NULL)
   340 	return NULL;
   342     if (!only)
   343 	return attr1;
   345     while ((attr2 = *attrs++) != NULL) {
   346 	if (attr2->type.len == oid->oid.len && PORT_Memcmp (attr2->type.data,
   347 							    oid->oid.data,
   348 							    oid->oid.len) == 0)
   349 	    break;
   350     }
   352     if (attr2 != NULL)
   353 	return NULL;
   355     return attr1;
   356 }
   358 /*
   359  * NSS_CMSAttributeArray_AddAttr - add an attribute to an
   360  * array of attributes. 
   361  */
   362 SECStatus
   363 NSS_CMSAttributeArray_AddAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, NSSCMSAttribute *attr)
   364 {
   365     NSSCMSAttribute *oattr;
   366     void *mark;
   367     SECOidTag type;
   369     mark = PORT_ArenaMark(poolp);
   371     /* find oidtag of attr */
   372     type = NSS_CMSAttribute_GetType(attr);
   374     /* see if we have one already */
   375     oattr = NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE);
   376     PORT_Assert (oattr == NULL);
   377     if (oattr != NULL)
   378 	goto loser;	/* XXX or would it be better to replace it? */
   380     /* no, shove it in */
   381     if (NSS_CMSArray_Add(poolp, (void ***)attrs, (void *)attr) != SECSuccess)
   382 	goto loser;
   384     PORT_ArenaUnmark(poolp, mark);
   385     return SECSuccess;
   387 loser:
   388     PORT_ArenaRelease(poolp, mark);
   389     return SECFailure;
   390 }
   392 /*
   393  * NSS_CMSAttributeArray_SetAttr - set an attribute's value in a set of attributes
   394  */
   395 SECStatus
   396 NSS_CMSAttributeArray_SetAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, SECOidTag type, SECItem *value, PRBool encoded)
   397 {
   398     NSSCMSAttribute *attr;
   399     void *mark;
   401     mark = PORT_ArenaMark(poolp);
   403     /* see if we have one already */
   404     attr = NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE);
   405     if (attr == NULL) {
   406 	/* not found? create one! */
   407 	attr = NSS_CMSAttribute_Create(poolp, type, value, encoded);
   408 	if (attr == NULL)
   409 	    goto loser;
   410 	/* and add it to the list */
   411 	if (NSS_CMSArray_Add(poolp, (void ***)attrs, (void *)attr) != SECSuccess)
   412 	    goto loser;
   413     } else {
   414 	/* found, shove it in */
   415 	/* XXX we need a decent memory model @#$#$!#!!! */
   416 	attr->values[0] = value;
   417 	attr->encoded = encoded;
   418     }
   420     PORT_ArenaUnmark (poolp, mark);
   421     return SECSuccess;
   423 loser:
   424     PORT_ArenaRelease (poolp, mark);
   425     return SECFailure;
   426 }

mercurial