Coverage Report

Created: 2026-01-22 06:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nss/lib/pkcs7/p7decode.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
 * PKCS7 decoding, verification.
7
 */
8
9
#include "p7local.h"
10
11
#include "cert.h"
12
/* XXX do not want to have to include */
13
#include "certdb.h" /* certdb.h -- the trust stuff needed by */
14
                    /* the add certificate code needs to get */
15
                    /* rewritten/abstracted and then this */
16
                    /* include should be removed! */
17
/*#include "cdbhdl.h" */
18
#include "cryptohi.h"
19
#include "keyhi.h"
20
#include "secasn1.h"
21
#include "secitem.h"
22
#include "secoid.h"
23
#include "pk11func.h"
24
#include "prtime.h"
25
#include "secerr.h"
26
#include "sechash.h" /* for HASH_GetHashObject() */
27
#include "secder.h"
28
#include "secpkcs5.h"
29
30
struct sec_pkcs7_decoder_worker {
31
    int depth;
32
    int digcnt;
33
    void **digcxs;
34
    const SECHashObject **digobjs;
35
    sec_PKCS7CipherObject *decryptobj;
36
    PRBool saw_contents;
37
};
38
39
struct SEC_PKCS7DecoderContextStr {
40
    SEC_ASN1DecoderContext *dcx;
41
    SEC_PKCS7ContentInfo *cinfo;
42
    SEC_PKCS7DecoderContentCallback cb;
43
    void *cb_arg;
44
    SECKEYGetPasswordKey pwfn;
45
    void *pwfn_arg;
46
    struct sec_pkcs7_decoder_worker worker;
47
    PLArenaPool *tmp_poolp;
48
    int error;
49
    SEC_PKCS7GetDecryptKeyCallback dkcb;
50
    void *dkcb_arg;
51
    SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb;
52
};
53
54
/*
55
 * Handle one worker, decrypting and digesting the data as necessary.
56
 *
57
 * XXX If/when we support nested contents, this probably needs to be
58
 * revised somewhat to get passed the content-info (which unfortunately
59
 * can be two different types depending on whether it is encrypted or not)
60
 * corresponding to the given worker.
61
 */
62
static void
63
sec_pkcs7_decoder_work_data(SEC_PKCS7DecoderContext *p7dcx,
64
                            struct sec_pkcs7_decoder_worker *worker,
65
                            const unsigned char *data, unsigned long len,
66
                            PRBool final)
67
744k
{
68
744k
    unsigned char *buf = NULL;
69
744k
    PRBool freeBuf = PR_FALSE;
70
744k
    SECStatus rv;
71
744k
    int i;
72
73
    /*
74
     * We should really have data to process, or we should be trying
75
     * to finish/flush the last block.  (This is an overly paranoid
76
     * check since all callers are in this file and simple inspection
77
     * proves they do it right.  But it could find a bug in future
78
     * modifications/development, that is why it is here.)
79
     */
80
744k
    PORT_Assert((data != NULL && len) || final);
81
82
    /*
83
     * Decrypt this chunk.
84
     *
85
     * XXX If we get an error, we do not want to do the digest or callback,
86
     * but we want to keep decoding.  Or maybe we want to stop decoding
87
     * altogether if there is a callback, because obviously we are not
88
     * sending the data back and they want to know that.
89
     */
90
744k
    if (worker->decryptobj != NULL) {
91
        /* XXX the following lengths should all be longs? */
92
2.26k
        unsigned int inlen;  /* length of data being decrypted */
93
2.26k
        unsigned int outlen; /* length of decrypted data */
94
2.26k
        unsigned int buflen; /* length available for decrypted data */
95
2.26k
        SECItem *plain;
96
97
2.26k
        inlen = len;
98
2.26k
        buflen = sec_PKCS7DecryptLength(worker->decryptobj, inlen, final);
99
2.26k
        if (buflen == 0) {
100
2.26k
            if (inlen == 0) /* no input and no output */
101
2.26k
                return;
102
            /*
103
             * No output is expected, but the input data may be buffered
104
             * so we still have to call Decrypt.
105
             */
106
0
            rv = sec_PKCS7Decrypt(worker->decryptobj, NULL, NULL, 0,
107
0
                                  data, inlen, final);
108
0
            if (rv != SECSuccess) {
109
0
                p7dcx->error = PORT_GetError();
110
0
                return; /* XXX indicate error? */
111
0
            }
112
0
            return;
113
0
        }
114
115
2
        if (p7dcx->cb != NULL) {
116
2
            buf = (unsigned char *)PORT_Alloc(buflen);
117
2
            freeBuf = PR_TRUE;
118
2
            plain = NULL;
119
2
        } else {
120
0
            unsigned long oldlen;
121
122
            /*
123
             * XXX This assumes one level of content only.
124
             * See comment above about nested content types.
125
             * XXX Also, it should work for signedAndEnvelopedData, too!
126
             */
127
0
            plain = &(p7dcx->cinfo->content.envelopedData->encContentInfo.plainContent);
128
129
0
            oldlen = plain->len;
130
0
            if (oldlen == 0) {
131
0
                buf = (unsigned char *)PORT_ArenaAlloc(p7dcx->cinfo->poolp,
132
0
                                                       buflen);
133
0
            } else {
134
0
                buf = (unsigned char *)PORT_ArenaGrow(p7dcx->cinfo->poolp,
135
0
                                                      plain->data,
136
0
                                                      oldlen, oldlen + buflen);
137
0
                if (buf != NULL)
138
0
                    buf += oldlen;
139
0
            }
140
0
            plain->data = buf;
141
0
        }
142
2
        if (buf == NULL) {
143
0
            p7dcx->error = SEC_ERROR_NO_MEMORY;
144
0
            return; /* XXX indicate error? */
145
0
        }
146
2
        rv = sec_PKCS7Decrypt(worker->decryptobj, buf, &outlen, buflen,
147
2
                              data, inlen, final);
148
2
        if (rv != SECSuccess) {
149
0
            p7dcx->error = PORT_GetError();
150
0
            goto cleanup; /* XXX indicate error? */
151
0
        }
152
2
        if (plain != NULL) {
153
0
            PORT_Assert(final || outlen == buflen);
154
0
            plain->len += outlen;
155
0
        }
156
2
        data = buf;
157
2
        len = outlen;
158
2
    }
159
160
    /*
161
     * Update the running digests.
162
     */
163
742k
    if (len) {
164
1.96M
        for (i = 0; i < worker->digcnt; i++) {
165
1.22M
            (*worker->digobjs[i]->update)(worker->digcxs[i], data, len);
166
1.22M
        }
167
742k
    }
168
169
    /*
170
     * Pass back the contents bytes, and free the temporary buffer.
171
     */
172
742k
    if (p7dcx->cb != NULL) {
173
742k
        if (len)
174
742k
            (*p7dcx->cb)(p7dcx->cb_arg, (const char *)data, len);
175
742k
    }
176
177
742k
cleanup:
178
742k
    if (freeBuf && buf != NULL) {
179
2
        PORT_Free(buf);
180
2
    }
181
742k
}
182
183
static void
184
sec_pkcs7_decoder_filter(void *arg, const char *data, unsigned long len,
185
                         int depth, SEC_ASN1EncodingPart data_kind)
186
2.15M
{
187
2.15M
    SEC_PKCS7DecoderContext *p7dcx;
188
2.15M
    struct sec_pkcs7_decoder_worker *worker;
189
190
    /*
191
     * Since we do not handle any nested contents, the only bytes we
192
     * are really interested in are the actual contents bytes (not
193
     * the identifier, length, or end-of-contents bytes).  If we were
194
     * handling nested types we would probably need to do something
195
     * smarter based on depth and data_kind.
196
     */
197
2.15M
    if (data_kind != SEC_ASN1_Contents)
198
1.41M
        return;
199
200
    /*
201
     * The ASN.1 decoder should not even call us with a length of 0.
202
     * Just being paranoid.
203
     */
204
742k
    PORT_Assert(len);
205
742k
    if (len == 0)
206
0
        return;
207
208
742k
    p7dcx = (SEC_PKCS7DecoderContext *)arg;
209
210
    /*
211
     * Handling nested contents would mean that there is a chain
212
     * of workers -- one per each level of content.  The following
213
     * would start with the first worker and loop over them.
214
     */
215
742k
    worker = &(p7dcx->worker);
216
217
742k
    worker->saw_contents = PR_TRUE;
218
219
742k
    sec_pkcs7_decoder_work_data(p7dcx, worker,
220
742k
                                (const unsigned char *)data, len, PR_FALSE);
221
742k
}
222
223
/*
224
 * Create digest contexts for each algorithm in "digestalgs".
225
 * No algorithms is not an error, we just do not do anything.
226
 * An error (like trouble allocating memory), marks the error
227
 * in "p7dcx" and returns SECFailure, which means that our caller
228
 * should just give up altogether.
229
 */
230
static SECStatus
231
sec_pkcs7_decoder_start_digests(SEC_PKCS7DecoderContext *p7dcx, int depth,
232
                                SECAlgorithmID **digestalgs)
