Coverage Report

Created: 2026-04-04 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nss/lib/smime/cmsdecode.c
Line
Count
Source
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 decoding.
7
 */
8
9
#include "cmslocal.h"
10
11
#include "cert.h"
12
#include "keyhi.h"
13
#include "secasn1.h"
14
#include "secitem.h"
15
#include "secoid.h"
16
#include "prtime.h"
17
#include "secerr.h"
18
19
struct NSSCMSDecoderContextStr {
20
    SEC_ASN1DecoderContext *dcx;      /* ASN.1 decoder context */
21
    NSSCMSMessage *cmsg;              /* backpointer to the root message */
22
    SECOidTag type;                   /* type of message */
23
    NSSCMSContent content;            /* pointer to message */
24
    NSSCMSDecoderContext *childp7dcx; /* inner CMS decoder context */
25
    PRBool saw_contents;
26
    int error;
27
    NSSCMSContentCallback cb;
28
    void *cb_arg;
29
    PRBool first_decoded;
30
    PRBool need_indefinite_finish;
31
    unsigned int max_asn_len;
32
};
33
34
struct NSSCMSDecoderDataStr {
35
    SECItem data; /* must be first */
36
    unsigned int totalBufferSize;
37
};
38
39
typedef struct NSSCMSDecoderDataStr NSSCMSDecoderData;
40
41
static void nss_cms_decoder_update_filter(void *arg, const char *data,
42
                                          unsigned long len, int depth,
43
                                          SEC_ASN1EncodingPart data_kind);
44
static SECStatus nss_cms_before_data(NSSCMSDecoderContext *p7dcx);
45
static SECStatus nss_cms_after_data(NSSCMSDecoderContext *p7dcx);
46
static SECStatus nss_cms_after_end(NSSCMSDecoderContext *p7dcx);
47
static void nss_cms_decoder_update(void *p7ecx, const char *data, unsigned long len);
48
static void nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx,
49
                                      const unsigned char *data,
50
                                      unsigned long len,
51
                                      PRBool final);
52
static NSSCMSDecoderData *nss_cms_create_decoder_data(PLArenaPool *poolp);
53
54
extern const SEC_ASN1Template NSSCMSMessageTemplate[];
55
56
void
57
nss_cms_set_max_asn_length(NSSCMSDecoderContext *p7dcx, unsigned int max_asn_len)
58
6.65k
{
59
6.65k
    p7dcx->max_asn_len = max_asn_len;
60
6.65k
    if (p7dcx->dcx && max_asn_len) {
61
6.65k
        SEC_ASN1DecoderSetMaximumElementSize(p7dcx->dcx, max_asn_len);
62
6.65k
    }
63
6.65k
}
64
65
static NSSCMSDecoderData *
66
nss_cms_create_decoder_data(PLArenaPool *poolp)
67
200
{
68
200
    NSSCMSDecoderData *decoderData = NULL;
69
70
200
    decoderData = (NSSCMSDecoderData *)
71
200
        PORT_ArenaAlloc(poolp, sizeof(NSSCMSDecoderData));
72
200
    if (!decoderData) {
73
0
        return NULL;
74
0
    }
75
200
    decoderData->data.data = NULL;
76
200
    decoderData->data.len = 0;
77
200
    decoderData->totalBufferSize = 0;
78
200
    return decoderData;
79
200
}
80
81
/*
82
 * nss_cms_decoder_notify -
83
 *  this is the driver of the decoding process. It gets called by the ASN.1
84
 *  decoder before and after an object is decoded.
85
 *  at various points in the decoding process, we intercept to set up and do
86
 *  further processing.
87
 */
