]>
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(); | |
ecaf5866 | 185 | PORT_SetError(0); |
b1ab9ed8 A |
186 | } |
187 | } | |
188 | if (after && dest == &(cinfo->rawContent)) { | |
189 | /* we're right after of the data */ | |
ecaf5866 | 190 | if (nss_cms_after_data(p7dcx) != SECSuccess) { |
b1ab9ed8 | 191 | p7dcx->error = PORT_GetError(); |
ecaf5866 A |
192 | PORT_SetError(0); |
193 | } | |
b1ab9ed8 A |
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); | |
d8f41ccd A |
260 | |
261 | if (childtype == SEC_OID_PKCS7_DATA) { | |
b1ab9ed8 A |
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 | ||
b1ab9ed8 | 288 | /* start the child decoder */ |
822b670c | 289 | childp7dcx->dcx = SEC_ASN1DecoderStart(poolp, childp7dcx->content.pointer, template, NULL, 0); |
b1ab9ed8 A |
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 | { | |
b1ab9ed8 A |
327 | SecCmsDecoderRef childp7dcx; |
328 | OSStatus rv = SECFailure; | |
329 | ||
b1ab9ed8 A |
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; | |
b1ab9ed8 A |
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 | */ | |
b54c578e A |
429 | if (((data == NULL || len == 0) && !final) || |
430 | len > UINT_MAX) { | |
431 | goto loser; | |
432 | } | |
b1ab9ed8 A |
433 | |
434 | if (!p7dcx->content.pointer) // might be ExContent?? | |
435 | return; | |
b54c578e | 436 | |
b1ab9ed8 A |
437 | cinfo = SecCmsContentGetContentInfo(p7dcx->content.pointer, p7dcx->type); |
438 | ||
439 | if (cinfo->ciphcx != NULL) { | |
b54c578e A |
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 */ | |
d8f41ccd | 453 | /* 64 bits cast: Worst case here is we may not decrypt the full CMS blob, if the blob is bigger than 4GB */ |
b54c578e A |
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; | |
b1ab9ed8 A |
497 | } |
498 | ||
499 | if (len == 0) | |
b54c578e | 500 | goto done; /* nothing more to do */ |
b1ab9ed8 A |
501 | |
502 | /* | |
503 | * Update the running digests with plaintext bytes (if we need to). | |
504 | */ | |
505 | if (cinfo->digcx) | |
b54c578e | 506 | SecCmsDigestContextUpdate(cinfo->digcx, data, len); |
b1ab9ed8 A |
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) { | |
b54c578e A |
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 | } | |
b1ab9ed8 A |
549 | } |
550 | ||
551 | done: | |
552 | loser: | |
553 | if (buf) | |
b54c578e | 554 | PORT_Free (buf); |
b1ab9ed8 A |
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(). | |
d8f41ccd A |
564 | * |
565 | * len has to be of type size_t because it is a callback to the | |
566 | * SEC_ASN1Decoder layer | |
b1ab9ed8 A |
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 | |
d8f41ccd | 591 | SecCmsDecoderCreate(SecCmsContentCallback cb, void *cb_arg, |
b1ab9ed8 A |
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 | ||
ecaf5866 A |
600 | /* Clear the thread error to clean up dirty threads */ |
601 | PORT_SetError(0); | |
602 | ||
d8f41ccd | 603 | cmsg = SecCmsMessageCreate(); |
b1ab9ed8 A |
604 | if (cmsg == NULL) |
605 | goto loser; | |
606 | ||
d8f41ccd | 607 | SecCmsMessageSetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg); |
b1ab9ed8 A |
608 | |
609 | p7dcx = (SecCmsDecoderRef)PORT_ZAlloc(sizeof(struct SecCmsDecoderStr)); | |
610 | if (p7dcx == NULL) { | |
611 | SecCmsMessageDestroy(cmsg); | |
612 | goto loser; | |
613 | } | |
614 | ||
822b670c | 615 | p7dcx->dcx = SEC_ASN1DecoderStart(cmsg->poolp, cmsg, SecCmsMessageTemplate, NULL, 0); |
b1ab9ed8 A |
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; | |
d8f41ccd | 631 | return errSecSuccess; |
b1ab9ed8 A |
632 | |
633 | loser: | |
634 | result = PORT_GetError(); | |
ecaf5866 | 635 | PORT_SetError(0); // Clean the thread error since we've returned the error |
b1ab9ed8 A |
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 | { | |
6b200bc3 A |
645 | if (!p7dcx) { |
646 | return errSecParam; | |
647 | } | |
648 | ||
b1ab9ed8 A |
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 | } | |
ecaf5866 A |
667 | |
668 | PORT_SetError (0); // Clean the thread error since we've returned the error | |
b1ab9ed8 A |
669 | |
670 | return p7dcx->error; | |
671 | } | |
672 | ||
673 | /* | |
674 | * SecCmsDecoderDestroy - stop decoding in case of error | |
675 | */ | |
676 | void | |
677 | SecCmsDecoderDestroy(SecCmsDecoderRef p7dcx) | |
678 | { | |
6b200bc3 | 679 | /* SecCmsMessageDestroy frees inner decoders and digests. */ |
b1ab9ed8 | 680 | SecCmsMessageDestroy(p7dcx->cmsg); |
6b200bc3 | 681 | p7dcx->cmsg = NULL; |
b1ab9ed8 A |
682 | if (p7dcx->dcx) |
683 | (void)SEC_ASN1DecoderFinish(p7dcx->dcx); | |
6b200bc3 A |
684 | /* Clear out references */ |
685 | p7dcx->cmsg = NULL; | |
686 | p7dcx->dcx = NULL; | |
687 | p7dcx->childp7dcx = NULL; | |
b1ab9ed8 A |
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 | { | |
6b200bc3 A |
705 | if (p7dcx->cmsg) { |
706 | SecCmsMessageDestroy(cmsg); | |
707 | } | |
b1ab9ed8 A |
708 | result = PORT_GetError(); |
709 | goto loser; | |
710 | } | |
711 | ||
712 | *outMessage = cmsg; | |
d8f41ccd | 713 | result = errSecSuccess; |
b1ab9ed8 A |
714 | |
715 | loser: | |
6b200bc3 A |
716 | /* Clear out references */ |
717 | p7dcx->cmsg = NULL; | |
718 | p7dcx->dcx = NULL; | |
719 | p7dcx->childp7dcx = NULL; | |
b1ab9ed8 | 720 | PORT_Free(p7dcx); |
ecaf5866 | 721 | PORT_SetError(0); // Clean the thread error since we've returned the error |
b1ab9ed8 A |
722 | return result; |
723 | } | |
724 | ||
725 | OSStatus | |
d8f41ccd | 726 | SecCmsMessageDecode(const SecAsn1Item *encodedMessage, |
b1ab9ed8 A |
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; | |
d8f41ccd | 733 | SecCmsDecoderRef decoder = NULL; |
b1ab9ed8 | 734 | |
d8f41ccd | 735 | result = SecCmsDecoderCreate(cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg, &decoder); |
b1ab9ed8 A |
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 | } |