Coverage Report

Created: 2018-09-25 14:53

/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
}