233
414
{
234
414
    int i, digcnt;
235
236
414
    p7dcx->worker.digcnt = 0;
237
238
414
    if (digestalgs == NULL)
239
0
        return SECSuccess;
240
241
    /*
242
     * Count the algorithms.
243
     */
244
414
    digcnt = 0;
245
1.24k
    while (digestalgs[digcnt] != NULL)
246
835
        digcnt++;
247
248
    /*
249
     * No algorithms means no work to do.
250
     * Just act as if there were no algorithms specified.
251
     */
252
414
    if (digcnt == 0)
253
0
        return SECSuccess;
254
255
414
    p7dcx->worker.digcxs = (void **)PORT_ArenaAlloc(p7dcx->tmp_poolp,
256
414
                                                    digcnt * sizeof(void *));
257
414
    p7dcx->worker.digobjs = (const SECHashObject **)PORT_ArenaAlloc(p7dcx->tmp_poolp,
258
414
                                                                    digcnt * sizeof(SECHashObject *));
259
414
    if (p7dcx->worker.digcxs == NULL || p7dcx->worker.digobjs == NULL) {
260
0
        p7dcx->error = SEC_ERROR_NO_MEMORY;
261
0
        return SECFailure;
262
0
    }
263
264
414
    p7dcx->worker.depth = depth;
265
266
    /*
267
     * Create a digest context for each algorithm.
268
     */
269
1.24k
    for (i = 0; i < digcnt; i++) {
270
835
        SECAlgorithmID *algid = digestalgs[i];
271
835
        SECOidTag oidTag = SECOID_FindOIDTag(&(algid->algorithm));
272
835
        const SECHashObject *digobj = HASH_GetHashObjectByOidTag(oidTag);
273
835
        void *digcx;
274
275
        /*
276
         * Skip any algorithm we do not even recognize; obviously,
277
         * this could be a problem, but if it is critical then the
278
         * result will just be that the signature does not verify.
279
         * We do not necessarily want to error out here, because
280
         * the particular algorithm may not actually be important,
281
         * but we cannot know that until later.
282
         */
283
835
        if (digobj == NULL) {
284
46
            continue;
285
46
        }
286
287
789
        digcx = (*digobj->create)();
288
789
        if (digcx != NULL) {
289
789
            (*digobj->begin)(digcx);
290
789
            p7dcx->worker.digobjs[p7dcx->worker.digcnt] = digobj;
291
789
            p7dcx->worker.digcxs[p7dcx->worker.digcnt] = digcx;
292
789
            p7dcx->worker.digcnt++;
293
789
        }
294
789
    }
295
296
414
    if (p7dcx->worker.digcnt != 0)
297
414
        SEC_ASN1DecoderSetFilterProc(p7dcx->dcx,
298
414
                                     sec_pkcs7_decoder_filter,
299
414
                                     p7dcx,
300
414
                                     (PRBool)(p7dcx->cb != NULL));
301
414
    return SECSuccess;
302
414
}
303
304
/* destroy any active digest contexts without harvesting results */
305
static void
306
sec_pkcs7_decoder_abort_digests(struct sec_pkcs7_decoder_worker *worker)
307
600k
{
308
600k
    int i;
309
310
600k
    PORT_Assert(worker);
311
600k
    if (!worker) {
312
0
        return;
313
0
    }
314
315
600k
    if (worker->digcnt <= 0 || !worker->digcxs || !worker->digobjs) {
316
600k
        worker->digcnt = 0;
317
600k
        return;
318
600k
    }
319
320
1.10k
    for (i = 0; i < worker->digcnt; i++) {
321
720
        if (worker->digcxs[i] && worker->digobjs[i]) {
322
720
            (*worker->digobjs[i]->destroy)(worker->digcxs[i], PR_TRUE);
323
720
        }
324
720
        worker->digcxs[i] = NULL;
325
720
    }
326
327
381
    worker->digcnt = 0;
328
381
}
329
330
/*
331
 * Close out all of the digest contexts, storing the results in "digestsp".
332
 */
333
static SECStatus
334
sec_pkcs7_decoder_finish_digests(SEC_PKCS7DecoderContext *p7dcx,
335
                                 PLArenaPool *poolp,
336
                                 SECItem ***digestsp)
337
33
{
338
    /*
339
     * XXX Handling nested contents would mean that there is a chain
340
     * of workers -- one per each level of content.  The following
341
     * would want to find the last worker in the chain.
342
     */
343
33
    struct sec_pkcs7_decoder_worker *worker = &(p7dcx->worker);
344
345
    /*
346
     * If no digests, then we have nothing to do.
347
     */
348
33
    if (worker->digcnt == 0) {
349
0
        return SECSuccess;
350
0
    }
351
352
    /*
353
     * No matter what happens after this, we want to stop filtering.
354
     * XXX If we handle nested contents, we only want to stop filtering
355
     * if we are finishing off the *last* worker.
356
     */
357
33
    SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
358
359
    /*
360
     * If we ended up with no contents, just destroy each
361
     * digest context -- they are meaningless and potentially
362
     * confusing, because their presence would imply some content
363
     * was digested.
364
     */
365
33
    if (!worker->saw_contents) {
366
0
        sec_pkcs7_decoder_abort_digests(worker);
367
0
        return SECSuccess;
368
0
    }
369
370
33
    void *mark = PORT_ArenaMark(poolp);
371
372
    /*
373
     * Close out each digest context, saving digest away.
374
     */
375
33
    SECItem **digests =
376
33
        (SECItem **)PORT_ArenaZAlloc(poolp, (worker->digcnt + 1) * sizeof(SECItem *));
377
33
    if (digests == NULL) {
378
0
        p7dcx->error = PORT_GetError();
379
0
        sec_pkcs7_decoder_abort_digests(worker);
380
0
        PORT_ArenaRelease(poolp, mark);
381
0
        return SECFailure;
382
0
    }
383
384
102
    for (int i = 0; i < worker->digcnt; i++) {
385
69
        const SECHashObject *digobj = worker->digobjs[i];
386
69
        digests[i] = SECITEM_AllocItem(poolp, NULL, digobj->length);
387
69
        if (!digests[i]) {
388
0
            p7dcx->error = PORT_GetError();
389
0
            sec_pkcs7_decoder_abort_digests(worker);
390
0
            PORT_ArenaRelease(poolp, mark);
391
0
            return SECFailure;
392
0
        }
393
69
    }
394
395
102
    for (int i = 0; i < worker->digcnt; i++) {
396
69
        void *digcx = worker->digcxs[i];
397
69
        const SECHashObject *digobj = worker->digobjs[i];
398
399
69
        (*digobj->end)(digcx, digests[i]->data, &(digests[i]->len), digests[i]->len);
400
69
        (*digobj->destroy)(digcx, PR_TRUE);
401
69
        worker->digcxs[i] = NULL;
402
69
    }
403
33
    worker->digcnt = 0;
404
33
    *digestsp = digests;
405
406
33
    PORT_ArenaUnmark(poolp, mark);
407
33
    return SECSuccess;
408
33
}
409
410
/*
411
 * XXX Need comment explaining following helper function (which is used
412
 * by sec_pkcs7_decoder_start_decrypt).
413
 */
414
415
static PK11SymKey *
416
sec_pkcs7_decoder_get_recipient_key(SEC_PKCS7DecoderContext *p7dcx,
417
                                    SEC_PKCS7RecipientInfo **recipientinfos,
418
                                    SEC_PKCS7EncryptedContentInfo *enccinfo)
419
0
{
420
0
    SEC_PKCS7RecipientInfo *ri;
421
0
    CERTCertificate *cert = NULL;
422
0
    SECKEYPrivateKey *privkey = NULL;
423
0
    PK11SymKey *bulkkey = NULL;
424
0
    SECOidTag keyalgtag, bulkalgtag, encalgtag;
425
0
    PK11SlotInfo *slot = NULL;
426
427
0
    if (recipientinfos == NULL || recipientinfos[0] == NULL) {
428
0
        p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT;
429
0
        goto no_key_found;
430
0
    }
431
432
0
    cert = PK11_FindCertAndKeyByRecipientList(&slot, recipientinfos, &ri,
433
0
                                              &privkey, p7dcx->pwfn_arg);
434
0
    if (cert == NULL) {
435
0
        p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT;
436
0
        goto no_key_found;
437
0
    }
438
439
0
    ri->cert = cert; /* so we can find it later */
440
0
    PORT_Assert(privkey != NULL);
441
442
0
    keyalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
443
0
    encalgtag = SECOID_GetAlgorithmTag(&(ri->keyEncAlg));
444
0
    if (keyalgtag != encalgtag) {
445
0
        p7dcx->error = SEC_ERROR_PKCS7_KEYALG_MISMATCH;
446
0
        goto no_key_found;
447
0
    }
448
0
    bulkalgtag = SECOID_GetAlgorithmTag(&(enccinfo->contentEncAlg));
449
450
0
    switch (encalgtag) {
451
0
        case SEC_OID_PKCS1_RSA_ENCRYPTION:
452
0
            bulkkey = PK11_PubUnwrapSymKey(privkey, &ri->encKey,
453
0
                                           PK11_AlgtagToMechanism(bulkalgtag),
454
0
                                           CKA_DECRYPT, 0);
455
0
            if (bulkkey == NULL) {
456
0
                p7dcx->error = PORT_GetError();
457
0
                PORT_SetError(0);
458
0
                goto no_key_found;
459
0
            }
460
0
            break;
461
0
        default:
462
0
            p7dcx->error = SEC_ERROR_UNSUPPORTED_KEYALG;
463
0
            break;
464
0
    }
465
466
0
no_key_found:
467
0
    if (privkey != NULL)
468
0
        SECKEY_DestroyPrivateKey(privkey);
469
0
    if (slot != NULL)
470
0
        PK11_FreeSlot(slot);
471
472
0
    return bulkkey;
473
0
}
474
475
/*
476
 * XXX The following comment is old -- the function used to only handle
477
 * EnvelopedData or SignedAndEnvelopedData but now handles EncryptedData
478
 * as well (and it had all of the code of the helper function above
479
 * built into it), though the comment was left as is.  Fix it...
480
 *
481
 * We are just about to decode the content of an EnvelopedData.
482
 * Set up a decryption context so we can decrypt as we go.
483
 * Presumably we are one of the recipients listed in "recipientinfos".
484
 * (XXX And if we are not, or if we have trouble, what should we do?
485
 *  It would be nice to let the decoding still work.  Maybe it should
486
 *  be an error if there is a content callback, but not an error otherwise?)
487
 * The encryption key and related information can be found in "enccinfo".
488
 */
489
static SECStatus
490
sec_pkcs7_decoder_start_decrypt(SEC_PKCS7DecoderContext *p7dcx, int depth,
491
                                SEC_PKCS7RecipientInfo **recipientinfos,
492
                                SEC_PKCS7EncryptedContentInfo *enccinfo,
493
                                PK11SymKey **copy_key_for_signature)