88
static void
89
nss_cms_decoder_notify(void *arg, PRBool before, void *dest, int depth)
90
11.2M
{
91
11.2M
    NSSCMSDecoderContext *p7dcx;
92
11.2M
    NSSCMSContentInfo *rootcinfo, *cinfo;
93
11.2M
    PRBool after = !before;
94
95
11.2M
    p7dcx = (NSSCMSDecoderContext *)arg;
96
11.2M
    rootcinfo = &(p7dcx->cmsg->contentInfo);
97
98
    /* XXX error handling: need to set p7dcx->error */
99
100
#ifdef CMSDEBUG
101
    fprintf(stderr, "%6.6s, dest = 0x%p, depth = %d\n", before ? "before" : "after",
102
            dest, depth);
103
#endif
104
105
    /* so what are we working on right now? */
106
11.2M
    if (p7dcx->type == SEC_OID_UNKNOWN) {
107
        /*
108
         * right now, we are still decoding the OUTER (root) cinfo
109
         * As soon as we know the inner content type, set up the info,
110
         * but NO inner decoder or filter. The root decoder handles the first
111
         * level children by itself - only for encapsulated contents (which
112
         * are encoded as DER inside of an OCTET STRING) we need to set up a
113
         * child decoder...
114
         */
115
9.19k
        if (after && dest == &(rootcinfo->contentType)) {
116
3.95k
            p7dcx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo);
117
3.95k
            p7dcx->content = rootcinfo->content;
118
            /* is this ready already ? need to alloc? */
119
            /* XXX yes we need to alloc -- continue here */
120
3.95k
        }
121
11.2M
    } else if (NSS_CMSType_IsData(p7dcx->type)) {
122
        /* this can only happen if the outermost cinfo has DATA in it */
123
        /* otherwise, we handle this type implicitely in the inner decoders */
124
125
38
        if (before && dest == &(rootcinfo->content)) {
126
            /* cause the filter to put the data in the right place...
127
            ** We want the ASN.1 decoder to deliver the decoded bytes to us
128
            ** from now on
129
            */
130
35
            SEC_ASN1DecoderSetFilterProc(p7dcx->dcx,
131
35
                                         nss_cms_decoder_update_filter,
132
35
                                         p7dcx,
133
35
                                         (PRBool)(p7dcx->cb != NULL));
134
35
        } else if (after && dest == &(rootcinfo->content.data)) {
135
            /* remove the filter */
136
3
            SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
137
3
        }
138
11.2M
    } else if (NSS_CMSType_IsWrapper(p7dcx->type)) {
139
11.2M
        if (!before || dest != &(rootcinfo->content)) {
140
141
11.2M
            if (p7dcx->content.pointer == NULL)
142
3.03k
                p7dcx->content = rootcinfo->content;
143
144
            /* get this data type's inner contentInfo */
145
11.2M
            cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer,
146
11.2M
                                                  p7dcx->type);
147
11.2M
            if (cinfo == NULL) {
148
3
                p7dcx->error = SEC_ERROR_BAD_DATA;
149
3
                return;
150
3
            }
151
11.2M
            if (before && dest == &(cinfo->contentType)) {
152
                /* at this point, set up the &%$&$ back pointer */
153
                /* we cannot do it later, because the content itself
154
                 * is optional! */
155
5.25k
                switch (p7dcx->type) {
156
4.74k
                    case SEC_OID_PKCS7_SIGNED_DATA:
157
4.74k
                        p7dcx->content.signedData->cmsg = p7dcx->cmsg;
158
4.74k
                        break;
159
121
                    case SEC_OID_PKCS7_DIGESTED_DATA:
160
121
                        p7dcx->content.digestedData->cmsg = p7dcx->cmsg;
161
121
                        break;
162
354
                    case SEC_OID_PKCS7_ENVELOPED_DATA:
163
354
                        p7dcx->content.envelopedData->cmsg = p7dcx->cmsg;
164
354
                        break;
165
26
                    case SEC_OID_PKCS7_ENCRYPTED_DATA:
166
26
                        p7dcx->content.encryptedData->cmsg = p7dcx->cmsg;
167
26
                        break;
168
0
                    default:
169
0
                        p7dcx->content.genericData->cmsg = p7dcx->cmsg;
170
0
                        break;
171
5.25k
                }
172
5.25k
            }
173
174
11.2M
            if (before && dest == &(cinfo->rawContent)) {
175
                /* we want the ASN.1 decoder to deliver the decoded bytes to us
176
                 ** from now on
177
                 */
178
2.54k
                SEC_ASN1DecoderSetFilterProc(p7dcx->dcx,
179
2.54k
                                             nss_cms_decoder_update_filter,
180
2.54k
                                             p7dcx, (PRBool)(p7dcx->cb != NULL));
181
182
                /* we're right in front of the data */
183
2.54k
                if (nss_cms_before_data(p7dcx) != SECSuccess) {
184
210
                    SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
185
                    /* stop all processing */
186
210
                    p7dcx->error = PORT_GetError();
187
210
                }
188
2.54k
            }
189
11.2M
            if (after && dest == &(cinfo->rawContent)) {
190
                /* we're right after of the data */
191
803
                if (nss_cms_after_data(p7dcx) != SECSuccess)
192
413
                    p7dcx->error = PORT_GetError();
193
194
                /* we don't need to see the contents anymore */
195
803
                SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
196
803
            }
197
11.2M
        }
