]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
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 | ||
d8f41ccd | 45 | #include "SecAsn1Item.h" |
b1ab9ed8 | 46 | #include "secoid.h" |
d8f41ccd | 47 | |
b1ab9ed8 A |
48 | #include <security_asn1/secasn1.h> |
49 | #include <security_asn1/secerr.h> | |
d8f41ccd A |
50 | #include <security_asn1/secport.h> |
51 | ||
52 | #include <limits.h> | |
b1ab9ed8 A |
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 | ||
d8f41ccd A |
66 | /* We use size_t for len in this function because the SEC_ASN1Decoder* layer |
67 | uses that for callback function */ | |
b1ab9ed8 A |
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 | |
d8f41ccd | 98 | fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth); |
b1ab9ed8 A |
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: | |
b1ab9ed8 A |
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: | |
d8f41ccd | 158 | p7dcx->content.signedData->contentInfo.cmsg = p7dcx->cmsg; |
b1ab9ed8 A |
159 | break; |
160 | case SEC_OID_PKCS7_DIGESTED_DATA: | |
d8f41ccd | 161 | p7dcx->content.digestedData->contentInfo.cmsg = p7dcx->cmsg; |
b1ab9ed8 A |
162 | break; |
163 | case SEC_OID_PKCS7_ENVELOPED_DATA: | |
d8f41ccd | 164 | p7dcx->content.envelopedData->contentInfo.cmsg = p7dcx->cmsg; |
b1ab9ed8 A |
165 | break; |
166 | case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
d8f41ccd | 167 | p7dcx->content.encryptedData->contentInfo.cmsg = p7dcx->cmsg; |
b1ab9ed8 A |
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 | } | |
186 | } | |
187 | if (after && dest == &(cinfo->rawContent)) { | |
188 | /* we're right after of the data */ | |
189 | if (nss_cms_after_data(p7dcx) != SECSuccess) | |
190 | p7dcx->error = PORT_GetError(); | |
191 | ||
192 | /* we don't need to see the contents anymore */ | |
193 | SEC_ASN1DecoderClearFilterProc(p7dcx->dcx); | |
194 | } | |
195 | break; | |
196 | ||
197 | #if 0 /* NIH */ | |
198 | case SEC_OID_PKCS7_AUTHENTICATED_DATA: | |
199 | #endif | |
200 | default: | |
201 | /* unsupported or unknown message type - fail (more or less) gracefully */ | |
202 | p7dcx->error = SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE; | |
203 | break; | |
204 | } | |
205 | } | |
206 | ||
207 | /* | |
208 | * nss_cms_before_data - set up the current encoder to receive data | |
209 | */ | |
210 | static OSStatus | |
211 | nss_cms_before_data(SecCmsDecoderRef p7dcx) | |
212 | { | |
213 | OSStatus rv; | |
214 | SECOidTag childtype; | |
215 | PLArenaPool *poolp; | |
216 | SecCmsDecoderRef childp7dcx; | |
217 | SecCmsContentInfoRef cinfo; | |
218 | const SecAsn1Template *template; | |
219 | void *mark = NULL; | |
220 | size_t size; | |
221 | ||
222 | poolp = p7dcx->cmsg->poolp; | |
223 | ||
224 | /* call _Decode_BeforeData handlers */ | |
225 | switch (p7dcx->type) { | |
226 | case SEC_OID_PKCS7_SIGNED_DATA: | |
227 | /* we're decoding a signedData, so set up the digests */ | |
228 | rv = SecCmsSignedDataDecodeBeforeData(p7dcx->content.signedData); | |
229 | if (rv != SECSuccess) | |
230 | return SECFailure; | |
231 | break; | |
232 | case SEC_OID_PKCS7_DIGESTED_DATA: | |
233 | /* we're encoding a digestedData, so set up the digest */ | |
234 | rv = SecCmsDigestedDataDecodeBeforeData(p7dcx->content.digestedData); | |
235 | if (rv != SECSuccess) | |
236 | return SECFailure; | |
237 | break; | |
238 | case SEC_OID_PKCS7_ENVELOPED_DATA: | |
239 | rv = SecCmsEnvelopedDataDecodeBeforeData(p7dcx->content.envelopedData); | |
240 | if (rv != SECSuccess) | |
241 | return SECFailure; | |
242 | break; | |
243 | case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
244 | rv = SecCmsEncryptedDataDecodeBeforeData(p7dcx->content.encryptedData); | |
245 | if (rv != SECSuccess) | |
246 | return SECFailure; | |
247 | break; | |
248 | default: | |
249 | return SECFailure; | |
250 | } | |
251 | ||
252 | /* ok, now we have a pointer to cinfo */ | |
253 | /* find out what kind of data is encapsulated */ | |
254 | ||
255 | cinfo = SecCmsContentGetContentInfo(p7dcx->content.pointer, p7dcx->type); | |
256 | childtype = SecCmsContentInfoGetContentTypeTag(cinfo); | |
d8f41ccd A |
257 | |
258 | if (childtype == SEC_OID_PKCS7_DATA) { | |
b1ab9ed8 A |
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 | ||
b1ab9ed8 | 285 | /* start the child decoder */ |
822b670c | 286 | childp7dcx->dcx = SEC_ASN1DecoderStart(poolp, childp7dcx->content.pointer, template, NULL, 0); |
b1ab9ed8 A |
287 | if (childp7dcx->dcx == NULL) |
288 | goto loser; | |
289 | ||
290 | /* the new decoder needs to notify, too */ | |
291 | SEC_ASN1DecoderSetNotifyProc(childp7dcx->dcx, nss_cms_decoder_notify, childp7dcx); | |
292 | ||
293 | /* tell the parent decoder that it needs to feed us the content data */ | |
294 | p7dcx->childp7dcx = childp7dcx; | |
295 | ||
296 | childp7dcx->type = childtype; /* our type */ | |
297 | ||
298 | childp7dcx->cmsg = p7dcx->cmsg; /* backpointer to root message */ | |
299 | ||
300 | /* should the child decoder encounter real data, it needs to give it to the caller */ | |
301 | childp7dcx->cb = p7dcx->cb; | |
302 | childp7dcx->cb_arg = p7dcx->cb_arg; | |
303 | ||
304 | /* now set up the parent to hand decoded data to the next level decoder */ | |
305 | p7dcx->cb = (SecCmsContentCallback)SecCmsDecoderUpdate; | |
306 | p7dcx->cb_arg = childp7dcx; | |
307 | ||
308 | PORT_ArenaUnmark(poolp, mark); | |
309 | ||
310 | return SECSuccess; | |
311 | ||
312 | loser: | |
313 | if (mark) | |
314 | PORT_ArenaRelease(poolp, mark); | |
315 | if (childp7dcx) | |
316 | PORT_Free(childp7dcx); | |
317 | p7dcx->childp7dcx = NULL; | |
318 | return SECFailure; | |
319 | } | |
320 | ||
321 | static OSStatus | |
322 | nss_cms_after_data(SecCmsDecoderRef p7dcx) | |
323 | { | |
b1ab9ed8 A |
324 | SecCmsDecoderRef childp7dcx; |
325 | OSStatus rv = SECFailure; | |
326 | ||
b1ab9ed8 A |
327 | /* Handle last block. This is necessary to flush out the last bytes |
328 | * of a possibly incomplete block */ | |
329 | nss_cms_decoder_work_data(p7dcx, NULL, 0, PR_TRUE); | |
330 | ||
331 | /* finish any "inner" decoders - there's no more data coming... */ | |
332 | if (p7dcx->childp7dcx != NULL) { | |
333 | childp7dcx = p7dcx->childp7dcx; | |
334 | if (childp7dcx->dcx != NULL) { | |
335 | if (SEC_ASN1DecoderFinish(childp7dcx->dcx) != SECSuccess) { | |
336 | /* do what? free content? */ | |
337 | rv = SECFailure; | |
338 | } else { | |
339 | rv = nss_cms_after_end(childp7dcx); | |
340 | } | |
341 | if (rv != SECSuccess) | |
342 | goto done; | |
343 | } | |
344 | PORT_Free(p7dcx->childp7dcx); | |
345 | p7dcx->childp7dcx = NULL; | |
346 | } | |
347 | ||
348 | switch (p7dcx->type) { | |
349 | case SEC_OID_PKCS7_SIGNED_DATA: | |
350 | /* this will finish the digests and verify */ | |
351 | rv = SecCmsSignedDataDecodeAfterData(p7dcx->content.signedData); | |
352 | break; | |
353 | case SEC_OID_PKCS7_ENVELOPED_DATA: | |
354 | rv = SecCmsEnvelopedDataDecodeAfterData(p7dcx->content.envelopedData); | |
355 | break; | |
356 | case SEC_OID_PKCS7_DIGESTED_DATA: | |
357 | rv = SecCmsDigestedDataDecodeAfterData(p7dcx->content.digestedData); | |
358 | break; | |
359 | case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
360 | rv = SecCmsEncryptedDataDecodeAfterData(p7dcx->content.encryptedData); | |
361 | break; | |
362 | case SEC_OID_PKCS7_DATA: | |
363 | /* do nothing */ | |
364 | break; | |
365 | default: | |
366 | rv = SECFailure; | |
367 | break; | |
368 | } | |
369 | done: | |
370 | return rv; | |
371 | } | |
372 | ||
373 | static OSStatus | |
374 | nss_cms_after_end(SecCmsDecoderRef p7dcx) | |
375 | { | |
376 | OSStatus rv; | |
377 | PLArenaPool *poolp; | |
378 | ||
379 | poolp = p7dcx->cmsg->poolp; | |
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; | |
d8f41ccd | 418 | size_t offset; |
b1ab9ed8 | 419 | OSStatus rv; |
d8f41ccd A |
420 | SecAsn1Item * storage; |
421 | ||
b1ab9ed8 A |
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 | PORT_Assert ((data != NULL && len) || final); | |
d8f41ccd A |
430 | /* Debug check for 64 bits cast later */ |
431 | PORT_Assert (len <= UINT_MAX); | |
b1ab9ed8 A |
432 | |
433 | if (!p7dcx->content.pointer) // might be ExContent?? | |
434 | return; | |
435 | ||
436 | cinfo = SecCmsContentGetContentInfo(p7dcx->content.pointer, p7dcx->type); | |
437 | ||
438 | if (cinfo->ciphcx != NULL) { | |
439 | /* | |
440 | * we are decrypting. | |
441 | * | |
442 | * XXX If we get an error, we do not want to do the digest or callback, | |
443 | * but we want to keep decoding. Or maybe we want to stop decoding | |
444 | * altogether if there is a callback, because obviously we are not | |
445 | * sending the data back and they want to know that. | |
446 | */ | |
447 | ||
d8f41ccd A |
448 | unsigned int outlen = 0; /* length of decrypted data */ |
449 | unsigned int buflen; /* length available for decrypted data */ | |
b1ab9ed8 A |
450 | |
451 | /* find out about the length of decrypted data */ | |
d8f41ccd A |
452 | /* 64 bits cast: Worst case here is we may not decrypt the full CMS blob, if the blob is bigger than 4GB */ |
453 | buflen = SecCmsCipherContextDecryptLength(cinfo->ciphcx, (unsigned int)len, final); | |
b1ab9ed8 A |
454 | |
455 | /* | |
456 | * it might happen that we did not provide enough data for a full | |
457 | * block (decryption unit), and that there is no output available | |
458 | */ | |
459 | ||
460 | /* no output available, AND no input? */ | |
461 | if (buflen == 0 && len == 0) | |
462 | goto loser; /* bail out */ | |
463 | ||
464 | /* | |
465 | * have inner decoder: pass the data on (means inner content type is NOT data) | |
466 | * no inner decoder: we have DATA in here: either call callback or store | |
467 | */ | |
468 | if (buflen != 0) { | |
469 | /* there will be some output - need to make room for it */ | |
470 | /* allocate buffer from the heap */ | |
471 | buf = (unsigned char *)PORT_Alloc(buflen); | |
472 | if (buf == NULL) { | |
473 | p7dcx->error = SEC_ERROR_NO_MEMORY; | |
474 | goto loser; | |
475 | } | |
476 | } | |
477 | ||
478 | /* | |
479 | * decrypt incoming data | |
480 | * buf can still be NULL here (and buflen == 0) here if we don't expect | |
481 | * any output (see above), but we still need to call SecCmsCipherContextDecrypt to | |
482 | * keep track of incoming data | |
483 | */ | |
484 | rv = SecCmsCipherContextDecrypt(cinfo->ciphcx, buf, &outlen, buflen, | |
d8f41ccd | 485 | data, (unsigned int)len, final); |
b1ab9ed8 A |
486 | if (rv != SECSuccess) { |
487 | p7dcx->error = PORT_GetError(); | |
488 | goto loser; | |
489 | } | |
490 | ||
d8f41ccd | 491 | //PORT_Assert (final || outlen == buflen); |
b1ab9ed8 A |
492 | |
493 | /* swap decrypted data in */ | |
494 | data = buf; | |
495 | len = outlen; | |
496 | } | |
497 | ||
498 | if (len == 0) | |
499 | goto done; /* nothing more to do */ | |
500 | ||
501 | /* | |
502 | * Update the running digests with plaintext bytes (if we need to). | |
503 | */ | |
504 | if (cinfo->digcx) | |
505 | SecCmsDigestContextUpdate(cinfo->digcx, data, len); | |
506 | ||
507 | /* at this point, we have the plain decoded & decrypted data */ | |
508 | /* which is either more encoded DER which we need to hand to the child decoder */ | |
509 | /* or data we need to hand back to our caller */ | |
510 | ||
511 | /* pass the content back to our caller or */ | |
512 | /* feed our freshly decrypted and decoded data into child decoder */ | |
513 | if (p7dcx->cb != NULL) { | |
514 | (*p7dcx->cb)(p7dcx->cb_arg, (const char *)data, len); | |
515 | } | |
516 | #if 1 | |
517 | else | |
518 | #endif | |
d8f41ccd | 519 | if (SecCmsContentInfoGetContentTypeTag(cinfo) == SEC_OID_PKCS7_DATA) { |
b1ab9ed8 A |
520 | /* store it in "inner" data item as well */ |
521 | /* find the DATA item in the encapsulated cinfo and store it there */ | |
522 | storage = cinfo->content.data; | |
523 | ||
524 | offset = storage->Length; | |
60c433a9 A |
525 | |
526 | /* check for potential overflow */ | |
527 | if (len >= (size_t)(INT_MAX - storage->Length)) { | |
528 | p7dcx->error = SEC_ERROR_NO_MEMORY; | |
529 | goto loser; | |
530 | } | |
531 | ||
b1ab9ed8 A |
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 | ||
552 | done: | |
553 | loser: | |
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(). | |
d8f41ccd A |
565 | * |
566 | * len has to be of type size_t because it is a callback to the | |
567 | * SEC_ASN1Decoder layer | |
b1ab9ed8 A |
568 | */ |
569 | static void | |
570 | nss_cms_decoder_update_filter (void *arg, const char *data, size_t len, | |
571 | int depth, SEC_ASN1EncodingPart data_kind) | |
572 | { | |
573 | SecCmsDecoderRef p7dcx; | |
574 | ||
575 | PORT_Assert (len); /* paranoia */ | |
576 | if (len == 0) | |
577 | return; | |
578 | ||
579 | p7dcx = (SecCmsDecoderRef)arg; | |
580 | ||
581 | p7dcx->saw_contents = PR_TRUE; | |
582 | ||
583 | /* pass on the content bytes only */ | |
584 | if (data_kind == SEC_ASN1_Contents) | |
585 | nss_cms_decoder_work_data(p7dcx, (const unsigned char *) data, len, PR_FALSE); | |
586 | } | |
587 | ||
588 | /* | |
589 | * SecCmsDecoderCreate - set up decoding of a BER-encoded CMS message | |
590 | */ | |
591 | OSStatus | |
d8f41ccd | 592 | SecCmsDecoderCreate(SecCmsContentCallback cb, void *cb_arg, |
b1ab9ed8 A |
593 | PK11PasswordFunc pwfn, void *pwfn_arg, |
594 | SecCmsGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg, | |
595 | SecCmsDecoderRef *outDecoder) | |
596 | { | |
597 | SecCmsDecoderRef p7dcx; | |
598 | SecCmsMessageRef cmsg; | |
599 | OSStatus result; | |
600 | ||
d8f41ccd | 601 | cmsg = SecCmsMessageCreate(); |
b1ab9ed8 A |
602 | if (cmsg == NULL) |
603 | goto loser; | |
604 | ||
d8f41ccd | 605 | SecCmsMessageSetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg); |
b1ab9ed8 A |
606 | |
607 | p7dcx = (SecCmsDecoderRef)PORT_ZAlloc(sizeof(struct SecCmsDecoderStr)); | |
608 | if (p7dcx == NULL) { | |
609 | SecCmsMessageDestroy(cmsg); | |
610 | goto loser; | |
611 | } | |
612 | ||
822b670c | 613 | p7dcx->dcx = SEC_ASN1DecoderStart(cmsg->poolp, cmsg, SecCmsMessageTemplate, NULL, 0); |
b1ab9ed8 A |
614 | if (p7dcx->dcx == NULL) { |
615 | PORT_Free (p7dcx); | |
616 | SecCmsMessageDestroy(cmsg); | |
617 | goto loser; | |
618 | } | |
619 | ||
620 | SEC_ASN1DecoderSetNotifyProc (p7dcx->dcx, nss_cms_decoder_notify, p7dcx); | |
621 | ||
622 | p7dcx->cmsg = cmsg; | |
623 | p7dcx->type = SEC_OID_UNKNOWN; | |
624 | ||
625 | p7dcx->cb = cb; | |
626 | p7dcx->cb_arg = cb_arg; | |
627 | ||
628 | *outDecoder = p7dcx; | |
d8f41ccd | 629 | return errSecSuccess; |
b1ab9ed8 A |
630 | |
631 | loser: | |
632 | result = PORT_GetError(); | |
633 | return result; | |
634 | } | |
635 | ||
636 | /* | |
637 | * SecCmsDecoderUpdate - feed DER-encoded data to decoder | |
638 | */ | |
639 | OSStatus | |
640 | SecCmsDecoderUpdate(SecCmsDecoderRef p7dcx, const void *buf, CFIndex len) | |
641 | { | |
642 | if (p7dcx->dcx != NULL && p7dcx->error == 0) { /* if error is set already, don't bother */ | |
643 | if (SEC_ASN1DecoderUpdate (p7dcx->dcx, buf, len) != SECSuccess) { | |
644 | p7dcx->error = PORT_GetError(); | |
645 | PORT_Assert (p7dcx->error); | |
646 | if (p7dcx->error == 0) | |
647 | p7dcx->error = -1; | |
648 | } | |
649 | } | |
650 | ||
651 | if (p7dcx->error == 0) | |
652 | return 0; | |
653 | ||
654 | /* there has been a problem, let's finish the decoder */ | |
655 | if (p7dcx->dcx != NULL) { | |
656 | /* @@@ Change this to SEC_ASN1DecoderAbort()? */ | |
657 | (void) SEC_ASN1DecoderFinish (p7dcx->dcx); | |
658 | p7dcx->dcx = NULL; | |
659 | } | |
660 | PORT_SetError (p7dcx->error); | |
661 | ||
662 | return p7dcx->error; | |
663 | } | |
664 | ||
665 | /* | |
666 | * SecCmsDecoderDestroy - stop decoding in case of error | |
667 | */ | |
668 | void | |
669 | SecCmsDecoderDestroy(SecCmsDecoderRef p7dcx) | |
670 | { | |
671 | /* XXXX what about inner decoders? running digests? decryption? */ | |
672 | /* XXXX there's a leak here! */ | |
673 | SecCmsMessageDestroy(p7dcx->cmsg); | |
674 | if (p7dcx->dcx) | |
675 | (void)SEC_ASN1DecoderFinish(p7dcx->dcx); | |
676 | PORT_Free(p7dcx); | |
677 | } | |
678 | ||
679 | /* | |
680 | * SecCmsDecoderFinish - mark the end of inner content and finish decoding | |
681 | */ | |
682 | OSStatus | |
683 | SecCmsDecoderFinish(SecCmsDecoderRef p7dcx, SecCmsMessageRef *outMessage) | |
684 | { | |
685 | SecCmsMessageRef cmsg; | |
686 | OSStatus result; | |
687 | ||
688 | cmsg = p7dcx->cmsg; | |
689 | ||
690 | if (p7dcx->dcx == NULL || SEC_ASN1DecoderFinish(p7dcx->dcx) != SECSuccess || | |
691 | nss_cms_after_end(p7dcx) != SECSuccess) | |
692 | { | |
d8f41ccd | 693 | SecCmsMessageDestroy(cmsg); |
b1ab9ed8 A |
694 | result = PORT_GetError(); |
695 | goto loser; | |
696 | } | |
697 | ||
698 | *outMessage = cmsg; | |
d8f41ccd | 699 | result = errSecSuccess; |
b1ab9ed8 A |
700 | |
701 | loser: | |
702 | PORT_Free(p7dcx); | |
703 | return result; | |
704 | } | |
705 | ||
706 | OSStatus | |
d8f41ccd | 707 | SecCmsMessageDecode(const SecAsn1Item *encodedMessage, |
b1ab9ed8 A |
708 | SecCmsContentCallback cb, void *cb_arg, |
709 | PK11PasswordFunc pwfn, void *pwfn_arg, | |
710 | SecCmsGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg, | |
711 | SecCmsMessageRef *outMessage) | |
712 | { | |
713 | OSStatus result; | |
d8f41ccd | 714 | SecCmsDecoderRef decoder = NULL; |
b1ab9ed8 | 715 | |
d8f41ccd | 716 | result = SecCmsDecoderCreate(cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg, &decoder); |
b1ab9ed8 A |
717 | if (result) |
718 | goto loser; | |
719 | result = SecCmsDecoderUpdate(decoder, encodedMessage->Data, encodedMessage->Length); | |
720 | if (result) { | |
721 | SecCmsDecoderDestroy(decoder); | |
722 | goto loser; | |
723 | } | |
724 | ||
725 | result = SecCmsDecoderFinish(decoder, outMessage); | |
726 | loser: | |
727 | return result; | |
728 | } |