494
18.5k
{
495
18.5k
    PK11SymKey *bulkkey = NULL;
496
18.5k
    sec_PKCS7CipherObject *decryptobj;
497
498
    /*
499
     * If a callback is supplied to retrieve the encryption key,
500
     * for instance, for Encrypted Content infos, then retrieve
501
     * the bulkkey from the callback.  Otherwise, assume that
502
     * we are processing Enveloped or SignedAndEnveloped data
503
     * content infos.
504
     *
505
     * XXX Put an assert here?
506
     */
507
18.5k
    if (SEC_PKCS7ContentType(p7dcx->cinfo) == SEC_OID_PKCS7_ENCRYPTED_DATA) {
508
18.5k
        if (p7dcx->dkcb != NULL) {
509
18.5k
            bulkkey = (*p7dcx->dkcb)(p7dcx->dkcb_arg,
510
18.5k
                                     &(enccinfo->contentEncAlg));
511
18.5k
        }
512
18.5k
        enccinfo->keysize = 0;
513
18.5k
    } else {
514
0
        bulkkey = sec_pkcs7_decoder_get_recipient_key(p7dcx, recipientinfos,
515
0
                                                      enccinfo);
516
0
        if (bulkkey == NULL)
517
0
            goto no_decryption;
518
0
        enccinfo->keysize = PK11_GetKeyStrength(bulkkey,
519
0
                                                &(enccinfo->contentEncAlg));
520
0
    }
521
522
    /*
523
     * XXX I think following should set error in p7dcx and clear set error
524
     * (as used to be done here, or as is done in get_receipient_key above.
525
     */
526
18.5k
    if (bulkkey == NULL) {
527
16.2k
        goto no_decryption;
528
16.2k
    }
529
530
    /*
531
     * We want to make sure decryption is allowed.  This is done via
532
     * a callback specified in SEC_PKCS7DecoderStart().
533
     */
534
2.30k
    if (p7dcx->decrypt_allowed_cb) {
535
2.30k
        if ((*p7dcx->decrypt_allowed_cb)(&(enccinfo->contentEncAlg),
536
2.30k
                                         bulkkey) == PR_FALSE) {
537
5
            p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED;
538
5
            goto no_decryption;
539
5
        }
540
2.30k
    } else {
541
0
        p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED;
542
0
        goto no_decryption;
543
0
    }
544
545
    /*
546
     * When decrypting a signedAndEnvelopedData, the signature also has
547
     * to be decrypted with the bulk encryption key; to avoid having to
548
     * get it all over again later (and do another potentially expensive
549
     * RSA operation), copy it for later signature verification to use.
550
     */
551
2.29k
    if (copy_key_for_signature != NULL)
552
0
        *copy_key_for_signature = PK11_ReferenceSymKey(bulkkey);
553
554
    /*
555
     * Now we have the bulk encryption key (in bulkkey) and the
556
     * the algorithm (in enccinfo->contentEncAlg).  Using those,
557
     * create a decryption context.
558
     */
559
2.29k
    decryptobj = sec_PKCS7CreateDecryptObject(bulkkey,
560
2.29k
                                              &(enccinfo->contentEncAlg));
561
562
    /*
563
     * We are done with (this) bulkkey now.
564
     */
565
2.29k
    PK11_FreeSymKey(bulkkey);
566
2.29k
    bulkkey = NULL;
567
568
2.29k
    if (decryptobj == NULL) {
569
30
        p7dcx->error = PORT_GetError();
570
30
        PORT_SetError(0);
571
30
        goto no_decryption;
572
30
    }
573
574
2.26k
    SEC_ASN1DecoderSetFilterProc(p7dcx->dcx,
575
2.26k
                                 sec_pkcs7_decoder_filter,
576
2.26k
                                 p7dcx,
577
2.26k
                                 (PRBool)(p7dcx->cb != NULL));
578
579
2.26k
    p7dcx->worker.depth = depth;
580
2.26k
    p7dcx->worker.decryptobj = decryptobj;
581
582
2.26k
    return SECSuccess;
583
584
16.2k
no_decryption:
585
16.2k
    PK11_FreeSymKey(bulkkey);
586
    /*
587
     * For some reason (error set already, if appropriate), we cannot
588
     * decrypt the content.  I am not sure what exactly is the right
589
     * thing to do here; in some cases we want to just stop, and in
590
     * others we want to let the decoding finish even though we cannot
591
     * decrypt the content.  My current thinking is that if the caller
592
     * set up a content callback, then they are really interested in
593
     * getting (decrypted) content, and if they cannot they will want
594
     * to know about it.  However, if no callback was specified, then
595
     * maybe it is not important that the decryption failed.
596
     */
597
16.2k
    if (p7dcx->cb != NULL)
598
16.2k
        return SECFailure;
599
0
    else
600
0
        return SECSuccess; /* Let the decoding continue. */
601
16.2k
}
602
603
static SECStatus
604
sec_pkcs7_decoder_finish_decrypt(SEC_PKCS7DecoderContext *p7dcx,
605
                                 PLArenaPool *poolp,
606
                                 SEC_PKCS7EncryptedContentInfo *enccinfo)