198
11.2M
    } else {
199
        /* unsupported or unknown message type - fail  gracefully */
200
44
        p7dcx->error = SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE;
201
44
    }
202
11.2M
}
203
204
/*
205
 * nss_cms_before_data - set up the current encoder to receive data
206
 */
207
static SECStatus
208
nss_cms_before_data(NSSCMSDecoderContext *p7dcx)
209
2.54k
{
210
2.54k
    SECStatus rv;
211
2.54k
    SECOidTag childtype;
212
2.54k
    PLArenaPool *poolp;
213
2.54k
    NSSCMSDecoderContext *childp7dcx;
214
2.54k
    NSSCMSContentInfo *cinfo;
215
2.54k
    const SEC_ASN1Template *template;
216
2.54k
    void *mark = NULL;
217
2.54k
    size_t size;
218
219
2.54k
    poolp = p7dcx->cmsg->poolp;
220
221
    /* call _Decode_BeforeData handlers */
222
2.54k
    switch (p7dcx->type) {
223
2.33k
        case SEC_OID_PKCS7_SIGNED_DATA:
224
            /* we're decoding a signedData, so set up the digests */
225
2.33k
            rv = NSS_CMSSignedData_Decode_BeforeData(p7dcx->content.signedData);
226
2.33k
            break;
227
55
        case SEC_OID_PKCS7_DIGESTED_DATA:
228
            /* we're encoding a digestedData, so set up the digest */
229
55
            rv = NSS_CMSDigestedData_Decode_BeforeData(p7dcx->content.digestedData);
230
55
            break;
231
154
        case SEC_OID_PKCS7_ENVELOPED_DATA:
232
154
            rv = NSS_CMSEnvelopedData_Decode_BeforeData(
233
154
                p7dcx->content.envelopedData);
234
154
            break;
235
6
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
236
6
            rv = NSS_CMSEncryptedData_Decode_BeforeData(
237
6
                p7dcx->content.encryptedData);
238
6
            break;
239
0
        default:
240
0
            rv = NSS_CMSGenericWrapperData_Decode_BeforeData(p7dcx->type,
241
0
                                                             p7dcx->content.genericData);
242
2.54k
    }
243
2.54k
    if (rv != SECSuccess)
244
210
        return SECFailure;
245
246
    /* ok, now we have a pointer to cinfo */
247
    /* find out what kind of data is encapsulated */
248
249
2.33k
    cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type);
250
2.33k
    childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
251
252
2.33k
    if (NSS_CMSType_IsData(childtype)) {
253
200
        cinfo->content.pointer = (void *)nss_cms_create_decoder_data(poolp);
254
200
        if (cinfo->content.pointer == NULL)
255
            /* set memory error */
256
0
            return SECFailure;
257
258
200
        p7dcx->childp7dcx = NULL;
259
200
        return SECSuccess;
260
200
    }
261
262
    /* set up inner decoder */
263
264
2.13k
    if ((template = NSS_CMSUtil_GetTemplateByTypeTag(childtype)) == NULL)
265
0
        return SECFailure;
266
267
2.13k
    childp7dcx = PORT_ArenaZNew(poolp, NSSCMSDecoderContext);
268
2.13k
    if (childp7dcx == NULL)
269
0
        return SECFailure;
270
271
2.13k
    mark = PORT_ArenaMark(poolp);
272
273
    /* allocate space for the stuff we're creating */
274
2.13k
    size = NSS_CMSUtil_GetSizeByTypeTag(childtype);
275
2.13k
    childp7dcx->content.pointer = (void *)PORT_ArenaZAlloc(poolp, size);
276
2.13k
    if (childp7dcx->content.pointer == NULL)
277
0
        goto loser;
278
279
    /* give the parent a copy of the pointer so that it doesn't get lost */
280
2.13k
    cinfo->content.pointer = childp7dcx->content.pointer;
281
282
    /* start the child decoder */
283
2.13k
    childp7dcx->dcx = SEC_ASN1DecoderStart(poolp, childp7dcx->content.pointer,
284
2.13k
                                           template);
285
2.13k
    if (childp7dcx->dcx == NULL)
286
0
        goto loser;
