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 }