Coverage Report

Created: 2018-09-25 14:53

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