287
288
2.13k
    if (p7dcx->max_asn_len) {
289
2.13k
        nss_cms_set_max_asn_length(childp7dcx, p7dcx->max_asn_len);
290
2.13k
    }
291
292
    /* the new decoder needs to notify, too */
293
2.13k
    SEC_ASN1DecoderSetNotifyProc(childp7dcx->dcx, nss_cms_decoder_notify,
294
2.13k
                                 childp7dcx);
295
296
    /* tell the parent decoder that it needs to feed us the content data */
297
2.13k
    p7dcx->childp7dcx = childp7dcx;
298
299
2.13k
    childp7dcx->type = childtype; /* our type */
300
301
2.13k
    childp7dcx->cmsg = p7dcx->cmsg; /* backpointer to root message */
302
303
    /* should the child decoder encounter real data,
304
    ** it must give it to the caller
305
    */
306
2.13k
    childp7dcx->cb = p7dcx->cb;
307
2.13k
    childp7dcx->cb_arg = p7dcx->cb_arg;
308
2.13k
    childp7dcx->first_decoded = PR_FALSE;
309
2.13k
    childp7dcx->need_indefinite_finish = PR_FALSE;
310
2.13k
    if (childtype == SEC_OID_PKCS7_SIGNED_DATA) {
311
401
        childp7dcx->first_decoded = PR_TRUE;
312
401
    }
313
314
    /* now set up the parent to hand decoded data to the next level decoder */
315
2.13k
    p7dcx->cb = (NSSCMSContentCallback)nss_cms_decoder_update;
316
2.13k
    p7dcx->cb_arg = childp7dcx;
317
318
2.13k
    PORT_ArenaUnmark(poolp, mark);
319
320
2.13k
    return SECSuccess;
321
322
0
loser:
323
0
    if (mark)
324
0
        PORT_ArenaRelease(poolp, mark);
325
0
    p7dcx->childp7dcx = NULL;
326
0
    return SECFailure;
