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