]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_smime/lib/cmsdecode.c
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / 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 "secitem.h"
46 #include "secoid.h"
47 #include <security_asn1/secasn1.h>
48 #include <security_asn1/secerr.h>
49
50 struct 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
62 static void nss_cms_decoder_update_filter (void *arg, const char *data, size_t len,
63 int depth, SEC_ASN1EncodingPart data_kind);
64 static OSStatus nss_cms_before_data(SecCmsDecoderRef p7dcx);
65 static OSStatus nss_cms_after_data(SecCmsDecoderRef p7dcx);
66 static OSStatus nss_cms_after_end(SecCmsDecoderRef p7dcx);
67 static void nss_cms_decoder_work_data(SecCmsDecoderRef p7dcx,
68 const unsigned char *data, size_t len, Boolean final);
69
70 extern 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 */
79 static void
80 nss_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 PORT_SetError(0); // Clean the thread error since we've returned the error
181 }
182 }
183 if (after && dest == &(cinfo->rawContent)) {
184 /* we're right after of the data */
185 if (nss_cms_after_data(p7dcx) != SECSuccess) {
186 p7dcx->error = PORT_GetError();
187 PORT_SetError(0); // Clean the thread error since we've returned the error
188 }
189
190 /* we don't need to see the contents anymore */
191 SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
192 }
193 break;
194
195 #if 0 /* NIH */
196 case SEC_OID_PKCS7_AUTHENTICATED_DATA:
197 #endif
198 default:
199 /* unsupported or unknown message type - fail (more or less) gracefully */
200 p7dcx->error = SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE;
201 break;
202 }
203 }
204
205 /*
206 * nss_cms_before_data - set up the current encoder to receive data
207 */
208 static OSStatus
209 nss_cms_before_data(SecCmsDecoderRef p7dcx)
210 {
211 OSStatus rv;
212 SECOidTag childtype;
213 PLArenaPool *poolp;
214 SecCmsDecoderRef childp7dcx;
215 SecCmsContentInfoRef cinfo;
216 const SecAsn1Template *template;
217 void *mark = NULL;
218 size_t size;
219
220 poolp = p7dcx->cmsg->poolp;
221
222 /* call _Decode_BeforeData handlers */
223 switch (p7dcx->type) {
224 case SEC_OID_PKCS7_SIGNED_DATA:
225 /* we're decoding a signedData, so set up the digests */
226 rv = SecCmsSignedDataDecodeBeforeData(p7dcx->content.signedData);
227 if (rv != SECSuccess)
228 return SECFailure;
229 break;
230 case SEC_OID_PKCS7_DIGESTED_DATA:
231 /* we're encoding a digestedData, so set up the digest */
232 rv = SecCmsDigestedDataDecodeBeforeData(p7dcx->content.digestedData);
233 if (rv != SECSuccess)
234 return SECFailure;
235 break;
236 case SEC_OID_PKCS7_ENVELOPED_DATA:
237 rv = SecCmsEnvelopedDataDecodeBeforeData(p7dcx->content.envelopedData);
238 if (rv != SECSuccess)
239 return SECFailure;
240 break;
241 case SEC_OID_PKCS7_ENCRYPTED_DATA:
242 rv = SecCmsEncryptedDataDecodeBeforeData(p7dcx->content.encryptedData);
243 if (rv != SECSuccess)
244 return SECFailure;
245 break;
246 default:
247 return SECFailure;
248 }
249
250 /* ok, now we have a pointer to cinfo */
251 /* find out what kind of data is encapsulated */
252
253 cinfo = SecCmsContentGetContentInfo(p7dcx->content.pointer, p7dcx->type);
254 childtype = SecCmsContentInfoGetContentTypeTag(cinfo);
255
256 /* special case for SignedData: "unknown" child type maps to SEC_OID_OTHER */
257 if((childtype == SEC_OID_UNKNOWN) && (p7dcx->type == SEC_OID_PKCS7_SIGNED_DATA)) {
258 childtype = SEC_OID_OTHER;
259 }
260
261 if ((childtype == SEC_OID_PKCS7_DATA) || (childtype == SEC_OID_OTHER)){
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 /* Apple: link the new content to parent ContentInfo */
289 cinfo->content.pointer = childp7dcx->content.pointer;
290
291 /* start the child decoder */
292 childp7dcx->dcx = SEC_ASN1DecoderStart(poolp, childp7dcx->content.pointer, template, NULL, 0);
293 if (childp7dcx->dcx == NULL)
294 goto loser;
295
296 /* the new decoder needs to notify, too */
297 SEC_ASN1DecoderSetNotifyProc(childp7dcx->dcx, nss_cms_decoder_notify, childp7dcx);
298
299 /* tell the parent decoder that it needs to feed us the content data */
300 p7dcx->childp7dcx = childp7dcx;
301
302 childp7dcx->type = childtype; /* our type */
303
304 childp7dcx->cmsg = p7dcx->cmsg; /* backpointer to root message */
305
306 /* should the child decoder encounter real data, it needs to give it to the caller */
307 childp7dcx->cb = p7dcx->cb;
308 childp7dcx->cb_arg = p7dcx->cb_arg;
309
310 /* now set up the parent to hand decoded data to the next level decoder */
311 p7dcx->cb = (SecCmsContentCallback)SecCmsDecoderUpdate;
312 p7dcx->cb_arg = childp7dcx;
313
314 PORT_ArenaUnmark(poolp, mark);
315
316 return SECSuccess;
317
318 loser:
319 if (mark)
320 PORT_ArenaRelease(poolp, mark);
321 if (childp7dcx)
322 PORT_Free(childp7dcx);
323 p7dcx->childp7dcx = NULL;
324 return SECFailure;
325 }
326
327 static OSStatus
328 nss_cms_after_data(SecCmsDecoderRef p7dcx)
329 {
330 PLArenaPool *poolp;
331 SecCmsDecoderRef childp7dcx;
332 OSStatus rv = SECFailure;
333
334 poolp = p7dcx->cmsg->poolp;
335
336 /* Handle last block. This is necessary to flush out the last bytes
337 * of a possibly incomplete block */
338 nss_cms_decoder_work_data(p7dcx, NULL, 0, PR_TRUE);
339
340 /* finish any "inner" decoders - there's no more data coming... */
341 if (p7dcx->childp7dcx != NULL) {
342 childp7dcx = p7dcx->childp7dcx;
343 if (childp7dcx->dcx != NULL) {
344 if (SEC_ASN1DecoderFinish(childp7dcx->dcx) != SECSuccess) {
345 /* do what? free content? */
346 rv = SECFailure;
347 } else {
348 rv = nss_cms_after_end(childp7dcx);
349 }
350 if (rv != SECSuccess)
351 goto done;
352 }
353 PORT_Free(p7dcx->childp7dcx);
354 p7dcx->childp7dcx = NULL;
355 }
356
357 switch (p7dcx->type) {
358 case SEC_OID_PKCS7_SIGNED_DATA:
359 /* this will finish the digests and verify */
360 rv = SecCmsSignedDataDecodeAfterData(p7dcx->content.signedData);
361 break;
362 case SEC_OID_PKCS7_ENVELOPED_DATA:
363 rv = SecCmsEnvelopedDataDecodeAfterData(p7dcx->content.envelopedData);
364 break;
365 case SEC_OID_PKCS7_DIGESTED_DATA:
366 rv = SecCmsDigestedDataDecodeAfterData(p7dcx->content.digestedData);
367 break;
368 case SEC_OID_PKCS7_ENCRYPTED_DATA:
369 rv = SecCmsEncryptedDataDecodeAfterData(p7dcx->content.encryptedData);
370 break;
371 case SEC_OID_PKCS7_DATA:
372 /* do nothing */
373 break;
374 default:
375 rv = SECFailure;
376 break;
377 }
378 done:
379 return rv;
380 }
381
382 static OSStatus
383 nss_cms_after_end(SecCmsDecoderRef p7dcx)
384 {
385 OSStatus rv;
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 */
416 static void
417 nss_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 PORT_SetError(0); // Clean the thread error since we've returned the error
492 goto loser;
493 }
494
495 PORT_Assert (final || outlen == buflen);
496
497 /* swap decrypted data in */
498 data = buf;
499 len = outlen;
500 }
501
502 if (len == 0)
503 goto done; /* nothing more to do */
504
505 /*
506 * Update the running digests with plaintext bytes (if we need to).
507 */
508 if (cinfo->digcx)
509 SecCmsDigestContextUpdate(cinfo->digcx, data, len);
510
511 /* at this point, we have the plain decoded & decrypted data */
512 /* which is either more encoded DER which we need to hand to the child decoder */
513 /* or data we need to hand back to our caller */
514
515 /* pass the content back to our caller or */
516 /* feed our freshly decrypted and decoded data into child decoder */
517 if (p7dcx->cb != NULL) {
518 (*p7dcx->cb)(p7dcx->cb_arg, (const char *)data, len);
519 }
520 #if 1
521 else
522 #endif
523 switch(SecCmsContentInfoGetContentTypeTag(cinfo)) {
524 default:
525 break;
526 case SEC_OID_PKCS7_DATA:
527 case SEC_OID_OTHER:
528 /* store it in "inner" data item as well */
529 /* find the DATA item in the encapsulated cinfo and store it there */
530 storage = cinfo->content.data;
531
532 offset = storage->Length;
533
534 /* check for potential overflow */
535 if (len >= (size_t)(INT_MAX - storage->Length)) {
536 p7dcx->error = SEC_ERROR_NO_MEMORY;
537 goto loser;
538 }
539
540 if (storage->Length == 0) {
541 dest = (unsigned char *)PORT_ArenaAlloc(p7dcx->cmsg->poolp, len);
542 } else {
543 dest = (unsigned char *)PORT_ArenaGrow(p7dcx->cmsg->poolp,
544 storage->Data,
545 storage->Length,
546 storage->Length + len);
547 }
548 if (dest == NULL) {
549 p7dcx->error = SEC_ERROR_NO_MEMORY;
550 goto loser;
551 }
552
553 storage->Data = dest;
554 storage->Length += len;
555
556 /* copy it in */
557 if (data != NULL) {
558 PORT_Memcpy(storage->Data + offset, data, len);
559 }
560 }
561
562 done:
563 loser:
564 if (buf)
565 PORT_Free (buf);
566 }
567
568 /*
569 * nss_cms_decoder_update_filter - process ASN.1 data
570 *
571 * once we have set up a filter in nss_cms_decoder_notify(),
572 * all data processed by the ASN.1 decoder is also passed through here.
573 * we pass the content bytes (as opposed to length and tag bytes) on to
574 * nss_cms_decoder_work_data().
575 */
576 static void
577 nss_cms_decoder_update_filter (void *arg, const char *data, size_t len,
578 int depth, SEC_ASN1EncodingPart data_kind)
579 {
580 SecCmsDecoderRef p7dcx;
581
582 PORT_Assert (len); /* paranoia */
583 if (len == 0)
584 return;
585
586 p7dcx = (SecCmsDecoderRef)arg;
587
588 p7dcx->saw_contents = PR_TRUE;
589
590 /* pass on the content bytes only */
591 if (data_kind == SEC_ASN1_Contents)
592 nss_cms_decoder_work_data(p7dcx, (const unsigned char *) data, len, PR_FALSE);
593 }
594
595 /*
596 * SecCmsDecoderCreate - set up decoding of a BER-encoded CMS message
597 */
598 OSStatus
599 SecCmsDecoderCreate(SecArenaPoolRef pool,
600 SecCmsContentCallback cb, void *cb_arg,
601 PK11PasswordFunc pwfn, void *pwfn_arg,
602 SecCmsGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
603 SecCmsDecoderRef *outDecoder)
604 {
605 SecCmsDecoderRef p7dcx;
606 SecCmsMessageRef cmsg;
607 OSStatus result;
608
609 /* Clear the thread error to clean up dirty threads */
610 PORT_SetError(0);
611
612 cmsg = SecCmsMessageCreate(pool);
613 if (cmsg == NULL)
614 goto loser;
615
616 SecCmsMessageSetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg,
617 NULL, NULL);
618
619 p7dcx = (SecCmsDecoderRef)PORT_ZAlloc(sizeof(struct SecCmsDecoderStr));
620 if (p7dcx == NULL) {
621 SecCmsMessageDestroy(cmsg);
622 goto loser;
623 }
624
625 p7dcx->dcx = SEC_ASN1DecoderStart(cmsg->poolp, cmsg, SecCmsMessageTemplate, NULL, 0);
626 if (p7dcx->dcx == NULL) {
627 PORT_Free (p7dcx);
628 SecCmsMessageDestroy(cmsg);
629 goto loser;
630 }
631
632 SEC_ASN1DecoderSetNotifyProc (p7dcx->dcx, nss_cms_decoder_notify, p7dcx);
633
634 p7dcx->cmsg = cmsg;
635 p7dcx->type = SEC_OID_UNKNOWN;
636
637 p7dcx->cb = cb;
638 p7dcx->cb_arg = cb_arg;
639
640 *outDecoder = p7dcx;
641 return noErr;
642
643 loser:
644 result = PORT_GetError();
645 PORT_SetError(0); // Clean the thread error since we've returned the error
646 return result;
647 }
648
649 /*
650 * SecCmsDecoderUpdate - feed DER-encoded data to decoder
651 */
652 OSStatus
653 SecCmsDecoderUpdate(SecCmsDecoderRef p7dcx, const void *buf, CFIndex len)
654 {
655 if (!p7dcx) {
656 return errSecParam;
657 }
658
659 if (p7dcx->dcx != NULL && p7dcx->error == 0) { /* if error is set already, don't bother */
660 if (SEC_ASN1DecoderUpdate (p7dcx->dcx, buf, len) != SECSuccess) {
661 p7dcx->error = PORT_GetError();
662 PORT_Assert (p7dcx->error);
663 if (p7dcx->error == 0)
664 p7dcx->error = -1;
665 }
666 }
667
668 if (p7dcx->error == 0)
669 return 0;
670
671 /* there has been a problem, let's finish the decoder */
672 if (p7dcx->dcx != NULL) {
673 /* @@@ Change this to SEC_ASN1DecoderAbort()? */
674 (void) SEC_ASN1DecoderFinish (p7dcx->dcx);
675 p7dcx->dcx = NULL;
676 }
677 PORT_SetError (0); // Clean the thread error since we've returned the error
678
679 return p7dcx->error;
680 }
681
682 /*
683 * SecCmsDecoderDestroy - stop decoding in case of error
684 */
685 void
686 SecCmsDecoderDestroy(SecCmsDecoderRef p7dcx)
687 {
688 /* SecCmsMessageDestroy frees inner decoders and digests. */
689 if (p7dcx->cmsg) {
690 SecCmsMessageDestroy(p7dcx->cmsg);
691 }
692 if (p7dcx->dcx) {
693 (void)SEC_ASN1DecoderFinish(p7dcx->dcx);
694 }
695 /* Clear out references */
696 p7dcx->cmsg = NULL;
697 p7dcx->dcx = NULL;
698 p7dcx->childp7dcx = NULL;
699 PORT_Free(p7dcx);
700 }
701
702 /*
703 * SecCmsDecoderFinish - mark the end of inner content and finish decoding
704 */
705 OSStatus
706 SecCmsDecoderFinish(SecCmsDecoderRef p7dcx, SecCmsMessageRef *outMessage)
707 {
708 SecCmsMessageRef cmsg;
709 OSStatus result;
710
711 cmsg = p7dcx->cmsg;
712
713 if (p7dcx->dcx == NULL || SEC_ASN1DecoderFinish(p7dcx->dcx) != SECSuccess ||
714 nss_cms_after_end(p7dcx) != SECSuccess)
715 {
716 if (p7dcx->cmsg) {
717 SecCmsMessageDestroy(cmsg); /* needs to get rid of pool if it's ours */
718 }
719 result = PORT_GetError();
720 goto loser;
721 }
722
723 *outMessage = cmsg;
724 result = noErr;
725
726 loser:
727 /* Clear out references */
728 p7dcx->cmsg = NULL;
729 p7dcx->dcx = NULL;
730 p7dcx->childp7dcx = NULL;
731 PORT_Free(p7dcx);
732 PORT_SetError(0); // Clean the thread error since we've returned the error
733 return result;
734 }
735
736 OSStatus
737 SecCmsMessageDecode(const CSSM_DATA *encodedMessage,
738 SecCmsContentCallback cb, void *cb_arg,
739 PK11PasswordFunc pwfn, void *pwfn_arg,
740 SecCmsGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
741 SecCmsMessageRef *outMessage)
742 {
743 OSStatus result;
744 SecCmsDecoderRef decoder;
745
746 result = SecCmsDecoderCreate(NULL, cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg, &decoder);
747 if (result)
748 goto loser;
749 result = SecCmsDecoderUpdate(decoder, encodedMessage->Data, encodedMessage->Length);
750 if (result) {
751 SecCmsDecoderDestroy(decoder);
752 goto loser;
753 }
754
755 result = SecCmsDecoderFinish(decoder, outMessage);
756 loser:
757 return result;
758 }
759