]> git.saurik.com Git - apple/security.git/blob - libsecurity_smime/lib/cmsdecode.c
Security-59306.120.7.tar.gz
[apple/security.git] / libsecurity_smime / lib / cmsdecode.c
1 /*
2 * The contents of this file are subject to the Mozilla Public
3 * License Version 1.1 (the "License"); you may not use this file
4 * except in compliance with the License. You may obtain a copy of
5 * the License at http://www.mozilla.org/MPL/
6 *
7 * Software distributed under the License is distributed on an "AS
8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9 * implied. See the License for the specific language governing
10 * rights and limitations under the License.
11 *
12 * The Original Code is the Netscape security libraries.
13 *
14 * The Initial Developer of the Original Code is Netscape
15 * Communications Corporation. Portions created by Netscape are
16 * Copyright (C) 1994-2000 Netscape Communications Corporation. All
17 * Rights Reserved.
18 *
19 * Contributor(s):
20 *
21 * Alternatively, the contents of this file may be used under the
22 * terms of the GNU General Public License Version 2 or later (the
23 * "GPL"), in which case the provisions of the GPL are applicable
24 * instead of those above. If you wish to allow use of your
25 * version of this file only under the terms of the GPL and not to
26 * allow others to use your version of this file under the MPL,
27 * indicate your decision by deleting the provisions above and
28 * replace them with the notice and other provisions required by
29 * the GPL. If you do not delete the provisions above, a recipient
30 * may use your version of this file under either the MPL or the
31 * GPL.
32 */
33
34 /*
35 * CMS decoding.
36 */
37
38 #include <Security/SecCmsDecoder.h>
39 #include <Security/SecCmsContentInfo.h>
40 #include <Security/SecCmsDigestContext.h>
41 #include <Security/SecCmsMessage.h>
42
43 #include "cmslocal.h"
44
45 #include "SecAsn1Item.h"
46 #include "secoid.h"
47
48 #include <security_asn1/secasn1.h>
49 #include <security_asn1/secerr.h>
50 #include <security_asn1/secport.h>
51
52 #include <limits.h>
53
54 struct SecCmsDecoderStr {
55 SEC_ASN1DecoderContext * dcx; /* ASN.1 decoder context */
56 SecCmsMessageRef cmsg; /* backpointer to the root message */
57 SECOidTag type; /* type of message */
58 SecCmsContent content; /* pointer to message */
59 SecCmsDecoderRef childp7dcx; /* inner CMS decoder context */
60 Boolean saw_contents;
61 int error;
62 SecCmsContentCallback cb;
63 void * cb_arg;
64 };
65
66 /* We use size_t for len in this function because the SEC_ASN1Decoder* layer
67 uses that for callback function */
68 static void nss_cms_decoder_update_filter (void *arg, const char *data, size_t len,
69 int depth, SEC_ASN1EncodingPart data_kind);
70 static OSStatus nss_cms_before_data(SecCmsDecoderRef p7dcx);
71 static OSStatus nss_cms_after_data(SecCmsDecoderRef p7dcx);
72 static OSStatus nss_cms_after_end(SecCmsDecoderRef p7dcx);
73 static void nss_cms_decoder_work_data(SecCmsDecoderRef p7dcx,
74 const unsigned char *data, size_t len, Boolean final);
75
76 extern const SecAsn1Template SecCmsMessageTemplate[];
77
78 /*
79 * nss_cms_decoder_notify -
80 * this is the driver of the decoding process. It gets called by the ASN.1
81 * decoder before and after an object is decoded.
82 * at various points in the decoding process, we intercept to set up and do
83 * further processing.
84 */
85 static void
86 nss_cms_decoder_notify(void *arg, Boolean before, void *dest, int depth)
87 {
88 SecCmsDecoderRef p7dcx;
89 SecCmsContentInfoRef rootcinfo, cinfo;
90 Boolean after = !before;
91
92 p7dcx = (SecCmsDecoderRef)arg;
93 rootcinfo = &(p7dcx->cmsg->contentInfo);
94
95 /* XXX error handling: need to set p7dcx->error */
96
97 #ifdef CMSDEBUG
98 fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth);
99 #endif
100
101 /* so what are we working on right now? */
102 switch (p7dcx->type) {
103 case SEC_OID_UNKNOWN:
104 /*
105 * right now, we are still decoding the OUTER (root) cinfo
106 * As soon as we know the inner content type, set up the info,
107 * but NO inner decoder or filter. The root decoder handles the first
108 * level children by itself - only for encapsulated contents (which
109 * are encoded as DER inside of an OCTET STRING) we need to set up a
110 * child decoder...
111 */
112 if (after && dest == &(rootcinfo->contentType)) {
113 p7dcx->type = SecCmsContentInfoGetContentTypeTag(rootcinfo);
114 p7dcx->content = rootcinfo->content; /* is this ready already ? need to alloc? */
115 /* XXX yes we need to alloc -- continue here */
116 }
117 break;
118 case SEC_OID_PKCS7_DATA:
119 /* this can only happen if the outermost cinfo has DATA in it */
120 /* otherwise, we handle this type implicitely in the inner decoders */
121
122 if (before && dest == &(rootcinfo->content)) {
123 /* fake it to cause the filter to put the data in the right place... */
124 /* we want the ASN.1 decoder to deliver the decoded bytes to us from now on */
125 SEC_ASN1DecoderSetFilterProc(p7dcx->dcx,
126 nss_cms_decoder_update_filter,
127 p7dcx,
128 (Boolean)(p7dcx->cb != NULL));
129 break;
130 }
131
132 if (after && dest == &(rootcinfo->content.data)) {
133 /* remove the filter */
134 SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
135 }
136 break;
137
138 case SEC_OID_PKCS7_SIGNED_DATA:
139 case SEC_OID_PKCS7_ENVELOPED_DATA:
140 case SEC_OID_PKCS7_DIGESTED_DATA:
141 case SEC_OID_PKCS7_ENCRYPTED_DATA:
142
143 if (before && dest == &(rootcinfo->content))
144 break; /* we're not there yet */
145
146 if (p7dcx->content.pointer == NULL)
147 p7dcx->content = rootcinfo->content;
148
149 /* get this data type's inner contentInfo */
150 cinfo = SecCmsContentGetContentInfo(p7dcx->content.pointer, p7dcx->type);
151
152 if (before && dest == &(cinfo->contentType)) {
153 /* at this point, set up the &%$&$ back pointer */
154 /* we cannot do it later, because the content itself is optional! */
155 /* please give me C++ */
156 switch (p7dcx->type) {
157 case SEC_OID_PKCS7_SIGNED_DATA:
158 p7dcx->content.signedData->contentInfo.cmsg = p7dcx->cmsg;
159 break;
160 case SEC_OID_PKCS7_DIGESTED_DATA:
161 p7dcx->content.digestedData->contentInfo.cmsg = p7dcx->cmsg;
162 break;
163 case SEC_OID_PKCS7_ENVELOPED_DATA:
164 p7dcx->content.envelopedData->contentInfo.cmsg = p7dcx->cmsg;
165 break;
166 case SEC_OID_PKCS7_ENCRYPTED_DATA:
167 p7dcx->content.encryptedData->contentInfo.cmsg = p7dcx->cmsg;
168 break;
169 default:
170 PORT_Assert(0);
171 break;
172 }
173 }
174
175 if (before && dest == &(cinfo->rawContent)) {
176 /* we want the ASN.1 decoder to deliver the decoded bytes to us from now on */
177 SEC_ASN1DecoderSetFilterProc(p7dcx->dcx, nss_cms_decoder_update_filter,
178 p7dcx, (Boolean)(p7dcx->cb != NULL));
179
180
181 /* we're right in front of the data */
182 if (nss_cms_before_data(p7dcx) != SECSuccess) {
183 SEC_ASN1DecoderClearFilterProc(p7dcx->dcx); /* stop all processing */
184 p7dcx->error = PORT_GetError();
185 PORT_SetError(0);
186 }
187 }
188 if (after && dest == &(cinfo->rawContent)) {
189 /* we're right after of the data */
190 if (nss_cms_after_data(p7dcx) != SECSuccess) {
191 p7dcx->error = PORT_GetError();
192 PORT_SetError(0);
193 }
194
195 /* we don't need to see the contents anymore */
196 SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
197 }
198 break;
199
200 #if 0 /* NIH */
201 case SEC_OID_PKCS7_AUTHENTICATED_DATA:
202 #endif
203 default:
204 /* unsupported or unknown message type - fail (more or less) gracefully */
205 p7dcx->error = SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE;
206 break;
207 }
208 }
209
210 /*
211 * nss_cms_before_data - set up the current encoder to receive data
212 */
213 static OSStatus
214 nss_cms_before_data(SecCmsDecoderRef p7dcx)
215 {
216 OSStatus rv;
217 SECOidTag childtype;
218 PLArenaPool *poolp;
219 SecCmsDecoderRef childp7dcx;
220 SecCmsContentInfoRef cinfo;
221 const SecAsn1Template *template;
222 void *mark = NULL;
223 size_t size;
224
225 poolp = p7dcx->cmsg->poolp;
226
227 /* call _Decode_BeforeData handlers */
228 switch (p7dcx->type) {
229 case SEC_OID_PKCS7_SIGNED_DATA:
230 /* we're decoding a signedData, so set up the digests */
231 rv = SecCmsSignedDataDecodeBeforeData(p7dcx->content.signedData);
232 if (rv != SECSuccess)
233 return SECFailure;
234 break;
235 case SEC_OID_PKCS7_DIGESTED_DATA:
236 /* we're encoding a digestedData, so set up the digest */
237 rv = SecCmsDigestedDataDecodeBeforeData(p7dcx->content.digestedData);
238 if (rv != SECSuccess)
239 return SECFailure;
240 break;
241 case SEC_OID_PKCS7_ENVELOPED_DATA:
242 rv = SecCmsEnvelopedDataDecodeBeforeData(p7dcx->content.envelopedData);
243 if (rv != SECSuccess)
244 return SECFailure;
245 break;
246 case SEC_OID_PKCS7_ENCRYPTED_DATA:
247 rv = SecCmsEncryptedDataDecodeBeforeData(p7dcx->content.encryptedData);
248 if (rv != SECSuccess)
249 return SECFailure;
250 break;
251 default:
252 return SECFailure;
253 }
254
255 /* ok, now we have a pointer to cinfo */
256 /* find out what kind of data is encapsulated */
257
258 cinfo = SecCmsContentGetContentInfo(p7dcx->content.pointer, p7dcx->type);
259 childtype = SecCmsContentInfoGetContentTypeTag(cinfo);
260
261 if (childtype == SEC_OID_PKCS7_DATA) {
262 cinfo->content.data = SECITEM_AllocItem(poolp, NULL, 0);
263 if (cinfo->content.data == NULL)
264 /* set memory error */
265 return SECFailure;
266
267 p7dcx->childp7dcx = NULL;
268 return SECSuccess;
269 }
270
271 /* set up inner decoder */
272
273 if ((template = SecCmsUtilGetTemplateByTypeTag(childtype)) == NULL)
274 return SECFailure;
275
276 childp7dcx = (SecCmsDecoderRef)PORT_ZAlloc(sizeof(struct SecCmsDecoderStr));
277 if (childp7dcx == NULL)
278 return SECFailure;
279
280 mark = PORT_ArenaMark(poolp);
281
282 /* allocate space for the stuff we're creating */
283 size = SecCmsUtilGetSizeByTypeTag(childtype);
284 childp7dcx->content.pointer = (void *)PORT_ArenaZAlloc(poolp, size);
285 if (childp7dcx->content.pointer == NULL)
286 goto loser;
287
288 /* start the child decoder */
289 childp7dcx->dcx = SEC_ASN1DecoderStart(poolp, childp7dcx->content.pointer, template, NULL, 0);
290 if (childp7dcx->dcx == NULL)
291 goto loser;
292
293 /* the new decoder needs to notify, too */
294 SEC_ASN1DecoderSetNotifyProc(childp7dcx->dcx, nss_cms_decoder_notify, childp7dcx);
295
296 /* tell the parent decoder that it needs to feed us the content data */
297 p7dcx->childp7dcx = childp7dcx;
298
299 childp7dcx->type = childtype; /* our type */
300
301 childp7dcx->cmsg = p7dcx->cmsg; /* backpointer to root message */
302
303 /* should the child decoder encounter real data, it needs to give it to the caller */
304 childp7dcx->cb = p7dcx->cb;
305 childp7dcx->cb_arg = p7dcx->cb_arg;
306
307 /* now set up the parent to hand decoded data to the next level decoder */
308 p7dcx->cb = (SecCmsContentCallback)SecCmsDecoderUpdate;
309 p7dcx->cb_arg = childp7dcx;
310
311 PORT_ArenaUnmark(poolp, mark);
312
313 return SECSuccess;
314
315 loser:
316 if (mark)
317 PORT_ArenaRelease(poolp, mark);
318 if (childp7dcx)
319 PORT_Free(childp7dcx);
320 p7dcx->childp7dcx = NULL;
321 return SECFailure;
322 }
323
324 static OSStatus
325 nss_cms_after_data(SecCmsDecoderRef p7dcx)
326 {
327 SecCmsDecoderRef childp7dcx;
328 OSStatus rv = SECFailure;
329
330 /* Handle last block. This is necessary to flush out the last bytes
331 * of a possibly incomplete block */
332 nss_cms_decoder_work_data(p7dcx, NULL, 0, PR_TRUE);
333
334 /* finish any "inner" decoders - there's no more data coming... */
335 if (p7dcx->childp7dcx != NULL) {
336 childp7dcx = p7dcx->childp7dcx;
337 if (childp7dcx->dcx != NULL) {
338 if (SEC_ASN1DecoderFinish(childp7dcx->dcx) != SECSuccess) {
339 /* do what? free content? */
340 rv = SECFailure;
341 } else {
342 rv = nss_cms_after_end(childp7dcx);
343 }
344 if (rv != SECSuccess)
345 goto done;
346 }
347 PORT_Free(p7dcx->childp7dcx);
348 p7dcx->childp7dcx = NULL;
349 }
350
351 switch (p7dcx->type) {
352 case SEC_OID_PKCS7_SIGNED_DATA:
353 /* this will finish the digests and verify */
354 rv = SecCmsSignedDataDecodeAfterData(p7dcx->content.signedData);
355 break;
356 case SEC_OID_PKCS7_ENVELOPED_DATA:
357 rv = SecCmsEnvelopedDataDecodeAfterData(p7dcx->content.envelopedData);
358 break;
359 case SEC_OID_PKCS7_DIGESTED_DATA:
360 rv = SecCmsDigestedDataDecodeAfterData(p7dcx->content.digestedData);
361 break;
362 case SEC_OID_PKCS7_ENCRYPTED_DATA:
363 rv = SecCmsEncryptedDataDecodeAfterData(p7dcx->content.encryptedData);
364 break;
365 case SEC_OID_PKCS7_DATA:
366 /* do nothing */
367 break;
368 default:
369 rv = SECFailure;
370 break;
371 }
372 done:
373 return rv;
374 }
375
376 static OSStatus
377 nss_cms_after_end(SecCmsDecoderRef p7dcx)
378 {
379 OSStatus rv;
380
381 switch (p7dcx->type) {
382 case SEC_OID_PKCS7_SIGNED_DATA:
383 rv = SecCmsSignedDataDecodeAfterEnd(p7dcx->content.signedData);
384 break;
385 case SEC_OID_PKCS7_ENVELOPED_DATA:
386 rv = SecCmsEnvelopedDataDecodeAfterEnd(p7dcx->content.envelopedData);
387 break;
388 case SEC_OID_PKCS7_DIGESTED_DATA:
389 rv = SecCmsDigestedDataDecodeAfterEnd(p7dcx->content.digestedData);
390 break;
391 case SEC_OID_PKCS7_ENCRYPTED_DATA:
392 rv = SecCmsEncryptedDataDecodeAfterEnd(p7dcx->content.encryptedData);
393 break;
394 case SEC_OID_PKCS7_DATA:
395 rv = SECSuccess;
396 break;
397 default:
398 rv = SECFailure; /* we should not have got that far... */
399 break;
400 }
401 return rv;
402 }
403
404 /*
405 * nss_cms_decoder_work_data - handle decoded data bytes.
406 *
407 * This function either decrypts the data if needed, and/or calculates digests
408 * on it, then either stores it or passes it on to the next level decoder.
409 */
410 static void
411 nss_cms_decoder_work_data(SecCmsDecoderRef p7dcx,
412 const unsigned char *data, size_t len,
413 Boolean final)
414 {
415 SecCmsContentInfoRef cinfo;
416 unsigned char *buf = NULL;
417 unsigned char *dest;
418 size_t offset;
419 OSStatus rv;
420 SecAsn1Item * storage;
421
422 /*
423 * We should really have data to process, or we should be trying
424 * to finish/flush the last block. (This is an overly paranoid
425 * check since all callers are in this file and simple inspection
426 * proves they do it right. But it could find a bug in future
427 * modifications/development, that is why it is here.)
428 */
429 if (((data == NULL || len == 0) && !final) ||
430 len > UINT_MAX) {
431 goto loser;
432 }
433
434 if (!p7dcx->content.pointer) // might be ExContent??
435 return;
436
437 cinfo = SecCmsContentGetContentInfo(p7dcx->content.pointer, p7dcx->type);
438
439 if (cinfo->ciphcx != NULL) {
440 /*
441 * we are decrypting.
442 *
443 * XXX If we get an error, we do not want to do the digest or callback,
444 * but we want to keep decoding. Or maybe we want to stop decoding
445 * altogether if there is a callback, because obviously we are not
446 * sending the data back and they want to know that.
447 */
448
449 unsigned int outlen = 0; /* length of decrypted data */
450 unsigned int buflen; /* length available for decrypted data */
451
452 /* find out about the length of decrypted data */
453 /* 64 bits cast: Worst case here is we may not decrypt the full CMS blob, if the blob is bigger than 4GB */
454 buflen = SecCmsCipherContextDecryptLength(cinfo->ciphcx, (unsigned int)len, final);
455
456 /*
457 * it might happen that we did not provide enough data for a full
458 * block (decryption unit), and that there is no output available
459 */
460
461 /* no output available, AND no input? */
462 if (buflen == 0 && len == 0)
463 goto loser; /* bail out */
464
465 /*
466 * have inner decoder: pass the data on (means inner content type is NOT data)
467 * no inner decoder: we have DATA in here: either call callback or store
468 */
469 if (buflen != 0) {
470 /* there will be some output - need to make room for it */
471 /* allocate buffer from the heap */
472 buf = (unsigned char *)PORT_Alloc(buflen);
473 if (buf == NULL) {
474 p7dcx->error = SEC_ERROR_NO_MEMORY;
475 goto loser;
476 }
477 }
478
479 /*
480 * decrypt incoming data
481 * buf can still be NULL here (and buflen == 0) here if we don't expect
482 * any output (see above), but we still need to call SecCmsCipherContextDecrypt to
483 * keep track of incoming data
484 */
485 rv = SecCmsCipherContextDecrypt(cinfo->ciphcx, buf, &outlen, buflen,
486 data, (unsigned int)len, final);
487 if (rv != SECSuccess) {
488 p7dcx->error = PORT_GetError();
489 goto loser;
490 }
491
492 //PORT_Assert (final || outlen == buflen);
493
494 /* swap decrypted data in */
495 data = buf;
496 len = outlen;
497 }
498
499 if (len == 0)
500 goto done; /* nothing more to do */
501
502 /*
503 * Update the running digests with plaintext bytes (if we need to).
504 */
505 if (cinfo->digcx)
506 SecCmsDigestContextUpdate(cinfo->digcx, data, len);
507
508 /* at this point, we have the plain decoded & decrypted data */
509 /* which is either more encoded DER which we need to hand to the child decoder */
510 /* or data we need to hand back to our caller */
511
512 /* pass the content back to our caller or */
513 /* feed our freshly decrypted and decoded data into child decoder */
514 if (p7dcx->cb != NULL) {
515 (*p7dcx->cb)(p7dcx->cb_arg, (const char *)data, len);
516 } else if (SecCmsContentInfoGetContentTypeTag(cinfo) == SEC_OID_PKCS7_DATA) {
517 /* store it in "inner" data item as well */
518 /* find the DATA item in the encapsulated cinfo and store it there */
519 storage = cinfo->content.data;
520
521 offset = storage->Length;
522
523 /* check for potential overflow */
524 if (len >= (size_t)(INT_MAX - storage->Length)) {
525 p7dcx->error = SEC_ERROR_NO_MEMORY;
526 goto loser;
527 }
528
529 if (storage->Length == 0) {
530 dest = (unsigned char *)PORT_ArenaAlloc(p7dcx->cmsg->poolp, len);
531 } else {
532 dest = (unsigned char *)PORT_ArenaGrow(p7dcx->cmsg->poolp,
533 storage->Data,
534 storage->Length,
535 storage->Length + len);
536 }
537 if (dest == NULL) {
538 p7dcx->error = SEC_ERROR_NO_MEMORY;
539 goto loser;
540 }
541
542 storage->Data = dest;
543 storage->Length += len;
544
545 /* copy it in */
546 if (data != NULL) {
547 PORT_Memcpy(storage->Data + offset, data, len);
548 }
549 }
550
551 done:
552 loser:
553 if (buf)
554 PORT_Free (buf);
555 }
556
557 /*
558 * nss_cms_decoder_update_filter - process ASN.1 data
559 *
560 * once we have set up a filter in nss_cms_decoder_notify(),
561 * all data processed by the ASN.1 decoder is also passed through here.
562 * we pass the content bytes (as opposed to length and tag bytes) on to
563 * nss_cms_decoder_work_data().
564 *
565 * len has to be of type size_t because it is a callback to the
566 * SEC_ASN1Decoder layer
567 */
568 static void
569 nss_cms_decoder_update_filter (void *arg, const char *data, size_t len,
570 int depth, SEC_ASN1EncodingPart data_kind)
571 {
572 SecCmsDecoderRef p7dcx;
573
574 PORT_Assert (len); /* paranoia */
575 if (len == 0)
576 return;
577
578 p7dcx = (SecCmsDecoderRef)arg;
579
580 p7dcx->saw_contents = PR_TRUE;
581
582 /* pass on the content bytes only */
583 if (data_kind == SEC_ASN1_Contents)
584 nss_cms_decoder_work_data(p7dcx, (const unsigned char *) data, len, PR_FALSE);
585 }
586
587 /*
588 * SecCmsDecoderCreate - set up decoding of a BER-encoded CMS message
589 */
590 OSStatus
591 SecCmsDecoderCreate(SecCmsContentCallback cb, void *cb_arg,
592 PK11PasswordFunc pwfn, void *pwfn_arg,
593 SecCmsGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
594 SecCmsDecoderRef *outDecoder)
595 {
596 SecCmsDecoderRef p7dcx;
597 SecCmsMessageRef cmsg;
598 OSStatus result;
599
600 /* Clear the thread error to clean up dirty threads */
601 PORT_SetError(0);
602
603 cmsg = SecCmsMessageCreate();
604 if (cmsg == NULL)
605 goto loser;
606
607 SecCmsMessageSetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg);
608
609 p7dcx = (SecCmsDecoderRef)PORT_ZAlloc(sizeof(struct SecCmsDecoderStr));
610 if (p7dcx == NULL) {
611 SecCmsMessageDestroy(cmsg);
612 goto loser;
613 }
614
615 p7dcx->dcx = SEC_ASN1DecoderStart(cmsg->poolp, cmsg, SecCmsMessageTemplate, NULL, 0);
616 if (p7dcx->dcx == NULL) {
617 PORT_Free (p7dcx);
618 SecCmsMessageDestroy(cmsg);
619 goto loser;
620 }
621
622 SEC_ASN1DecoderSetNotifyProc (p7dcx->dcx, nss_cms_decoder_notify, p7dcx);
623
624 p7dcx->cmsg = cmsg;
625 p7dcx->type = SEC_OID_UNKNOWN;
626
627 p7dcx->cb = cb;
628 p7dcx->cb_arg = cb_arg;
629
630 *outDecoder = p7dcx;
631 return errSecSuccess;
632
633 loser:
634 result = PORT_GetError();
635 PORT_SetError(0); // Clean the thread error since we've returned the error
636 return result;
637 }
638
639 /*
640 * SecCmsDecoderUpdate - feed DER-encoded data to decoder
641 */
642 OSStatus
643 SecCmsDecoderUpdate(SecCmsDecoderRef p7dcx, const void *buf, CFIndex len)
644 {
645 if (!p7dcx) {
646 return errSecParam;
647 }
648
649 if (p7dcx->dcx != NULL && p7dcx->error == 0) { /* if error is set already, don't bother */
650 if (SEC_ASN1DecoderUpdate (p7dcx->dcx, buf, len) != SECSuccess) {
651 p7dcx->error = PORT_GetError();
652 PORT_Assert (p7dcx->error);
653 if (p7dcx->error == 0)
654 p7dcx->error = -1;
655 }
656 }
657
658 if (p7dcx->error == 0)
659 return 0;
660
661 /* there has been a problem, let's finish the decoder */
662 if (p7dcx->dcx != NULL) {
663 /* @@@ Change this to SEC_ASN1DecoderAbort()? */
664 (void) SEC_ASN1DecoderFinish (p7dcx->dcx);
665 p7dcx->dcx = NULL;
666 }
667
668 PORT_SetError (0); // Clean the thread error since we've returned the error
669
670 return p7dcx->error;
671 }
672
673 /*
674 * SecCmsDecoderDestroy - stop decoding in case of error
675 */
676 void
677 SecCmsDecoderDestroy(SecCmsDecoderRef p7dcx)
678 {
679 /* SecCmsMessageDestroy frees inner decoders and digests. */
680 SecCmsMessageDestroy(p7dcx->cmsg);
681 p7dcx->cmsg = NULL;
682 if (p7dcx->dcx)
683 (void)SEC_ASN1DecoderFinish(p7dcx->dcx);
684 /* Clear out references */
685 p7dcx->cmsg = NULL;
686 p7dcx->dcx = NULL;
687 p7dcx->childp7dcx = NULL;
688 PORT_Free(p7dcx);
689 }
690
691 /*
692 * SecCmsDecoderFinish - mark the end of inner content and finish decoding
693 */
694 OSStatus
695 SecCmsDecoderFinish(SecCmsDecoderRef p7dcx, SecCmsMessageRef *outMessage)
696 {
697 SecCmsMessageRef cmsg;
698 OSStatus result;
699
700 cmsg = p7dcx->cmsg;
701
702 if (p7dcx->dcx == NULL || SEC_ASN1DecoderFinish(p7dcx->dcx) != SECSuccess ||
703 nss_cms_after_end(p7dcx) != SECSuccess)
704 {
705 if (p7dcx->cmsg) {
706 SecCmsMessageDestroy(cmsg);
707 }
708 result = PORT_GetError();
709 goto loser;
710 }
711
712 *outMessage = cmsg;
713 result = errSecSuccess;
714
715 loser:
716 /* Clear out references */
717 p7dcx->cmsg = NULL;
718 p7dcx->dcx = NULL;
719 p7dcx->childp7dcx = NULL;
720 PORT_Free(p7dcx);
721 PORT_SetError(0); // Clean the thread error since we've returned the error
722 return result;
723 }
724
725 OSStatus
726 SecCmsMessageDecode(const SecAsn1Item *encodedMessage,
727 SecCmsContentCallback cb, void *cb_arg,
728 PK11PasswordFunc pwfn, void *pwfn_arg,
729 SecCmsGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
730 SecCmsMessageRef *outMessage)
731 {
732 OSStatus result;
733 SecCmsDecoderRef decoder = NULL;
734
735 result = SecCmsDecoderCreate(cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg, &decoder);
736 if (result)
737 goto loser;
738 result = SecCmsDecoderUpdate(decoder, encodedMessage->Data, encodedMessage->Length);
739 if (result) {
740 SecCmsDecoderDestroy(decoder);
741 goto loser;
742 }
743
744 result = SecCmsDecoderFinish(decoder, outMessage);
745 loser:
746 return result;
747 }