607
2.26k
{
608
2.26k
    struct sec_pkcs7_decoder_worker *worker;
609
610
    /*
611
     * XXX Handling nested contents would mean that there is a chain
612
     * of workers -- one per each level of content.  The following
613
     * would want to find the last worker in the chain.
614
     */
615
2.26k
    worker = &(p7dcx->worker);
616
617
    /*
618
     * If no decryption context, then we have nothing to do.
619
     */
620
2.26k
    if (worker->decryptobj == NULL)
621
0
        return SECSuccess;
622
623
    /*
624
     * No matter what happens after this, we want to stop filtering.
625
     * XXX If we handle nested contents, we only want to stop filtering
626
     * if we are finishing off the *last* worker.
627
     */
628
2.26k
    SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
629
630
    /*
631
     * Handle the last block.
632
     */
633
2.26k
    sec_pkcs7_decoder_work_data(p7dcx, worker, NULL, 0, PR_TRUE);
634
635
    /*
636
     * The callback invoked from work_data may have aborted and already
637
     * torn down the decrypt context, so only destroy if it is still set.
638
     */
639
2.26k
    if (worker->decryptobj) {
640
2.26k
        sec_PKCS7DestroyDecryptObject(worker->decryptobj);
641
2.26k
        worker->decryptobj = NULL;
642
2.26k
    }
643
644
2.26k
    return SECSuccess;
645
2.26k
}
646
647
static void
648
sec_pkcs7_decoder_notify(void *arg, PRBool before, void *dest, int depth)
649
381k
{
650
381k
    SEC_PKCS7DecoderContext *p7dcx;
651
381k
    SEC_PKCS7ContentInfo *cinfo;
652
381k
    SEC_PKCS7SignedData *sigd;
653
381k
    SEC_PKCS7EnvelopedData *envd;
654
381k
    SEC_PKCS7SignedAndEnvelopedData *saed;
655
381k
    SEC_PKCS7EncryptedData *encd;
656
381k
    SEC_PKCS7DigestedData *digd;
657
381k
    PRBool after;
658
381k
    SECStatus rv;
659
660
    /*
661
     * Just to make the code easier to read, create an "after" variable
662
     * that is equivalent to "not before".
663
     * (This used to be just the statement "after = !before", but that
664
     * causes a warning on the mac; to avoid that, we do it the long way.)
665
     */
666
381k
    if (before)
667
218k
        after = PR_FALSE;
668
162k
    else
669
162k
        after = PR_TRUE;
670
671
381k
    p7dcx = (SEC_PKCS7DecoderContext *)arg;
672
381k
    if (!p7dcx) {
673
0
        return;
674
0
    }
675
676
381k
    cinfo = p7dcx->cinfo;
677
678
381k
    if (!cinfo) {
679
0
        return;
680
0
    }
681
682
381k
    if (cinfo->contentTypeTag == NULL) {
683
94.8k
        if (after && dest == &(cinfo->contentType))
684
36.5k
            cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
685
94.8k
        return;
686
94.8k
    }
687
688
286k
    switch (cinfo->contentTypeTag->offset) {
689
30.8k
        case SEC_OID_PKCS7_SIGNED_DATA:
690
30.8k
            sigd = cinfo->content.signedData;
691
30.8k
            if (sigd == NULL)
692
487
                break;
693
694
30.3k
            if (sigd->contentInfo.contentTypeTag == NULL) {
695
29.8k
                if (after && dest == &(sigd->contentInfo.contentType))
696
444
                    sigd->contentInfo.contentTypeTag =
697
444
                        SECOID_FindOID(&(sigd->contentInfo.contentType));
698
29.8k
                break;
699
29.8k
            }
700
701
            /*
702
             * We only set up a filtering digest if the content is
703
             * plain DATA; anything else needs more work because a
704
             * second pass is required to produce a DER encoding from
705
             * an input that can be BER encoded.  (This is a requirement
706
             * of PKCS7 that is unfortunate, but there you have it.)
707
             *
708
             * XXX Also, since we stop here if this is not DATA, the
709
             * inner content is not getting processed at all.  Someday
710
             * we may want to fix that.
711
             */
712
447
            if (sigd->contentInfo.contentTypeTag->offset != SEC_OID_PKCS7_DATA) {
713
                /* XXX Set an error in p7dcx->error */
714
0
                SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
715
0
                break;
716
0
            }
717
718
            /*
719
             * Just before the content, we want to set up a digest context
720
             * for each digest algorithm listed, and start a filter which
721
             * will run all of the contents bytes through that digest.
722
             */
723
447
            if (before && dest == &(sigd->contentInfo.content)) {
724
414
                rv = sec_pkcs7_decoder_start_digests(p7dcx, depth,
725
414
                                                     sigd->digestAlgorithms);
726
414
                if (rv != SECSuccess)
727
0
                    SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
728
729
414
                break;
730
414
            }
731
732
            /*
733
             * XXX To handle nested types, here is where we would want
734
             * to check for inner boundaries that need handling.
735
             */
736
737
            /*
738
             * Are we done?
739
             */
740
33
            if (after && dest == &(sigd->contentInfo.content)) {
741
                /*
742
                 * Close out the digest contexts.  We ignore any error
743
                 * because we are stopping anyway; the error status left
744
                 * behind in p7dcx will be seen by outer functions.
745
                 */
746
33
                (void)sec_pkcs7_decoder_finish_digests(p7dcx, cinfo->poolp,
747
33
                                                       &(sigd->digests));
748
749
                /*
750
                 * XXX To handle nested contents, we would need to remove
751
                 * the worker from the chain (and free it).
752
                 */
753
754
                /*
755
                 * Stop notify.
756
                 */
757
33
                SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
758
33
            }
759
33
            break;
760
761
2.01k
        case SEC_OID_PKCS7_ENVELOPED_DATA:
762
2.01k
            envd = cinfo->content.envelopedData;
763
2.01k
            if (envd == NULL)
764
2.01k
                break;
765
766
0
            if (envd->encContentInfo.contentTypeTag == NULL) {
767
0
                if (after && dest == &(envd->encContentInfo.contentType))
768
0
                    envd->encContentInfo.contentTypeTag =
769
0
                        SECOID_FindOID(&(envd->encContentInfo.contentType));
770
0
                break;
771
0
            }
772
773
            /*
774
             * Just before the content, we want to set up a decryption
775
             * context, and start a filter which will run all of the
776
             * contents bytes through it to determine the plain content.
777
             */
778
0
            if (before && dest == &(envd->encContentInfo.encContent)) {
779
0
                rv = sec_pkcs7_decoder_start_decrypt(p7dcx, depth,
780
0
                                                     envd->recipientInfos,
781
0
                                                     &(envd->encContentInfo),
782
0
                                                     NULL);
783
0
                if (rv != SECSuccess)
784
0
                    SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
785
786
0
                break;
787
0
            }
788
789
            /*
790
             * Are we done?
791
             */
792
0
            if (after && dest == &(envd->encContentInfo.encContent)) {
793
                /*
794
                 * Close out the decryption context.  We ignore any error
795
                 * because we are stopping anyway; the error status left
796
                 * behind in p7dcx will be seen by outer functions.
797
                 */
798
0
                (void)sec_pkcs7_decoder_finish_decrypt(p7dcx, cinfo->poolp,
799
0
                                                       &(envd->encContentInfo));
800
801
                /*
802
                 * XXX To handle nested contents, we would need to remove
803
                 * the worker from the chain (and free it).
804
                 */
805
806
                /*
807
                 * Stop notify.
808
                 */
809
0
                SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
810
0
            }
811
0
            break;
812
813
2.18k
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
814
2.18k
            saed = cinfo->content.signedAndEnvelopedData;
815
2.18k
            if (saed == NULL)
816
2.18k
                break;
817
818
0
            if (saed->encContentInfo.contentTypeTag == NULL) {
819
0
                if (after && dest == &(saed->encContentInfo.contentType))
820
0
                    saed->encContentInfo.contentTypeTag =
821
0
                        SECOID_FindOID(&(saed->encContentInfo.contentType));
822
0
                break;
823
0
            }
824
825
            /*
826
             * Just before the content, we want to set up a decryption
827
             * context *and* digest contexts, and start a filter which
828
             * will run all of the contents bytes through both.
829
             */
830
0
            if (before && dest == &(saed->encContentInfo.encContent)) {
831
0
                rv = sec_pkcs7_decoder_start_decrypt(p7dcx, depth,
832
0
                                                     saed->recipientInfos,
833
0
                                                     &(saed->encContentInfo),
834
0
                                                     &(saed->sigKey));
835
0
                if (rv == SECSuccess)
836
0
                    rv = sec_pkcs7_decoder_start_digests(p7dcx, depth,
837
0
                                                         saed->digestAlgorithms);
838
0
                if (rv != SECSuccess)
839
0
                    SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
840
841
0
                break;
842
0
            }
843
844
            /*
845
             * Are we done?
846
             */
847
0
            if (after && dest == &(saed->encContentInfo.encContent)) {
848
                /*
849
                 * Close out the decryption and digests contexts.
850
                 * We ignore any errors because we are stopping anyway;
851
                 * the error status left behind in p7dcx will be seen by
852
                 * outer functions.
853
                 *
854
                 * Note that the decrypt stuff must be called first;
855
                 * it may have a last buffer to do which in turn has
856
                 * to be added to the digest.
857
                 */
858
0
                (void)sec_pkcs7_decoder_finish_decrypt(p7dcx, cinfo->poolp,
859
0
                                                       &(saed->encContentInfo));
860
0
                (void)sec_pkcs7_decoder_finish_digests(p7dcx, cinfo->poolp,
861
0
                                                       &(saed->digests));
862
863
                /*
864
                 * XXX To handle nested contents, we would need to remove
865
                 * the worker from the chain (and free it).
866
                 */
867
868
                /*
869
                 * Stop notify.
870
                 */
871
0
                SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
872
0
            }
873
0
            break;
874
875
6.33k
        case SEC_OID_PKCS7_DIGESTED_DATA:
876
6.33k
            digd = cinfo->content.digestedData;
877
6.33k
            if (digd == NULL)
878
6.30k
                break;
879
880
            /*
881
             * XXX Want to do the digest or not?  Maybe future enhancement...
882
             */
883
27
            if (before && dest == &(digd->contentInfo.content.data)) {
884
2
                SEC_ASN1DecoderSetFilterProc(p7dcx->dcx, sec_pkcs7_decoder_filter,
885
2
                                             p7dcx,
886
2
                                             (PRBool)(p7dcx->cb != NULL));
887
2
                break;
888
2
            }
889
890
            /*
891
             * Are we done?
892
             */
893
25
            if (after && dest == &(digd->contentInfo.content.data)) {
894
1
                SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
895
1
            }
896
25
            break;
897
898
243k
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
899
243k
            encd = cinfo->content.encryptedData;
900
901
243k
            if (!encd) {
902
18.5k
                break;
903
18.5k
            }
904
905
            /*
906
             * XXX If the decryption key callback is set, we want to start
907
             * the decryption.  If the callback is not set, we will treat the
908
             * content as plain data, since we do not have the key.
909
             *
910
             * Is this the proper thing to do?
911
             */
912
224k
            if (before && dest == &(encd->encContentInfo.encContent)) {
913
                /*
914
                 * Start the encryption process if the decryption key callback
915
                 * is present.  Otherwise, treat the content like plain data.
916
                 */
917
18.5k
                rv = SECSuccess;
918
18.5k
                if (p7dcx->dkcb != NULL) {
919
18.5k
                    rv = sec_pkcs7_decoder_start_decrypt(p7dcx, depth, NULL,
920
18.5k
                                                         &(encd->encContentInfo),
921
18.5k
                                                         NULL);
922
18.5k
                }
923
924
18.5k
                if (rv != SECSuccess)
925
16.2k
                    SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
926
927
18.5k
                break;
928
18.5k
            }
929
930
            /*
931
             * Are we done?
932
             */
933
206k
            if (after && dest == &(encd->encContentInfo.encContent)) {
934
                /*
935
                 * Close out the decryption context.  We ignore any error
936
                 * because we are stopping anyway; the error status left
937
                 * behind in p7dcx will be seen by outer functions.
938
                 */
939
2.26k
                (void)sec_pkcs7_decoder_finish_decrypt(p7dcx, cinfo->poolp,
940
2.26k
                                                       &(encd->encContentInfo));
941
942
                /*
943
                 * Stop notify.
944
                 */
945
2.26k
                SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
946
2.26k
            }
947
206k
            break;
948
949
1.35k
        case SEC_OID_PKCS7_DATA:
950
            /*
951
             * If a output callback has been specified, we want to set the filter
952
             * to call the callback.  This is taken care of in
953
             * sec_pkcs7_decoder_start_decrypt() or
954
             * sec_pkcs7_decoder_start_digests() for the other content types.
955
             */
956
957
1.35k
            if (before && dest == &(cinfo->content.data)) {
958
959
                /*
960
                 * Set the filter proc up.
961
                 */
962
845
                SEC_ASN1DecoderSetFilterProc(p7dcx->dcx,
963
845
                                             sec_pkcs7_decoder_filter,
964
845
                                             p7dcx,
965
845
                                             (PRBool)(p7dcx->cb != NULL));
966
845
                break;
967
845
            }
968
969
509
            if (after && dest == &(cinfo->content.data)) {
970
                /*
971
                 * Time to clean up after ourself, stop the Notify and Filter
972
                 * procedures.
973
                 */
974
509
                SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
975
509
                SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
976
509
            }
977
509
            break;
978
979
244
        default:
980
244
            SEC_ASN1DecoderClearNotifyProc(p7dcx->dcx);
981
244
            break;
982
286k
    }
983
286k
}
984
985
SEC_PKCS7DecoderContext *
986
SEC_PKCS7DecoderStart(SEC_PKCS7DecoderContentCallback cb, void *cb_arg,
987
                      SECKEYGetPasswordKey pwfn, void *pwfn_arg,
988
                      SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb,
989
                      void *decrypt_key_cb_arg,
990
                      SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb)
