/src/mozilla-central/security/nss/lib/smime/cmsattr.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | { |
38 | 0 | NSSCMSAttribute *attr; |
39 | 0 | SECItem *copiedvalue; |
40 | 0 | void *mark; |
41 | 0 |
|
42 | 0 | PORT_Assert(poolp != NULL); |
43 | 0 |
|
44 | 0 | mark = PORT_ArenaMark(poolp); |
45 | 0 |
|
46 | 0 | attr = (NSSCMSAttribute *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSAttribute)); |
47 | 0 | if (attr == NULL) |
48 | 0 | goto loser; |
49 | 0 | |
50 | 0 | attr->typeTag = SECOID_FindOIDByTag(oidtag); |
51 | 0 | if (attr->typeTag == NULL) |
52 | 0 | goto loser; |
53 | 0 | |
54 | 0 | if (SECITEM_CopyItem(poolp, &(attr->type), &(attr->typeTag->oid)) != SECSuccess) |
55 | 0 | goto loser; |
56 | 0 | |
57 | 0 | if (value != NULL) { |
58 | 0 | if ((copiedvalue = SECITEM_ArenaDupItem(poolp, value)) == NULL) |
59 | 0 | goto loser; |
60 | 0 | |
61 | 0 | if (NSS_CMSArray_Add(poolp, (void ***)&(attr->values), (void *)copiedvalue) != SECSuccess) |
62 | 0 | goto loser; |
63 | 0 | } |
64 | 0 | |
65 | 0 | attr->encoded = encoded; |
66 | 0 |
|
67 | 0 | PORT_ArenaUnmark(poolp, mark); |
68 | 0 |
|
69 | 0 | return attr; |
70 | 0 |
|
71 | 0 | loser: |
72 | 0 | PORT_Assert(mark != NULL); |
73 | 0 | PORT_ArenaRelease(poolp, mark); |
74 | 0 | return NULL; |
75 | 0 | } |
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 | 0 | { |
83 | 0 | SECItem *copiedvalue; |
84 | 0 | void *mark; |
85 | 0 |
|
86 | 0 | PORT_Assert(poolp != NULL); |
87 | 0 |
|
88 | 0 | mark = PORT_ArenaMark(poolp); |
89 | 0 |
|
90 | 0 | if (value == NULL) { |
91 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
92 | 0 | goto loser; |
93 | 0 | } |
94 | 0 |
|
95 | 0 | if ((copiedvalue = SECITEM_ArenaDupItem(poolp, value)) == NULL) |
96 | 0 | goto loser; |
97 | 0 | |
98 | 0 | if (NSS_CMSArray_Add(poolp, (void ***)&(attr->values), (void *)copiedvalue) != SECSuccess) |
99 | 0 | goto loser; |
100 | 0 | |
101 | 0 | PORT_ArenaUnmark(poolp, mark); |
102 | 0 | return SECSuccess; |
103 | 0 | |
104 | 0 | loser: |
105 | 0 | PORT_Assert(mark != NULL); |
106 | 0 | PORT_ArenaRelease(poolp, mark); |
107 | 0 | return SECFailure; |
108 | 0 | } |
109 | | |
110 | | /* |
111 | | * NSS_CMSAttribute_GetType - return the OID tag |
112 | | */ |
113 | | SECOidTag |
114 | | NSS_CMSAttribute_GetType(NSSCMSAttribute *attr) |
115 | 0 | { |
116 | 0 | SECOidData *typetag; |
117 | 0 |
|
118 | 0 | typetag = SECOID_FindOID(&(attr->type)); |
119 | 0 | if (typetag == NULL) |
120 | 0 | return SEC_OID_UNKNOWN; |
121 | 0 | |
122 | 0 | return typetag->offset; |
123 | 0 | } |
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 | 0 | { |
135 | 0 | SECItem *value; |
136 | 0 |
|
137 | 0 | if (attr == NULL) |
138 | 0 | return NULL; |
139 | 0 | |
140 | 0 | value = attr->values[0]; |
141 | 0 |
|
142 | 0 | if (value == NULL || value->data == NULL || value->len == 0) |
143 | 0 | return NULL; |
144 | 0 | |
145 | 0 | if (attr->values[1] != NULL) |
146 | 0 | return NULL; |
147 | 0 | |
148 | 0 | return value; |
149 | 0 | } |
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 | 0 | { |
157 | 0 | SECItem *value; |
158 | 0 |
|
159 | 0 | if (attr == NULL) |
160 | 0 | return PR_FALSE; |
161 | 0 | |
162 | 0 | value = NSS_CMSAttribute_GetValue(attr); |
163 | 0 |
|
164 | 0 | return (value != NULL && value->len == av->len && |
165 | 0 | PORT_Memcmp(value->data, av->data, value->len) == 0); |
166 | 0 | } |
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 | 0 | { |
180 | 0 | const SEC_ASN1Template *theTemplate; |
181 | 0 | NSSCMSAttribute *attribute; |
182 | 0 | SECOidData *oiddata; |
183 | 0 | PRBool encoded; |
184 | 0 |
|
185 | 0 | PORT_Assert(src_or_dest != NULL); |
186 | 0 | if (src_or_dest == NULL) |
187 | 0 | return NULL; |
188 | 0 | |
189 | 0 | attribute = (NSSCMSAttribute *)src_or_dest; |
190 | 0 |
|
191 | 0 | if (encoding && (!attribute->values || !attribute->values[0] || |
192 | 0 | attribute->encoded)) { |
193 | 0 | /* we're encoding, and the attribute has no value or the attribute |
194 | 0 | * value is already encoded. */ |
195 | 0 | return SEC_ASN1_GET(SEC_AnyTemplate); |
196 | 0 | } |
197 | 0 |
|
198 | 0 | /* get attribute's typeTag */ |
199 | 0 | oiddata = attribute->typeTag; |
200 | 0 | if (oiddata == NULL) { |
201 | 0 | oiddata = SECOID_FindOID(&attribute->type); |
202 | 0 | attribute->typeTag = oiddata; |
203 | 0 | } |
204 | 0 |
|
205 | 0 | if (oiddata == NULL) { |
206 | 0 | /* still no OID tag? OID is unknown then. en/decode value as ANY. */ |
207 | 0 | encoded = PR_TRUE; |
208 | 0 | theTemplate = SEC_ASN1_GET(SEC_AnyTemplate); |
209 | 0 | } else { |
210 | 0 | switch (oiddata->offset) { |
211 | 0 | case SEC_OID_PKCS9_SMIME_CAPABILITIES: |
212 | 0 | case SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE: |
213 | 0 | /* these guys need to stay DER-encoded */ |
214 | 0 | default: |
215 | 0 | /* same goes for OIDs that are not handled here */ |
216 | 0 | encoded = PR_TRUE; |
217 | 0 | theTemplate = SEC_ASN1_GET(SEC_AnyTemplate); |
218 | 0 | break; |
219 | 0 | /* otherwise choose proper template */ |
220 | 0 | case SEC_OID_PKCS9_EMAIL_ADDRESS: |
221 | 0 | case SEC_OID_RFC1274_MAIL: |
222 | 0 | case SEC_OID_PKCS9_UNSTRUCTURED_NAME: |
223 | 0 | encoded = PR_FALSE; |
224 | 0 | theTemplate = SEC_ASN1_GET(SEC_IA5StringTemplate); |
225 | 0 | break; |
226 | 0 | case SEC_OID_PKCS9_CONTENT_TYPE: |
227 | 0 | encoded = PR_FALSE; |
228 | 0 | theTemplate = SEC_ASN1_GET(SEC_ObjectIDTemplate); |
229 | 0 | break; |
230 | 0 | case SEC_OID_PKCS9_MESSAGE_DIGEST: |
231 | 0 | encoded = PR_FALSE; |
232 | 0 | theTemplate = SEC_ASN1_GET(SEC_OctetStringTemplate); |
233 | 0 | break; |
234 | 0 | case SEC_OID_PKCS9_SIGNING_TIME: |
235 | 0 | encoded = PR_FALSE; |
236 | 0 | theTemplate = SEC_ASN1_GET(CERT_TimeChoiceTemplate); |
237 | 0 | break; |
238 | 0 | /* XXX Want other types here, too */ |
239 | 0 | } |
240 | 0 | } |
241 | 0 |
|
242 | 0 | if (encoding) { |
243 | 0 | /* |
244 | 0 | * If we are encoding and we think we have an already-encoded value, |
245 | 0 | * then the code which initialized this attribute should have set |
246 | 0 | * the "encoded" property to true (and we would have returned early, |
247 | 0 | * up above). No devastating error, but that code should be fixed. |
248 | 0 | * (It could indicate that the resulting encoded bytes are wrong.) |
249 | 0 | */ |
250 | 0 | PORT_Assert(!encoded); |
251 | 0 | } else { |
252 | 0 | /* |
253 | 0 | * We are decoding; record whether the resulting value is |
254 | 0 | * still encoded or not. |
255 | 0 | */ |
256 | 0 | attribute->encoded = encoded; |
257 | 0 | } |
258 | 0 | return theTemplate; |
259 | 0 | } |
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 | 0 | { |
294 | 0 | return SEC_ASN1EncodeItem(poolp, dest, (void *)attrs, nss_cms_set_of_attribute_template); |
295 | 0 | } |
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 | 0 | { |
307 | 0 | return NSS_CMSArray_SortByDER((void **)attrs, nss_cms_attribute_template, NULL); |
308 | 0 | } |
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 | 0 | { |
321 | 0 | SECOidData *oid; |
322 | 0 | NSSCMSAttribute *attr1, *attr2; |
323 | 0 |
|
324 | 0 | if (attrs == NULL) |
325 | 0 | return NULL; |
326 | 0 | |
327 | 0 | oid = SECOID_FindOIDByTag(oidtag); |
328 | 0 | if (oid == NULL) |
329 | 0 | return NULL; |
330 | 0 | |
331 | 0 | while ((attr1 = *attrs++) != NULL) { |
332 | 0 | if (attr1->type.len == oid->oid.len && |
333 | 0 | PORT_Memcmp(attr1->type.data, oid->oid.data, oid->oid.len) == 0) |
334 | 0 | break; |
335 | 0 | } |
336 | 0 |
|
337 | 0 | if (attr1 == NULL) |
338 | 0 | return NULL; |
339 | 0 | |
340 | 0 | if (!only) |
341 | 0 | return attr1; |
342 | 0 | |
343 | 0 | while ((attr2 = *attrs++) != NULL) { |
344 | 0 | if (attr2->type.len == oid->oid.len && |
345 | 0 | PORT_Memcmp(attr2->type.data, oid->oid.data, oid->oid.len) == 0) |
346 | 0 | break; |
347 | 0 | } |
348 | 0 |
|
349 | 0 | if (attr2 != NULL) |
350 | 0 | return NULL; |
351 | 0 | |
352 | 0 | return attr1; |
353 | 0 | } |
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 | 0 | { |
363 | 0 | NSSCMSAttribute *oattr; |
364 | 0 | void *mark; |
365 | 0 | SECOidTag type; |
366 | 0 |
|
367 | 0 | mark = PORT_ArenaMark(poolp); |
368 | 0 |
|
369 | 0 | /* find oidtag of attr */ |
370 | 0 | type = NSS_CMSAttribute_GetType(attr); |
371 | 0 |
|
372 | 0 | /* see if we have one already */ |
373 | 0 | oattr = NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE); |
374 | 0 | PORT_Assert(oattr == NULL); |
375 | 0 | if (oattr != NULL) |
376 | 0 | goto loser; /* XXX or would it be better to replace it? */ |
377 | 0 | |
378 | 0 | /* no, shove it in */ |
379 | 0 | if (NSS_CMSArray_Add(poolp, (void ***)attrs, (void *)attr) != SECSuccess) |
380 | 0 | goto loser; |
381 | 0 | |
382 | 0 | PORT_ArenaUnmark(poolp, mark); |
383 | 0 | return SECSuccess; |
384 | 0 | |
385 | 0 | loser: |
386 | 0 | PORT_ArenaRelease(poolp, mark); |
387 | 0 | return SECFailure; |
388 | 0 | } |
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 | 0 | { |
397 | 0 | NSSCMSAttribute *attr; |
398 | 0 | void *mark; |
399 | 0 |
|
400 | 0 | mark = PORT_ArenaMark(poolp); |
401 | 0 |
|
402 | 0 | /* see if we have one already */ |
403 | 0 | attr = NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE); |
404 | 0 | if (attr == NULL) { |
405 | 0 | /* not found? create one! */ |
406 | 0 | attr = NSS_CMSAttribute_Create(poolp, type, value, encoded); |
407 | 0 | if (attr == NULL) |
408 | 0 | goto loser; |
409 | 0 | /* and add it to the list */ |
410 | 0 | if (NSS_CMSArray_Add(poolp, (void ***)attrs, (void *)attr) != SECSuccess) |
411 | 0 | goto loser; |
412 | 0 | } else { |
413 | 0 | /* found, shove it in */ |
414 | 0 | /* XXX we need a decent memory model @#$#$!#!!! */ |
415 | 0 | attr->values[0] = value; |
416 | 0 | attr->encoded = encoded; |
417 | 0 | } |
418 | 0 |
|
419 | 0 | PORT_ArenaUnmark(poolp, mark); |
420 | 0 | return SECSuccess; |
421 | 0 | |
422 | 0 | loser: |
423 | 0 | PORT_ArenaRelease(poolp, mark); |
424 | 0 | return SECFailure; |
425 | 0 | } |