/ libsecurity_smime / lib / cmsdecode.c
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  }