991
36.7k
{
992
36.7k
    SEC_PKCS7DecoderContext *p7dcx;
993
36.7k
    SEC_ASN1DecoderContext *dcx;
994
36.7k
    SEC_PKCS7ContentInfo *cinfo;
995
36.7k
    PLArenaPool *poolp;
996
997
36.7k
    poolp = PORT_NewArena(1024); /* XXX what is right value? */
998
36.7k
    if (poolp == NULL)
999
0
        return NULL;
1000
1001
36.7k
    cinfo = (SEC_PKCS7ContentInfo *)PORT_ArenaZAlloc(poolp, sizeof(*cinfo));
1002
36.7k
    if (cinfo == NULL) {
1003
0
        PORT_FreeArena(poolp, PR_FALSE);
1004
0
        return NULL;
1005
0
    }
1006
1007
36.7k
    cinfo->poolp = poolp;
1008
36.7k
    cinfo->pwfn = pwfn;
1009
36.7k
    cinfo->pwfn_arg = pwfn_arg;
1010
36.7k
    cinfo->created = PR_FALSE;
1011
36.7k
    cinfo->refCount = 1;
1012
1013
36.7k
    p7dcx =
1014
36.7k
        (SEC_PKCS7DecoderContext *)PORT_ZAlloc(sizeof(SEC_PKCS7DecoderContext));
1015
36.7k
    if (p7dcx == NULL) {
1016
0
        PORT_FreeArena(poolp, PR_FALSE);
1017
0
        return NULL;
1018
0
    }
1019
1020
36.7k
    p7dcx->tmp_poolp = PORT_NewArena(1024); /* XXX what is right value? */
1021
36.7k
    if (p7dcx->tmp_poolp == NULL) {
1022
0
        PORT_Free(p7dcx);
1023
0
        PORT_FreeArena(poolp, PR_FALSE);
1024
0
        return NULL;
1025
0
    }
1026
1027
36.7k
    dcx = SEC_ASN1DecoderStart(poolp, cinfo, sec_PKCS7ContentInfoTemplate);
1028
36.7k
    if (dcx == NULL) {
1029
0
        PORT_FreeArena(p7dcx->tmp_poolp, PR_FALSE);
1030
0
        PORT_Free(p7dcx);
1031
0
        PORT_FreeArena(poolp, PR_FALSE);
1032
0
        return NULL;
1033
0
    }
1034
1035
36.7k
    SEC_ASN1DecoderSetNotifyProc(dcx, sec_pkcs7_decoder_notify, p7dcx);
1036
1037
36.7k
    p7dcx->dcx = dcx;
1038
36.7k
    p7dcx->cinfo = cinfo;
1039
36.7k
    p7dcx->cb = cb;
1040
36.7k
    p7dcx->cb_arg = cb_arg;
1041
36.7k
    p7dcx->pwfn = pwfn;
1042
36.7k
    p7dcx->pwfn_arg = pwfn_arg;
1043
36.7k
    p7dcx->dkcb = decrypt_key_cb;
1044
36.7k
    p7dcx->dkcb_arg = decrypt_key_cb_arg;
1045
36.7k
    p7dcx->decrypt_allowed_cb = decrypt_allowed_cb;
1046
1047
36.7k
    return p7dcx;
1048
36.7k
}
1049
1050
/*
1051
 * Do the next chunk of PKCS7 decoding.  If there is a problem, set
1052
 * an error and return a failure status.  Note that in the case of
1053
 * an error, this routine is still prepared to be called again and
1054
 * again in case that is the easiest route for our caller to take.
1055
 * We simply detect it and do not do anything except keep setting
1056
 * that error in case our caller has not noticed it yet...
1057
 */
1058
SECStatus
1059
SEC_PKCS7DecoderUpdate(SEC_PKCS7DecoderContext *p7dcx,
1060
                       const char *buf, unsigned long len)
1061
3.65M
{
1062
3.65M
    if (!p7dcx) {
1063
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
1064
0
        return SECFailure;
1065
0
    }
1066
1067
3.65M
    if (p7dcx->cinfo != NULL && p7dcx->dcx != NULL) {
1068
3.08M
        PORT_Assert(p7dcx->error == 0);
1069
3.08M
        if (p7dcx->error == 0) {
1070
3.08M
            if (SEC_ASN1DecoderUpdate(p7dcx->dcx, buf, len) != SECSuccess) {
1071
490
                p7dcx->error = PORT_GetError();
1072
490
                PORT_Assert(p7dcx->error);
1073
490
                if (p7dcx->error == 0)
1074
0
                    p7dcx->error = -1;
1075
490
            }
1076
3.08M
        }
1077
3.08M
    }
1078
1079
3.65M
    if (p7dcx->error) {
1080
564k
        sec_pkcs7_decoder_abort_digests(&p7dcx->worker);
1081
564k
        if (p7dcx->worker.decryptobj) {
1082
0
            sec_PKCS7DestroyDecryptObject(p7dcx->worker.decryptobj);
1083
0
            p7dcx->worker.decryptobj = NULL;
1084
0
        }
1085
564k
        if (p7dcx->dcx != NULL) {
1086
525
            (void)SEC_ASN1DecoderFinish(p7dcx->dcx);
1087
525
            p7dcx->dcx = NULL;
1088
525
        }
1089
564k
        if (p7dcx->cinfo != NULL) {
1090
525
            SEC_PKCS7DestroyContentInfo(p7dcx->cinfo);
1091
525
            p7dcx->cinfo = NULL;
1092
525
        }
1093
564k
        PORT_SetError(p7dcx->error);
1094
564k
        return SECFailure;
1095
564k
    }
1096
1097
3.08M
    return SECSuccess;
1098
3.65M
}
1099
1100
SEC_PKCS7ContentInfo *
1101
SEC_PKCS7DecoderFinish(SEC_PKCS7DecoderContext *p7dcx)
1102
36.7k
{
1103
36.7k
    SEC_PKCS7ContentInfo *cinfo;
1104
1105
36.7k
    sec_pkcs7_decoder_abort_digests(&p7dcx->worker);
1106
36.7k
    cinfo = p7dcx->cinfo;
1107
36.7k
    if (p7dcx->dcx != NULL) {
1108
36.2k
        if (SEC_ASN1DecoderFinish(p7dcx->dcx) != SECSuccess) {
1109
796
            SEC_PKCS7DestroyContentInfo(cinfo);
1110
796
            cinfo = NULL;
1111
796
        }
1112
36.2k
    }
1113
    /* free any NSS data structures */
1114
36.7k
    if (p7dcx->worker.decryptobj) {
1115
1
        sec_PKCS7DestroyDecryptObject(p7dcx->worker.decryptobj);
1116
1
        p7dcx->worker.decryptobj = NULL;
1117
1
    }
1118
1119
36.7k
    PORT_FreeArena(p7dcx->tmp_poolp, PR_FALSE);
1120
36.7k
    PORT_Free(p7dcx);
1121
36.7k
    return cinfo;
1122
36.7k
}
1123
1124
SEC_PKCS7ContentInfo *
1125
SEC_PKCS7DecodeItem(SECItem *p7item,
1126
                    SEC_PKCS7DecoderContentCallback cb, void *cb_arg,
1127
                    SECKEYGetPasswordKey pwfn, void *pwfn_arg,
1128
                    SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb,
1129
                    void *decrypt_key_cb_arg,
1130
                    SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb)
1131
0
{
1132
0
    SEC_PKCS7DecoderContext *p7dcx;
1133
1134
0
    p7dcx = SEC_PKCS7DecoderStart(cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb,
1135
0
                                  decrypt_key_cb_arg, decrypt_allowed_cb);
1136
0
    if (!p7dcx) {
1137
        /* error code is set */
1138
0
        return NULL;
1139
0
    }
1140
0
    (void)SEC_PKCS7DecoderUpdate(p7dcx, (char *)p7item->data, p7item->len);
1141
0
    return SEC_PKCS7DecoderFinish(p7dcx);
1142
0
}
1143
1144
/*
1145
 * Abort the ASN.1 stream. Used by pkcs 12
1146
 */
1147
void
1148
SEC_PKCS7DecoderAbort(SEC_PKCS7DecoderContext *p7dcx, int error)
1149
15
{
1150
15
    PORT_Assert(p7dcx);
1151
15
    if (!p7dcx) {
1152
0
        return;
1153
0
    }
1154
1155
    /* ensure any streaming helpers are torn down */
1156
15
    sec_pkcs7_decoder_abort_digests(&p7dcx->worker);
1157
15
    if (p7dcx->worker.decryptobj) {
1158
2
        sec_PKCS7DestroyDecryptObject(p7dcx->worker.decryptobj);
1159
2
        p7dcx->worker.decryptobj = NULL;
1160
2
    }
1161
1162
15
    SEC_ASN1DecoderAbort(p7dcx->dcx, error);
1163
15
}
1164
1165
/*
1166
 * If the thing contains any certs or crls return true; false otherwise.
1167
 */
1168
PRBool
1169
SEC_PKCS7ContainsCertsOrCrls(SEC_PKCS7ContentInfo *cinfo)
1170
0
{
1171
0
    SECOidTag kind;
1172
0
    SECItem **certs;
1173
0
    CERTSignedCrl **crls;
1174
1175
0
    kind = SEC_PKCS7ContentType(cinfo);
1176
0
    switch (kind) {
1177
0
        default:
1178
0
        case SEC_OID_PKCS7_DATA:
1179
0
        case SEC_OID_PKCS7_DIGESTED_DATA:
1180
0
        case SEC_OID_PKCS7_ENVELOPED_DATA:
1181
0
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
1182
0
            return PR_FALSE;
1183
0
        case SEC_OID_PKCS7_SIGNED_DATA:
1184
0
            certs = cinfo->content.signedData->rawCerts;
1185
0
            crls = cinfo->content.signedData->crls;
1186
0
            break;
1187
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
1188
0
            certs = cinfo->content.signedAndEnvelopedData->rawCerts;
1189
0
            crls = cinfo->content.signedAndEnvelopedData->crls;
1190
0
            break;
1191
0
    }
1192
1193
    /*
1194
     * I know this could be collapsed, but I was in a mood to be explicit.
1195
     */
1196
0
    if (certs != NULL && certs[0] != NULL)
1197
0
        return PR_TRUE;
1198
0
    else if (crls != NULL && crls[0] != NULL)
1199
0
        return PR_TRUE;
1200
0
    else
1201
0
        return PR_FALSE;
1202
0
}
1203
1204
/* return the content length...could use GetContent, however we
1205
 * need the encrypted content length
1206
 */
