Coverage Report

Created: 2018-09-25 14:53

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