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

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

mercurial