1207
PRBool
1208
SEC_PKCS7IsContentEmpty(SEC_PKCS7ContentInfo *cinfo, unsigned int minLen)
1209
0
{
1210
0
    SECItem *item = NULL;
1211
1212
0
    if (cinfo == NULL) {
1213
0
        return PR_TRUE;
1214
0
    }
1215
1216
0
    switch (SEC_PKCS7ContentType(cinfo)) {
1217
0
        case SEC_OID_PKCS7_DATA:
1218
0
            item = cinfo->content.data;
1219
0
            break;
1220
0
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
1221
0
            item = &cinfo->content.encryptedData->encContentInfo.encContent;
1222
0
            break;
1223
0
        default:
1224
            /* add other types */
1225
0
            return PR_FALSE;
1226
0
    }
1227
1228
0
    if (!item) {
1229
0
        return PR_TRUE;
1230
0
    } else if (item->len <= minLen) {
1231
0
        return PR_TRUE;
1232
0
    }
1233
1234
0
    return PR_FALSE;
1235
0
}
1236
1237
PRBool
1238
SEC_PKCS7ContentIsEncrypted(SEC_PKCS7ContentInfo *cinfo)
1239
0
{
1240
0
    SECOidTag kind;
1241
1242
0
    kind = SEC_PKCS7ContentType(cinfo);
1243
0
    switch (kind) {
1244
0
        default:
1245
0
        case SEC_OID_PKCS7_DATA:
1246
0
        case SEC_OID_PKCS7_DIGESTED_DATA:
1247
0
        case SEC_OID_PKCS7_SIGNED_DATA:
1248
0
            return PR_FALSE;
1249
0
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
1250
0
        case SEC_OID_PKCS7_ENVELOPED_DATA:
1251
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
1252
0
            return PR_TRUE;
1253
0
    }
1254
0
}
1255
1256
/*
1257
 * If the PKCS7 content has a signature (not just *could* have a signature)
1258
 * return true; false otherwise.  This can/should be called before calling
1259
 * VerifySignature, which will always indicate failure if no signature is
1260
 * present, but that does not mean there even was a signature!
1261
 * Note that the content itself can be empty (detached content was sent
1262
 * another way); it is the presence of the signature that matters.
1263
 */
1264
PRBool
1265
SEC_PKCS7ContentIsSigned(SEC_PKCS7ContentInfo *cinfo)
1266
0
{
1267
0
    SECOidTag kind;
1268
0
    SEC_PKCS7SignerInfo **signerinfos;
1269
1270
0
    kind = SEC_PKCS7ContentType(cinfo);
1271
0
    switch (kind) {
1272
0
        default:
1273
0
        case SEC_OID_PKCS7_DATA:
1274
0
        case SEC_OID_PKCS7_DIGESTED_DATA:
1275
0
        case SEC_OID_PKCS7_ENVELOPED_DATA:
1276
0
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
1277
0
            return PR_FALSE;
1278
0
        case SEC_OID_PKCS7_SIGNED_DATA:
1279
0
            signerinfos = cinfo->content.signedData->signerInfos;
1280
0
            break;
1281
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
1282
0
            signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos;
1283
0
            break;
1284
0
    }
1285
1286
    /*
1287
     * I know this could be collapsed; but I kind of think it will get
1288
     * more complicated before I am finished, so...
1289
     */
1290
0
    if (signerinfos != NULL && signerinfos[0] != NULL)
1291
0
        return PR_TRUE;
1292
0
    else
1293
0
        return PR_FALSE;
1294
0
}
1295
1296
/*
1297
 * sec_pkcs7_verify_signature
1298
 *
1299
 *      Look at a PKCS7 contentInfo and check if the signature is good.
1300
 *      The digest was either calculated earlier (and is stored in the
1301
 *      contentInfo itself) or is passed in via "detached_digest".
1302
 *
1303
 *      The verification checks that the signing cert is valid and trusted
1304
 *      for the purpose specified by "certusage" at
1305
 *      - "*atTime" if "atTime" is not null, or
1306
 *      - the signing time if the signing time is available in "cinfo", or
1307
 *      - the current time (as returned by PR_Now).
1308
 *
1309
 *      In addition, if "keepcerts" is true, add any new certificates found
1310
 *      into our local database.
1311
 *
1312
 * XXX Each place which returns PR_FALSE should be sure to have a good
1313
 * error set for inspection by the caller.  Alternatively, we could create
1314
 * an enumeration of success and each type of failure and return that
1315
 * instead of a boolean.  For now, the default in a bad situation is to
1316
 * set the error to SEC_ERROR_PKCS7_BAD_SIGNATURE.  But this should be
1317
 * reviewed; better (more specific) errors should be possible (to distinguish
1318
 * a signature failure from a badly-formed pkcs7 signedData, for example).
1319
 * Some of the errors should probably just be SEC_ERROR_BAD_SIGNATURE,
1320
 * but that has a less helpful error string associated with it right now;
1321
 * if/when that changes, review and change these as needed.
1322
 *
1323
 * XXX This is broken wrt signedAndEnvelopedData.  In that case, the
1324
 * message digest is doubly encrypted -- first encrypted with the signer
1325
 * private key but then again encrypted with the bulk encryption key used
1326
 * to encrypt the content.  So before we can pass the digest to VerifyDigest,
1327
 * we need to decrypt it with the bulk encryption key.  Also, in this case,
1328
 * there should be NO authenticatedAttributes (signerinfo->authAttr should
1329
 * be NULL).
1330
 */
1331
static PRBool
1332
sec_pkcs7_verify_signature(SEC_PKCS7ContentInfo *cinfo,
1333
                           SECCertUsage certusage,
1334
                           const SECItem *detached_digest,
1335
                           HASH_HashType digest_type,
1336
                           PRBool keepcerts,
1337
                           const PRTime *atTime)
1338
0
{
1339
0
    SECAlgorithmID **digestalgs, *bulkid;
1340
0
    const SECItem *digest;
1341
0
    SECItem **digests;
1342
0
    SECItem **rawcerts;
1343
0
    SEC_PKCS7SignerInfo **signerinfos, *signerinfo;
1344
0
    CERTCertificate *cert, **certs;
1345
0
    PRBool goodsig;
1346
0
    CERTCertDBHandle *certdb, *defaultdb;
1347
0
    SECOidTag encTag, digestTag;
1348
0
    HASH_HashType found_type;
1349
0
    int i, certcount;
1350
0
    SECKEYPublicKey *publickey;
1351
0
    SECItem *content_type;
1352
0
    PK11SymKey *sigkey;
1353
0
    SECItem *encoded_stime;
1354
0
    PRTime stime;
1355
0
    PRTime verificationTime;
1356
0
    SECStatus rv;
1357
1358
    /*
1359
     * Everything needed in order to "goto done" safely.
1360
     */
1361
0
    goodsig = PR_FALSE;
1362
0
    certcount = 0;
1363
0
    cert = NULL;
1364
0
    certs = NULL;
1365
0
    certdb = NULL;
1366
0
    defaultdb = CERT_GetDefaultCertDB();
1367
0
    publickey = NULL;
1368
1369
0
    if (!SEC_PKCS7ContentIsSigned(cinfo)) {
1370
0
        PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
1371
0
        goto done;
1372
0
    }
1373
1374
0
    PORT_Assert(cinfo->contentTypeTag != NULL);
1375
1376
0
    switch (cinfo->contentTypeTag->offset) {
1377
0
        default:
1378
0
        case SEC_OID_PKCS7_DATA:
1379
0
        case SEC_OID_PKCS7_DIGESTED_DATA:
1380
0
        case SEC_OID_PKCS7_ENVELOPED_DATA:
1381
0
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
1382
            /* Could only get here if SEC_PKCS7ContentIsSigned is broken. */
1383
0
            PORT_Assert(0);
1384
0
        case SEC_OID_PKCS7_SIGNED_DATA: {
1385
0
            SEC_PKCS7SignedData *sdp;
1386
1387
0
            sdp = cinfo->content.signedData;
1388
0
            digestalgs = sdp->digestAlgorithms;
1389
0
            digests = sdp->digests;
1390
0
            rawcerts = sdp->rawCerts;
1391
0
            signerinfos = sdp->signerInfos;
1392
0
            content_type = &(sdp->contentInfo.contentType);
1393
0
            sigkey = NULL;
1394
0
            bulkid = NULL;
1395
0
        } break;
1396
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
1397
0
            SEC_PKCS7SignedAndEnvelopedData *saedp;
1398
1399
0
            saedp = cinfo->content.signedAndEnvelopedData;
1400
0
            digestalgs = saedp->digestAlgorithms;
1401
0
            digests = saedp->digests;
1402
0
            rawcerts = saedp->rawCerts;
1403
0
            signerinfos = saedp->signerInfos;
1404
0
            content_type = &(saedp->encContentInfo.contentType);
1405
0
            sigkey = saedp->sigKey;
1406
0
            bulkid = &(saedp->encContentInfo.contentEncAlg);
1407
0
        } break;
1408
0
    }
1409
1410
0
    if ((signerinfos == NULL) || (signerinfos[0] == NULL)) {
1411
0
        PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
1412
0
        goto done;
1413
0
    }
1414
1415
    /*
1416
     * XXX Need to handle multiple signatures; checking them is easy,
1417
     * but what should be the semantics here (like, return value)?
1418
     */
1419
0
    if (signerinfos[1] != NULL) {
1420
0
        PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
1421
0
        goto done;
1422
0
    }
1423
1424
0
    signerinfo = signerinfos[0];
1425
1426
    /*
1427
     * XXX I would like to just pass the issuerAndSN, along with the rawcerts
1428
     * and crls, to some function that did all of this certificate stuff
1429
     * (open/close the database if necessary, verifying the certs, etc.)
1430
     * and gave me back a cert pointer if all was good.
1431
     */
1432
0
    certdb = defaultdb;
1433
0
    if (certdb == NULL) {
1434
0
        goto done;
1435
0
    }
1436
1437
0
    certcount = 0;
1438
0
    if (rawcerts != NULL) {
1439
0
        for (; rawcerts[certcount] != NULL; certcount++) {
1440
            /* just counting */
1441
0
        }
1442
0
    }
1443
1444
    /*
1445
     * Note that the result of this is that each cert in "certs"
1446
     * needs to be destroyed.
1447
     */
1448
0
    rv = CERT_ImportCerts(certdb, certusage, certcount, rawcerts, &certs,
1449
0
                          keepcerts, PR_FALSE, NULL);
1450
0
    if (rv != SECSuccess) {
1451
0
        goto done;
1452
0
    }
1453
1454
    /*
1455
     * This cert will also need to be freed, but since we save it
1456
     * in signerinfo for later, we do not want to destroy it when
1457
     * we leave this function -- we let the clean-up of the entire
1458
     * cinfo structure later do the destroy of this cert.
1459
     */
1460
0
    cert = CERT_FindCertByIssuerAndSN(certdb, signerinfo->issuerAndSN);
1461
0
    if (cert == NULL) {
1462
0
        goto done;
1463
0
    }
1464
1465
0
    signerinfo->cert = cert;