327
2.13k
}
328
329
static SECStatus
330
nss_cms_after_data(NSSCMSDecoderContext *p7dcx)
331
803
{
332
803
    NSSCMSDecoderContext *childp7dcx;
333
803
    SECStatus rv = SECFailure;
334
335
    /* Handle last block. This is necessary to flush out the last bytes
336
     * of a possibly incomplete block */
337
803
    nss_cms_decoder_work_data(p7dcx, NULL, 0, PR_TRUE);
338
339
    /* finish any "inner" decoders - there's no more data coming... */
340
803
    if (p7dcx->childp7dcx != NULL) {
341
743
        childp7dcx = p7dcx->childp7dcx;
342
743
        if (childp7dcx->dcx != NULL) {
343
            /* we started and indefinite sequence somewhere, not complete it */
344
429
            if (childp7dcx->need_indefinite_finish) {
345
56
                static const char lbuf[2] = { 0, 0 };
346
56
                NSS_CMSDecoder_Update(childp7dcx, lbuf, sizeof(lbuf));
347
56
                childp7dcx->need_indefinite_finish = PR_FALSE;
348
56
            }
349
350
429
            rv = nss_cms_after_end(childp7dcx);
351
429
            if (rv != SECSuccess)
352
413
                goto done;
353
429
        }
354
330
        p7dcx->childp7dcx = NULL;
355
330
    }
356
357
390
    switch (p7dcx->type) {
358
344
        case SEC_OID_PKCS7_SIGNED_DATA:
359
            /* this will finish the digests and verify */
360
344
            rv = NSS_CMSSignedData_Decode_AfterData(p7dcx->content.signedData);
361
344
            break;
362
24
        case SEC_OID_PKCS7_ENVELOPED_DATA:
363
24
            rv = NSS_CMSEnvelopedData_Decode_AfterData(
364
24
                p7dcx->content.envelopedData);
365
24
            break;
366
17
        case SEC_OID_PKCS7_DIGESTED_DATA:
367
17
            rv = NSS_CMSDigestedData_Decode_AfterData(
368
17
                p7dcx->content.digestedData);
369
17
            break;
370
5
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
371
5
            rv = NSS_CMSEncryptedData_Decode_AfterData(
372
5
                p7dcx->content.encryptedData);
373
5
            break;
374
0
        case SEC_OID_PKCS7_DATA:
375
            /* do nothing */
376
0
            break;
377
0
        default:
378
0
            rv = NSS_CMSGenericWrapperData_Decode_AfterData(p7dcx->type,
379
0
                                                            p7dcx->content.genericData);
380
0
            break;
381
390
    }
382
803
done:
383
803
    return rv;
384
390
}
385
386
static SECStatus
387
nss_cms_after_end(NSSCMSDecoderContext *p7dcx)
388
72.6k
{
389
72.6k
    SECStatus rv = SECSuccess, rv1 = SECSuccess, rv2 = SECSuccess;
390
391
    /* Finish any child decoders */
392
72.6k
    if (p7dcx->childp7dcx) {
393
1.80k
        rv1 = nss_cms_after_end(p7dcx->childp7dcx) != SECSuccess;
394
1.80k
        p7dcx->childp7dcx = NULL;
395
1.80k
    }
396
    /* Finish our asn1 decoder */
397
72.6k
    if (p7dcx->dcx) {
398
6.65k
        rv2 = SEC_ASN1DecoderFinish(p7dcx->dcx);
399
6.65k
        p7dcx->dcx = NULL;
400
6.65k
    }
401
72.6k
    if (rv1 != SECSuccess || rv2 != SECSuccess || p7dcx->error != 0) {
402
72.0k
        return SECFailure;
403
72.0k
    }
404
405
619
    switch (p7dcx->type) {
406
75
        case SEC_OID_PKCS7_SIGNED_DATA:
407
75
            if (p7dcx->content.signedData)
408
74
                rv = NSS_CMSSignedData_Decode_AfterEnd(p7dcx->content.signedData);
409
75
            break;
410
6
        case SEC_OID_PKCS7_ENVELOPED_DATA:
411
6
            if (p7dcx->content.envelopedData)
412
5
                rv = NSS_CMSEnvelopedData_Decode_AfterEnd(
413
5
                    p7dcx->content.envelopedData);
414
6
            break;
415
7
        case SEC_OID_PKCS7_DIGESTED_DATA:
416
7
            if (p7dcx->content.digestedData)
417
6
                rv = NSS_CMSDigestedData_Decode_AfterEnd(
418
6
                    p7dcx->content.digestedData);
419
7
            break;
420
3
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
421
3
            if (p7dcx->content.encryptedData)
422
2
                rv = NSS_CMSEncryptedData_Decode_AfterEnd(
423
2
                    p7dcx->content.encryptedData);
424
3
            break;
425
2
        case SEC_OID_PKCS7_DATA:
426
2
            break;
427
526
        default:
428
526
            rv = NSS_CMSGenericWrapperData_Decode_AfterEnd(p7dcx->type,
429
526
                                                           p7dcx->content.genericData);
430
526
            break;
431
619
    }
432
619
    return rv;
433
619
}
434
435
/*
436
 * nss_cms_decoder_work_data - handle decoded data bytes.
437
 *
438
 * This function either decrypts the data if needed, and/or calculates digests
439
 * on it, then either stores it or passes it on to the next level decoder.
440
 */
441
static void
442
nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx,
443
                          const unsigned char *data, unsigned long len,
444
                          PRBool final)
445
1.87M
{
446
1.87M
    NSSCMSContentInfo *cinfo;
447
1.87M
    unsigned char *buf = NULL;
448
1.87M
    unsigned char *dest;
449
1.87M
    unsigned int offset;
450
1.87M
    SECStatus rv;
451
452
    /*
453
     * We should really have data to process, or we should be trying
454
     * to finish/flush the last block.  (This is an overly paranoid
455
     * check since all callers are in this file and simple inspection
456
     * proves they do it right.  But it could find a bug in future
457
     * modifications/development, that is why it is here.)
458
     */
459
1.87M
    PORT_Assert((data != NULL && len) || final);
460
461
1.87M
    cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type);
462
1.87M
    if (!cinfo) {
463
        /* The original programmer didn't expect this to happen */
464
204
        p7dcx->error = SEC_ERROR_LIBRARY_FAILURE;
465
204
        goto loser;
466
204
    }
