Bug Summary

File:s/lib/pkcs7/p7decode.c
Warning:line 1707, column 9
Value stored to 'rv' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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