1466
1467
    /*
1468
     * Get and convert the signing time; if available, it will be used
1469
     * both on the cert verification and for importing the sender
1470
     * email profile.
1471
     */
1472
0
    encoded_stime = SEC_PKCS7GetSigningTime(cinfo);
1473
0
    if (encoded_stime != NULL) {
1474
0
        if (DER_DecodeTimeChoice(&stime, encoded_stime) != SECSuccess)
1475
0
            encoded_stime = NULL; /* conversion failed, so pretend none */
1476
0
    }
1477
1478
    /*
1479
     * XXX  This uses the signing time, if available.  Additionally, we
1480
     * might want to, if there is no signing time, get the message time
1481
     * from the mail header itself, and use that.  That would require
1482
     * a change to our interface though, and for S/MIME callers to pass
1483
     * in a time (and for non-S/MIME callers to pass in nothing, or
1484
     * maybe make them pass in the current time, always?).
1485
     */
1486
0
    if (atTime) {
1487
0
        verificationTime = *atTime;
1488
0
    } else if (encoded_stime != NULL) {
1489
0
        verificationTime = stime;
1490
0
    } else {
1491
0
        verificationTime = PR_Now();
1492
0
    }
1493
0
    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, verificationTime,
1494
0
                        cinfo->pwfn_arg, NULL) != SECSuccess) {
1495
        /*
1496
         * XXX Give the user an option to check the signature anyway?
1497
         * If we want to do this, need to give a way to leave and display
1498
         * some dialog and get the answer and come back through (or do
1499
         * the rest of what we do below elsewhere, maybe by putting it
1500
         * in a function that we call below and could call from a dialog
1501
         * finish handler).
1502
         */
1503
0
        goto savecert;
1504
0
    }
1505
1506
0
    publickey = CERT_ExtractPublicKey(cert);
1507
0
    if (publickey == NULL)
1508
0
        goto done;
1509
1510
    /*
1511
     * XXX No!  If digests is empty, see if we can create it now by
1512
     * digesting the contents.  This is necessary if we want to allow
1513
     * somebody to do a simple decode (without filtering, etc.) and
1514
     * then later call us here to do the verification.
1515
     * OR, we can just specify that the interface to this routine
1516
     * *requires* that the digest(s) be done before calling and either
1517
     * stashed in the struct itself or passed in explicitly (as would
1518
     * be done for detached contents).
1519
     */
1520
0
    if ((digests == NULL || digests[0] == NULL) && (detached_digest == NULL || detached_digest->data == NULL))
1521
0
        goto done;
1522
1523
    /*
1524
     * Find and confirm digest algorithm.
1525
     */
1526
0
    digestTag = SECOID_FindOIDTag(&(signerinfo->digestAlg.algorithm));
1527
1528
    /* make sure we understand the digest type first */
1529
0
    found_type = HASH_GetHashTypeByOidTag(digestTag);
1530
0
    if ((digestTag == SEC_OID_UNKNOWN) || (found_type == HASH_AlgNULL)) {
1531
0
        PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
1532
0
        goto done;
1533
0
    }
1534
1535
0
    if (detached_digest != NULL) {
1536
0
        unsigned int hashLen = HASH_ResultLen(found_type);
1537
1538
0
        if (digest_type != found_type ||
1539
0
            detached_digest->len != hashLen) {
1540
0
            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
1541
0
            goto done;
1542
0
        }
1543
0
        digest = detached_digest;
1544
0
    } else {
1545
0
        PORT_Assert(digestalgs != NULL && digestalgs[0] != NULL);
1546
0
        if (digestalgs == NULL || digestalgs[0] == NULL) {
1547
0
            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
1548
0
            goto done;
1549
0
        }
1550
1551
        /*
1552
         * pick digest matching signerinfo->digestAlg from digests
1553
         */
1554
0
        for (i = 0; digestalgs[i] != NULL; i++) {
1555
0
            if (SECOID_FindOIDTag(&(digestalgs[i]->algorithm)) == digestTag)
1556
0
                break;
1557
0
        }
1558
0
        if (digestalgs[i] == NULL) {
1559
0
            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
1560
0
            goto done;
1561
0
        }
1562
1563
0
        digest = digests[i];
1564
0
    }
1565
1566
0
    encTag = SECOID_FindOIDTag(&(signerinfo->digestEncAlg.algorithm));
1567
0
    if (encTag == SEC_OID_UNKNOWN) {
1568
0
        PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
1569
0
        goto done;
1570
0
    }
1571
1572
0
    if (signerinfo->authAttr != NULL) {
1573
0
        SEC_PKCS7Attribute *attr;
1574
0
        SECItem *value;
1575
0
        SECItem encoded_attrs;
1576
1577
        /*
1578
         * We have a sigkey only for signedAndEnvelopedData, which is
1579
         * not supposed to have any authenticated attributes.
1580
         */
1581
0
        if (sigkey != NULL) {
1582
0
            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
1583
0
            goto done;
1584
0
        }
1585
1586
        /*
1587
         * PKCS #7 says that if there are any authenticated attributes,
1588
         * then there must be one for content type which matches the
1589
         * content type of the content being signed, and there must
1590
         * be one for message digest which matches our message digest.
1591
         * So check these things first.
1592
         * XXX Might be nice to have a compare-attribute-value function
1593
         * which could collapse the following nicely.
1594
         */
1595
0
        attr = sec_PKCS7FindAttribute(signerinfo->authAttr,
1596
0
                                      SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE);
1597
0
        value = sec_PKCS7AttributeValue(attr);
1598
0
        if (value == NULL || value->len != content_type->len) {
1599
0
            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
1600
0
            goto done;
1601
0
        }
1602
0
        if (PORT_Memcmp(value->data, content_type->data, value->len) != 0) {
1603
0
            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
1604
0
            goto done;
1605
0
        }
1606
1607
0
        attr = sec_PKCS7FindAttribute(signerinfo->authAttr,
1608
0
                                      SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE);
1609
0
        value = sec_PKCS7AttributeValue(attr);
1610
0
        if (value == NULL || value->len != digest->len) {
1611
0
            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
1612
0
            goto done;
1613
0
        }
1614
0
        if (PORT_Memcmp(value->data, digest->data, value->len) != 0) {
1615
0
            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
1616
0
            goto done;
1617
0
        }
1618
1619
        /*
1620
         * Okay, we met the constraints of the basic attributes.
1621
         * Now check the signature, which is based on a digest of
1622
         * the DER-encoded authenticated attributes.  So, first we
1623
         * encode and then we digest/verify.
1624
         */
1625
0
        encoded_attrs.data = NULL;
1626
0
        encoded_attrs.len = 0;
1627
0
        if (sec_PKCS7EncodeAttributes(NULL, &encoded_attrs,
1628
0
                                      &(signerinfo->authAttr)) == NULL)
1629
0
            goto done;
1630
1631
0
        if (encoded_attrs.data == NULL || encoded_attrs.len == 0) {
1632
0
            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
1633
0
            goto done;
1634
0
        }
1635
1636
0
        goodsig = (PRBool)(VFY_VerifyDataDirect(encoded_attrs.data,
1637
0
                                                encoded_attrs.len,
1638
0
                                                publickey, &(signerinfo->encDigest),
1639
0
                                                encTag, digestTag, NULL,
1640
0
                                                cinfo->pwfn_arg) == SECSuccess);
1641
0
        PORT_Free(encoded_attrs.data);
1642
0
    } else {
1643
0
        SECItem *sig;
1644
0
        SECItem holder;
1645
1646
        /*
1647
         * No authenticated attributes.
1648
         * The signature is based on the plain message digest.
1649
         */
1650
1651
0
        sig = &(signerinfo->encDigest);
1652
0
        if (sig->len == 0) { /* bad signature */
1653
0
            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
1654
0
            goto done;
1655
0
        }
1656
1657
0
        if (sigkey != NULL) {
1658
0
            sec_PKCS7CipherObject *decryptobj;
1659
0
            unsigned int buflen;
1660
1661
            /*
1662
             * For signedAndEnvelopedData, we first must decrypt the encrypted
1663
             * digest with the bulk encryption key.  The result is the normal
1664
             * encrypted digest (aka the signature).
1665
             */
1666
0
            decryptobj = sec_PKCS7CreateDecryptObject(sigkey, bulkid);
1667
0
            if (decryptobj == NULL)
1668
0
                goto done;
1669
1670
0
            buflen = sec_PKCS7DecryptLength(decryptobj, sig->len, PR_TRUE);
1671
0
            PORT_Assert(buflen);
1672
0
            if (buflen == 0) { /* something is wrong */
1673
0
                sec_PKCS7DestroyDecryptObject(decryptobj);
1674
0
                goto done;
1675
0
            }
1676
1677
0
            holder.data = (unsigned char *)PORT_Alloc(buflen);
1678
0
            if (holder.data == NULL) {
1679
0
                sec_PKCS7DestroyDecryptObject(decryptobj);
1680
0
                goto done;
1681
0
            }
1682
1683
0
            rv = sec_PKCS7Decrypt(decryptobj, holder.data, &holder.len, buflen,
1684
0
                                  sig->data, sig->len, PR_TRUE);
1685
0
            sec_PKCS7DestroyDecryptObject(decryptobj);
1686
0
            if (rv != SECSuccess) {
1687
0
                goto done;
1688
0
            }
1689
1690
0
            sig = &holder;
1691
0
        }
1692
1693
0
        goodsig = (PRBool)(VFY_VerifyDigestDirect(digest, publickey, sig,
1694
0
                                                  encTag, digestTag, cinfo->pwfn_arg) == SECSuccess);
1695
1696
0
        if (sigkey != NULL) {
1697
0
            PORT_Assert(sig == &holder);
1698
0
            PORT_ZFree(holder.data, holder.len);
1699
0
        }
1700
0
    }