467
468
1.87M
    if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) {
469
        /*
470
         * we are decrypting.
471
         *
472
         * XXX If we get an error, we do not want to do the digest or callback,
473
         * but we want to keep decoding.  Or maybe we want to stop decoding
474
         * altogether if there is a callback, because obviously we are not
475
         * sending the data back and they want to know that.
476
         */
477
478
0
        unsigned int outlen = 0; /* length of decrypted data */
479
0
        unsigned int buflen;     /* length available for decrypted data */
480
481
        /* find out about the length of decrypted data */
482
0
        buflen = NSS_CMSCipherContext_DecryptLength(cinfo->privateInfo->ciphcx, len, final);
483
484
        /*
485
         * it might happen that we did not provide enough data for a full
486
         * block (decryption unit), and that there is no output available
487
         */
488
489
        /* no output available, AND no input? */
490
0
        if (buflen == 0 && len == 0)
491
0
            goto loser; /* bail out */
492
493
        /*
494
         * have inner decoder: pass the data on (means inner content type is NOT data)
495
         * no inner decoder: we have DATA in here: either call callback or store
496
         */
497
0
        if (buflen != 0) {
498
            /* there will be some output - need to make room for it */
499
            /* allocate buffer from the heap */
500
0
            buf = (unsigned char *)PORT_Alloc(buflen);
501
0
            if (buf == NULL) {
502
0
                p7dcx->error = SEC_ERROR_NO_MEMORY;
503
0
                goto loser;
504
0
            }
505
0
        }
506
507
        /*
508
         * decrypt incoming data
509
         * buf can still be NULL here (and buflen == 0) here if we don't expect
510
         * any output (see above), but we still need to call NSS_CMSCipherContext_Decrypt to
511
         * keep track of incoming data
512
         */
513
0
        rv = NSS_CMSCipherContext_Decrypt(cinfo->privateInfo->ciphcx, buf, &outlen, buflen,
514
0
                                          data, len, final);
515
0
        if (rv != SECSuccess) {
516
0
            p7dcx->error = PORT_GetError();
517
0
            goto loser;
518
0
        }
519
520
0
        PORT_Assert(final || outlen == buflen);
521
522
        /* swap decrypted data in */
523
0
        data = buf;
524
0
        len = outlen;
525
0
    }
526
527
1.87M
    if (len == 0)
528
803
        goto done; /* nothing more to do */
529
530
    /*
531
     * Update the running digests with plaintext bytes (if we need to).
532
     */
533
1.87M
    if (cinfo->privateInfo && cinfo->privateInfo->digcx)
534
1.87M
        NSS_CMSDigestContext_Update(cinfo->privateInfo->digcx, data, len);
535
536
    /* at this point, we have the plain decoded & decrypted data
537
    ** which is either more encoded DER (which we need to hand to the child
538
    ** decoder) or data we need to hand back to our caller
539
    */
540
541
    /* pass the content back to our caller or */
542
    /* feed our freshly decrypted and decoded data into child decoder */
543
1.87M
    if (p7dcx->cb != NULL) {
544
104k
        (*p7dcx->cb)(p7dcx->cb_arg, (const char *)data, len);
545
104k
    }
546
1.76M
#if 1
547
1.76M
    else
548
1.76M
#endif
549
1.76M
        if (NSS_CMSContentInfo_GetContentTypeTag(cinfo) == SEC_OID_PKCS7_DATA) {
550
        /* store it in "inner" data item as well */
551
        /* find the DATA item in the encapsulated cinfo and store it there */
552
1.76M
        NSSCMSDecoderData *decoderData =
553
1.76M
            (NSSCMSDecoderData *)cinfo->content.pointer;
554
1.76M
        SECItem *dataItem = &decoderData->data;
555
556
1.76M
        offset = dataItem->len;
557
1.76M
        if (dataItem->len + len > decoderData->totalBufferSize) {
558
627
            int needLen = (dataItem->len + len) * 2;
559
627
            dest = (unsigned char *)
560
627
                PORT_ArenaAlloc(p7dcx->cmsg->poolp, needLen);
561
627
            if (dest == NULL) {
562
0
                p7dcx->error = SEC_ERROR_NO_MEMORY;
563
0
                goto loser;
564
0
            }
565
566
627
            if (dataItem->len) {
567
445
                PORT_Memcpy(dest, dataItem->data, dataItem->len);
568
445
            }
569
627
            decoderData->totalBufferSize = needLen;
570
627
            dataItem->data = dest;
571
627
        }
572
573
        /* copy it in */
574
1.76M
        PORT_Memcpy(dataItem->data + offset, data, len);
575
1.76M
        dataItem->len += len;
576
1.76M
    }
577
578
1.87M
done:
579
1.87M
loser:
580
1.87M
    if (buf)
581
0
        PORT_Free(buf);