1701
1702
0
    if (!goodsig) {
1703
        /*
1704
         * XXX Change the generic error into our specific one, because
1705
         * in that case we get a better explanation out of the Security
1706
         * Advisor.  This is really a bug in our error strings (the
1707
         * "generic" error has a lousy/wrong message associated with it
1708
         * which assumes the signature verification was done for the
1709
         * purposes of checking the issuer signature on a certificate)
1710
         * but this is at least an easy workaround and/or in the
1711
         * Security Advisor, which specifically checks for the error
1712
         * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
1713
         * in that case but does not similarly check for
1714
         * SEC_ERROR_BAD_SIGNATURE.  It probably should, but then would
1715
         * probably say the wrong thing in the case that it *was* the
1716
         * certificate signature check that failed during the cert
1717
         * verification done above.  Our error handling is really a mess.
1718
         */
1719
0
        if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
1720
0
            PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
1721
0
    }
1722
1723
0
savecert:
1724
    /*
1725
     * Only save the smime profile if we are checking an email message and
1726
     * the cert has an email address in it.
1727
     */
1728
0
    if (cert->emailAddr && cert->emailAddr[0] &&
1729
0
        ((certusage == certUsageEmailSigner) ||
1730
0
         (certusage == certUsageEmailRecipient))) {
1731
0
        SECItem *profile = NULL;
1732
0
        int save_error;
1733
1734
        /*
1735
         * Remember the current error set because we do not care about
1736
         * anything set by the functions we are about to call.
1737
         */
1738
0
        save_error = PORT_GetError();
1739
1740
0
        if (goodsig && (signerinfo->authAttr != NULL)) {
1741
            /*
1742
             * If the signature is good, then we can save the S/MIME profile,
1743
             * if we have one.
1744
             */
1745
0
            SEC_PKCS7Attribute *attr;
1746
1747
0
            attr = sec_PKCS7FindAttribute(signerinfo->authAttr,
1748
0
                                          SEC_OID_PKCS9_SMIME_CAPABILITIES,
1749
0
                                          PR_TRUE);
1750
0
            profile = sec_PKCS7AttributeValue(attr);
1751
0
        }
1752
1753
0
        rv = CERT_SaveSMimeProfile(cert, profile, encoded_stime);
1754
1755
        /*
1756
         * Restore the saved error in case the calls above set a new
1757
         * one that we do not actually care about.
1758
         */
1759
0
        PORT_SetError(save_error);
1760
1761
        /*
1762
         * XXX Failure is not indicated anywhere -- the signature
1763
         * verification itself is unaffected by whether or not the
1764
         * profile was successfully saved.
1765
         */
1766
0
    }
1767
1768
0
done:
1769
1770
    /*
1771
     * See comment above about why we do not want to destroy cert
1772
     * itself here.
1773
     */
1774
1775
0
    if (certs != NULL)
1776
0
        CERT_DestroyCertArray(certs, certcount);
1777
1778
0
    if (publickey != NULL)
1779
0
        SECKEY_DestroyPublicKey(publickey);
1780
1781
0
    return goodsig;
1782
0
}
1783
1784
/*
1785
 * SEC_PKCS7VerifySignature
1786
 *      Look at a PKCS7 contentInfo and check if the signature is good.
1787
 *      The verification checks that the signing cert is valid and trusted
1788
 *      for the purpose specified by "certusage".
1789
 *
1790
 *      In addition, if "keepcerts" is true, add any new certificates found
1791
 *      into our local database.
1792
 */
1793
PRBool
1794
SEC_PKCS7VerifySignature(SEC_PKCS7ContentInfo *cinfo,
1795
                         SECCertUsage certusage,
1796
                         PRBool keepcerts)
1797
0
{
1798
0
    return sec_pkcs7_verify_signature(cinfo, certusage,
1799
0
                                      NULL, HASH_AlgNULL, keepcerts, NULL);
1800
0
}
1801
1802
/*
1803
 * SEC_PKCS7VerifyDetachedSignature
1804
 *      Look at a PKCS7 contentInfo and check if the signature matches
1805
 *      a passed-in digest (calculated, supposedly, from detached contents).
1806
 *      The verification checks that the signing cert is valid and trusted
1807
 *      for the purpose specified by "certusage".
1808
 *
1809
 *      In addition, if "keepcerts" is true, add any new certificates found
1810
 *      into our local database.
1811
 */
1812
PRBool
1813
SEC_PKCS7VerifyDetachedSignature(SEC_PKCS7ContentInfo *cinfo,
1814
                                 SECCertUsage certusage,
1815
                                 const SECItem *detached_digest,
1816
                                 HASH_HashType digest_type,
1817
                                 PRBool keepcerts)
1818
0
{
1819
0
    return sec_pkcs7_verify_signature(cinfo, certusage,
1820
0
                                      detached_digest, digest_type,
1821
0
                                      keepcerts, NULL);
1822
0
}
1823
1824
/*
1825
 * SEC_PKCS7VerifyDetachedSignatureAtTime
1826
 *      Look at a PKCS7 contentInfo and check if the signature matches
1827
 *      a passed-in digest (calculated, supposedly, from detached contents).
1828
 *      The verification checks that the signing cert is valid and trusted
1829
 *      for the purpose specified by "certusage" at time "atTime".
1830
 *
1831
 *      In addition, if "keepcerts" is true, add any new certificates found
1832
 *      into our local database.
1833
 */
1834
PRBool
1835
SEC_PKCS7VerifyDetachedSignatureAtTime(SEC_PKCS7ContentInfo *cinfo,
1836
                                       SECCertUsage certusage,
1837
                                       const SECItem *detached_digest,
1838
                                       HASH_HashType digest_type,
1839
                                       PRBool keepcerts,
1840
                                       PRTime atTime)
1841
0
{
1842
0
    return sec_pkcs7_verify_signature(cinfo, certusage,
1843
0
                                      detached_digest, digest_type,
1844
0
                                      keepcerts, &atTime);
1845
0
}
1846
1847
/*
1848
 * Return the asked-for portion of the name of the signer of a PKCS7
1849
 * signed object.
1850
 *
1851
 * Returns a pointer to allocated memory, which must be freed.
1852
 * A NULL return value is an error.
1853
 */
1854
1855
0
#define sec_common_name 1
1856
0
#define sec_email_address 2
1857
1858
static char *
1859
sec_pkcs7_get_signer_cert_info(SEC_PKCS7ContentInfo *cinfo, int selector)
1860
0
{
1861
0
    SECOidTag kind;
1862
0
    SEC_PKCS7SignerInfo **signerinfos;
1863
0
    CERTCertificate *signercert;
1864
0
    char *container;
1865
1866
0
    kind = SEC_PKCS7ContentType(cinfo);
1867
0
    switch (kind) {
1868
0
        default:
1869
0
        case SEC_OID_PKCS7_DATA:
1870
0
        case SEC_OID_PKCS7_DIGESTED_DATA:
1871
0
        case SEC_OID_PKCS7_ENVELOPED_DATA:
1872
0
        case SEC_OID_PKCS7_ENCRYPTED_DATA:
1873
0
            PORT_Assert(0);
1874
0
            return NULL;
1875
0
        case SEC_OID_PKCS7_SIGNED_DATA: {
1876
0
            SEC_PKCS7SignedData *sdp;
1877
1878
0
            sdp = cinfo->content.signedData;
1879
0
            signerinfos = sdp->signerInfos;
1880
0
        } break;
1881
0
        case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: {
1882
0
            SEC_PKCS7SignedAndEnvelopedData *saedp;
1883
1884
0
            saedp = cinfo->content.signedAndEnvelopedData;
1885
0
            signerinfos = saedp->signerInfos;
1886
0
        } break;
1887
0
    }
1888
1889
0
    if (signerinfos == NULL || signerinfos[0] == NULL)
1890
0
        return NULL;
1891
1892
0
    signercert = signerinfos[0]->cert;
1893
1894
    /*
1895
     * No cert there; see if we can find one by calling verify ourselves.
1896
     */
1897
0
    if (signercert == NULL) {
1898
        /*
1899
         * The cert usage does not matter in this case, because we do not
1900
         * actually care about the verification itself, but we have to pick
1901
         * some valid usage to pass in.
1902
         */
1903
0
        (void)sec_pkcs7_verify_signature(cinfo, certUsageEmailSigner,
1904
0
                                         NULL, HASH_AlgNULL, PR_FALSE, NULL);
1905
0
        signercert = signerinfos[0]->cert;
1906
0
        if (signercert == NULL)
1907
0
            return NULL;
1908
0
    }
1909
1910
0
    switch (selector) {
1911
0
        case sec_common_name:
1912
0
            container = CERT_GetCommonName(&signercert->subject);
1913
0
            break;
1914
0
        case sec_email_address:
1915
0
            if (signercert->emailAddr && signercert->emailAddr[0]) {
1916
0
                container = PORT_Strdup(signercert->emailAddr);
1917
0
            } else {
1918
0
                container = NULL;
1919
0
            }
1920
0
            break;
1921
0
        default:
1922
0
            PORT_Assert(0);
1923
0
            container = NULL;
1924
0
            break;
1925
0
    }
1926
1927
0
    return container;
1928
0
}
1929
1930
char *
1931
SEC_PKCS7GetSignerCommonName(SEC_PKCS7ContentInfo *cinfo)
1932
0
{
1933
0
    return sec_pkcs7_get_signer_cert_info(cinfo, sec_common_name);
1934
0
}
1935
1936
char *
1937
SEC_PKCS7GetSignerEmailAddress(SEC_PKCS7ContentInfo *cinfo)
1938
0
{
1939
0
    return sec_pkcs7_get_signer_cert_info(cinfo, sec_email_address);
1940
0
}
1941
1942
/*
1943
 * Return the signing time, in UTCTime format, of a PKCS7 contentInfo.
1944
 */
1945
SECItem *
1946
SEC_PKCS7GetSigningTime(SEC_PKCS7ContentInfo *cinfo)
1947
0
{
1948
0
    SEC_PKCS7SignerInfo **signerinfos;
1949
0
    SEC_PKCS7Attribute *attr;
1950
1951
0
    if (SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
1952
0
        return NULL;
1953
1954
0
    signerinfos = cinfo->content.signedData->signerInfos;
1955
1956
    /*
1957
     * No signature, or more than one, means no deal.
1958
     */
1959
0
    if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL)
1960
0
        return NULL;
1961
1962
0
    attr = sec_PKCS7FindAttribute(signerinfos[0]->authAttr,
1963
0
                                  SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
1964
0
    return sec_PKCS7AttributeValue(attr);
1965
0
}