582
1.87M
}
583
584
/*
585
 * nss_cms_decoder_update - deliver decoded data to the next higher level!
586
 */
587
static void
588
nss_cms_decoder_update(void *p7ecx, const char *data, unsigned long len)
589
104k
{
590
104k
    (void)NSS_CMSDecoder_Update((NSSCMSDecoderContext *)p7ecx, data, len);
591
104k
}
592
593
/*
594
 * nss_cms_decoder_update_filter - process ASN.1 data
595
 *
596
 * once we have set up a filter in nss_cms_decoder_notify(),
597
 * all data processed by the ASN.1 decoder is also passed through here.
598
 * we pass the content bytes (as opposed to length and tag bytes) on to
599
 * nss_cms_decoder_work_data().
600
 */
601
static void
602
nss_cms_decoder_update_filter(void *arg, const char *data, unsigned long len,
603
                              int depth, SEC_ASN1EncodingPart data_kind)
604
5.62M
{
605
5.62M
    NSSCMSDecoderContext *p7dcx;
606
607
5.62M
    PORT_Assert(len); /* paranoia */
608
5.62M
    if (len == 0)
609
0
        return;
610
611
5.62M
    p7dcx = (NSSCMSDecoderContext *)arg;
612
613
5.62M
    p7dcx->saw_contents = PR_TRUE;
614
615
    /* pass on the content bytes only */
616
5.62M
    if (data_kind == SEC_ASN1_Contents)
617
1.87M
        nss_cms_decoder_work_data(p7dcx, (const unsigned char *)data, len,
618
1.87M
                                  PR_FALSE);
619
5.62M
}
620
621
/*
622
 * NSS_CMSDecoder_Start - set up decoding of a DER-encoded CMS message
623
 *
624
 * "poolp" - pointer to arena for message, or NULL if new pool should be created
625
 * "cb", "cb_arg" - callback function and argument for delivery of inner content
626
 * "pwfn", pwfn_arg" - callback function for getting token password
627
 * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
628
 */
629
NSSCMSDecoderContext *
630
NSS_CMSDecoder_Start(PLArenaPool *poolp,
631
                     NSSCMSContentCallback cb, void *cb_arg,
632
                     PK11PasswordFunc pwfn, void *pwfn_arg,
633
                     NSSCMSGetDecryptKeyCallback decrypt_key_cb,
634
                     void *decrypt_key_cb_arg)
635
4.51k
{
636
4.51k
    NSSCMSDecoderContext *p7dcx;
637
4.51k
    NSSCMSMessage *cmsg;
638
639
4.51k
    cmsg = NSS_CMSMessage_Create(poolp);
640
4.51k
    if (cmsg == NULL)
641
0
        return NULL;
642
643
4.51k
    NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb,
644
4.51k
                                     decrypt_key_cb_arg, NULL, NULL);
645
646
4.51k
    p7dcx = PORT_ZNew(NSSCMSDecoderContext);
647
4.51k
    if (p7dcx == NULL) {
648
0
        NSS_CMSMessage_Destroy(cmsg);
649
0
        return NULL;
650
0
    }
651
652
4.51k
    p7dcx->dcx = SEC_ASN1DecoderStart(cmsg->poolp, cmsg, NSSCMSMessageTemplate);
653
4.51k
    if (p7dcx->dcx == NULL) {
654
0
        PORT_Free(p7dcx);
655
0
        NSS_CMSMessage_Destroy(cmsg);
656
0
        return NULL;
657
0
    }
658
659
4.51k
    SEC_ASN1DecoderSetNotifyProc(p7dcx->dcx, nss_cms_decoder_notify, p7dcx);
660
661
4.51k
    p7dcx->cmsg = cmsg;
662
4.51k
    p7dcx->type = SEC_OID_UNKNOWN;
663
664
4.51k
    p7dcx->cb = cb;
665
4.51k
    p7dcx->cb_arg = cb_arg;
666
4.51k
    p7dcx->first_decoded = PR_FALSE;
667
4.51k
    p7dcx->need_indefinite_finish = PR_FALSE;
668
4.51k
    return p7dcx;
669
4.51k
}
670
671
/*
672
 * NSS_CMSDecoder_Update - feed DER-encoded data to decoder
673
 */
674
SECStatus
675
NSS_CMSDecoder_Update(NSSCMSDecoderContext *p7dcx, const char *buf,
676
                      unsigned long len)
677
108k
{
678
108k
    SECStatus rv = SECSuccess;
679
108k
    if (p7dcx->dcx != NULL && p7dcx->error == 0) {
680
        /* if error is set already, don't bother */
681
45.7k
        if ((p7dcx->type == SEC_OID_PKCS7_SIGNED_DATA) && (p7dcx->first_decoded == PR_TRUE) && (buf[0] == SEC_ASN1_INTEGER)) {
682
            /* Microsoft Windows 2008 left out the Sequence wrapping in some
683
             * of their kerberos replies. If we are here, we most likely are
684
             * dealing with one of those replies. Supply the Sequence wrap
685
             * as indefinite encoding (since we don't know the total length
686
             * yet) */
687
349
            static const char lbuf[2] = { SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED, 0x80 };
688
349
            rv = SEC_ASN1DecoderUpdate(p7dcx->dcx, lbuf, sizeof(lbuf));
689
349
            if (rv != SECSuccess) {
690
0
                goto loser;
691
0
            }
692
            /* ok, we're going to need the indefinite finish when we are done */
693
349
            p7dcx->need_indefinite_finish = PR_TRUE;
694
349
        }
695
696
45.7k
        rv = SEC_ASN1DecoderUpdate(p7dcx->dcx, buf, len);
697
45.7k
    }
698
699
108k
loser:
700
108k
    p7dcx->first_decoded = PR_FALSE;
701
108k
    if (rv != SECSuccess) {
702
2.48k
        p7dcx->error = PORT_GetError();
703
2.48k
        PORT_Assert(p7dcx->error);
704
2.48k
        if (p7dcx->error == 0)
705
0
            p7dcx->error = -1;
706
2.48k
    }
707
708
108k
    if (p7dcx->error == 0)
709
42.9k
        return SECSuccess;
710
711
    /* there has been a problem, let's finish the decoder */
712
65.8k
    nss_cms_after_end(p7dcx);
713
65.8k
    PORT_SetError(p7dcx->error);
714
65.8k
    return SECFailure;
715
108k
}
716
717
/*
718
 * NSS_CMSDecoder_Cancel - stop decoding in case of error
719
 */
720
void
721
NSS_CMSDecoder_Cancel(NSSCMSDecoderContext *p7dcx)
722
0
{
723
0
    nss_cms_after_end(p7dcx);
724
0
    NSS_CMSMessage_Destroy(p7dcx->cmsg);
725
0
    PORT_Free(p7dcx);
726
0
}
727
728
/*
729
 * NSS_CMSDecoder_Finish - mark the end of inner content and finish decoding
730
 */
731
NSSCMSMessage *
732
NSS_CMSDecoder_Finish(NSSCMSDecoderContext *p7dcx)
733
4.51k
{
734
4.51k
    NSSCMSMessage *cmsg;
735
736
4.51k
    cmsg = p7dcx->cmsg;
737
738
4.51k
    if (nss_cms_after_end(p7dcx) != SECSuccess) {
739
4.47k
        NSS_CMSMessage_Destroy(cmsg); /* get rid of pool if it's ours */
740
4.47k
        cmsg = NULL;
741
4.47k
    }
742
743
4.51k
    PORT_Free(p7dcx);
744
4.51k
    return cmsg;
745
4.51k
}
746
747
NSSCMSMessage *
748
NSS_CMSMessage_CreateFromDER(SECItem *DERmessage,
749
                             NSSCMSContentCallback cb, void *cb_arg,
750
                             PK11PasswordFunc pwfn, void *pwfn_arg,
751
                             NSSCMSGetDecryptKeyCallback decrypt_key_cb,
752
                             void *decrypt_key_cb_arg)
753
4.51k
{
754
4.51k
    NSSCMSDecoderContext *p7dcx;
755
756
    /* first arg(poolp) == NULL => create our own pool */
757
4.51k
    p7dcx = NSS_CMSDecoder_Start(NULL, cb, cb_arg, pwfn, pwfn_arg,
758
4.51k
                                 decrypt_key_cb, decrypt_key_cb_arg);
759
4.51k
    if (p7dcx == NULL) {
760
0
        return NULL;
761
0
    }
762
4.51k
    nss_cms_set_max_asn_length(p7dcx, DERmessage->len);
763
764
4.51k
    NSS_CMSDecoder_Update(p7dcx, (char *)DERmessage->data, DERmessage->len);
765
4.51k
    return NSS_CMSDecoder_Finish(p7dcx);
766
4.51k
}