/src/mozilla-central/security/nss/lib/smime/cmsdecode.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | /* |
6 | | * CMS decoding. |
7 | | */ |
8 | | |
9 | | #include "cmslocal.h" |
10 | | |
11 | | #include "cert.h" |
12 | | #include "keyhi.h" |
13 | | #include "secasn1.h" |
14 | | #include "secitem.h" |
15 | | #include "secoid.h" |
16 | | #include "prtime.h" |
17 | | #include "secerr.h" |
18 | | |
19 | | struct NSSCMSDecoderContextStr { |
20 | | SEC_ASN1DecoderContext *dcx; /* ASN.1 decoder context */ |
21 | | NSSCMSMessage *cmsg; /* backpointer to the root message */ |
22 | | SECOidTag type; /* type of message */ |
23 | | NSSCMSContent content; /* pointer to message */ |
24 | | NSSCMSDecoderContext *childp7dcx; /* inner CMS decoder context */ |
25 | | PRBool saw_contents; |
26 | | int error; |
27 | | NSSCMSContentCallback cb; |
28 | | void *cb_arg; |
29 | | PRBool first_decoded; |
30 | | PRBool need_indefinite_finish; |
31 | | }; |
32 | | |
33 | | struct NSSCMSDecoderDataStr { |
34 | | SECItem data; /* must be first */ |
35 | | unsigned int totalBufferSize; |
36 | | }; |
37 | | |
38 | | typedef struct NSSCMSDecoderDataStr NSSCMSDecoderData; |
39 | | |
40 | | static void nss_cms_decoder_update_filter(void *arg, const char *data, |
41 | | unsigned long len, int depth, |
42 | | SEC_ASN1EncodingPart data_kind); |
43 | | static SECStatus nss_cms_before_data(NSSCMSDecoderContext *p7dcx); |
44 | | static SECStatus nss_cms_after_data(NSSCMSDecoderContext *p7dcx); |
45 | | static SECStatus nss_cms_after_end(NSSCMSDecoderContext *p7dcx); |
46 | | static void nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx, |
47 | | const unsigned char *data, |
48 | | unsigned long len, |
49 | | PRBool final); |
50 | | static NSSCMSDecoderData *nss_cms_create_decoder_data(PLArenaPool *poolp); |
51 | | |
52 | | extern const SEC_ASN1Template NSSCMSMessageTemplate[]; |
53 | | |
54 | | static NSSCMSDecoderData * |
55 | | nss_cms_create_decoder_data(PLArenaPool *poolp) |
56 | 0 | { |
57 | 0 | NSSCMSDecoderData *decoderData = NULL; |
58 | 0 |
|
59 | 0 | decoderData = (NSSCMSDecoderData *) |
60 | 0 | PORT_ArenaAlloc(poolp, sizeof(NSSCMSDecoderData)); |
61 | 0 | if (!decoderData) { |
62 | 0 | return NULL; |
63 | 0 | } |
64 | 0 | decoderData->data.data = NULL; |
65 | 0 | decoderData->data.len = 0; |
66 | 0 | decoderData->totalBufferSize = 0; |
67 | 0 | return decoderData; |
68 | 0 | } |
69 | | |
70 | | /* |
71 | | * nss_cms_decoder_notify - |
72 | | * this is the driver of the decoding process. It gets called by the ASN.1 |
73 | | * decoder before and after an object is decoded. |
74 | | * at various points in the decoding process, we intercept to set up and do |
75 | | * further processing. |
76 | | */ |
77 | | static void |
78 | | nss_cms_decoder_notify(void *arg, PRBool before, void *dest, int depth) |
79 | 0 | { |
80 | 0 | NSSCMSDecoderContext *p7dcx; |
81 | 0 | NSSCMSContentInfo *rootcinfo, *cinfo; |
82 | 0 | PRBool after = !before; |
83 | 0 |
|
84 | 0 | p7dcx = (NSSCMSDecoderContext *)arg; |
85 | 0 | rootcinfo = &(p7dcx->cmsg->contentInfo); |
86 | 0 |
|
87 | 0 | /* XXX error handling: need to set p7dcx->error */ |
88 | 0 |
|
89 | | #ifdef CMSDEBUG |
90 | | fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", |
91 | | dest, depth); |
92 | | #endif |
93 | |
|
94 | 0 | /* so what are we working on right now? */ |
95 | 0 | if (p7dcx->type == SEC_OID_UNKNOWN) { |
96 | 0 | /* |
97 | 0 | * right now, we are still decoding the OUTER (root) cinfo |
98 | 0 | * As soon as we know the inner content type, set up the info, |
99 | 0 | * but NO inner decoder or filter. The root decoder handles the first |
100 | 0 | * level children by itself - only for encapsulated contents (which |
101 | 0 | * are encoded as DER inside of an OCTET STRING) we need to set up a |
102 | 0 | * child decoder... |
103 | 0 | */ |
104 | 0 | if (after && dest == &(rootcinfo->contentType)) { |
105 | 0 | p7dcx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo); |
106 | 0 | p7dcx->content = rootcinfo->content; |
107 | 0 | /* is this ready already ? need to alloc? */ |
108 | 0 | /* XXX yes we need to alloc -- continue here */ |
109 | 0 | } |
110 | 0 | } else if (NSS_CMSType_IsData(p7dcx->type)) { |
111 | 0 | /* this can only happen if the outermost cinfo has DATA in it */ |
112 | 0 | /* otherwise, we handle this type implicitely in the inner decoders */ |
113 | 0 |
|
114 | 0 | if (before && dest == &(rootcinfo->content)) { |
115 | 0 | /* cause the filter to put the data in the right place... |
116 | 0 | ** We want the ASN.1 decoder to deliver the decoded bytes to us |
117 | 0 | ** from now on |
118 | 0 | */ |
119 | 0 | SEC_ASN1DecoderSetFilterProc(p7dcx->dcx, |
120 | 0 | nss_cms_decoder_update_filter, |
121 | 0 | p7dcx, |
122 | 0 | (PRBool)(p7dcx->cb != NULL)); |
123 | 0 | } else if (after && dest == &(rootcinfo->content.data)) { |
124 | 0 | /* remove the filter */ |
125 | 0 | SEC_ASN1DecoderClearFilterProc(p7dcx->dcx); |
126 | 0 | } |
127 | 0 | } else if (NSS_CMSType_IsWrapper(p7dcx->type)) { |
128 | 0 | if (!before || dest != &(rootcinfo->content)) { |
129 | 0 |
|
130 | 0 | if (p7dcx->content.pointer == NULL) |
131 | 0 | p7dcx->content = rootcinfo->content; |
132 | 0 |
|
133 | 0 | /* get this data type's inner contentInfo */ |
134 | 0 | cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, |
135 | 0 | p7dcx->type); |
136 | 0 |
|
137 | 0 | if (before && dest == &(cinfo->contentType)) { |
138 | 0 | /* at this point, set up the &%$&$ back pointer */ |
139 | 0 | /* we cannot do it later, because the content itself |
140 | 0 | * is optional! */ |
141 | 0 | switch (p7dcx->type) { |
142 | 0 | case SEC_OID_PKCS7_SIGNED_DATA: |
143 | 0 | p7dcx->content.signedData->cmsg = p7dcx->cmsg; |
144 | 0 | break; |
145 | 0 | case SEC_OID_PKCS7_DIGESTED_DATA: |
146 | 0 | p7dcx->content.digestedData->cmsg = p7dcx->cmsg; |
147 | 0 | break; |
148 | 0 | case SEC_OID_PKCS7_ENVELOPED_DATA: |
149 | 0 | p7dcx->content.envelopedData->cmsg = p7dcx->cmsg; |
150 | 0 | break; |
151 | 0 | case SEC_OID_PKCS7_ENCRYPTED_DATA: |
152 | 0 | p7dcx->content.encryptedData->cmsg = p7dcx->cmsg; |
153 | 0 | break; |
154 | 0 | default: |
155 | 0 | p7dcx->content.genericData->cmsg = p7dcx->cmsg; |
156 | 0 | break; |
157 | 0 | } |
158 | 0 | } |
159 | 0 | |
160 | 0 | if (before && dest == &(cinfo->rawContent)) { |
161 | 0 | /* we want the ASN.1 decoder to deliver the decoded bytes to us |
162 | 0 | ** from now on |
163 | 0 | */ |
164 | 0 | SEC_ASN1DecoderSetFilterProc(p7dcx->dcx, |
165 | 0 | nss_cms_decoder_update_filter, |
166 | 0 | p7dcx, (PRBool)(p7dcx->cb != NULL)); |
167 | 0 |
|
168 | 0 | /* we're right in front of the data */ |
169 | 0 | if (nss_cms_before_data(p7dcx) != SECSuccess) { |
170 | 0 | SEC_ASN1DecoderClearFilterProc(p7dcx->dcx); |
171 | 0 | /* stop all processing */ |
172 | 0 | p7dcx->error = PORT_GetError(); |
173 | 0 | } |
174 | 0 | } |
175 | 0 | if (after && dest == &(cinfo->rawContent)) { |
176 | 0 | /* we're right after of the data */ |
177 | 0 | if (nss_cms_after_data(p7dcx) != SECSuccess) |
178 | 0 | p7dcx->error = PORT_GetError(); |
179 | 0 |
|
180 | 0 | /* we don't need to see the contents anymore */ |
181 | 0 | SEC_ASN1DecoderClearFilterProc(p7dcx->dcx); |
182 | 0 | } |
183 | 0 | } |
184 | 0 | } else { |
185 | 0 | /* unsupported or unknown message type - fail gracefully */ |
186 | 0 | p7dcx->error = SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE; |
187 | 0 | } |
188 | 0 | } |
189 | | |
190 | | /* |
191 | | * nss_cms_before_data - set up the current encoder to receive data |
192 | | */ |
193 | | static SECStatus |
194 | | nss_cms_before_data(NSSCMSDecoderContext *p7dcx) |
195 | 0 | { |
196 | 0 | SECStatus rv; |
197 | 0 | SECOidTag childtype; |
198 | 0 | PLArenaPool *poolp; |
199 | 0 | NSSCMSDecoderContext *childp7dcx; |
200 | 0 | NSSCMSContentInfo *cinfo; |
201 | 0 | const SEC_ASN1Template *template; |
202 | 0 | void *mark = NULL; |
203 | 0 | size_t size; |
204 | 0 |
|
205 | 0 | poolp = p7dcx->cmsg->poolp; |
206 | 0 |
|
207 | 0 | /* call _Decode_BeforeData handlers */ |
208 | 0 | switch (p7dcx->type) { |
209 | 0 | case SEC_OID_PKCS7_SIGNED_DATA: |
210 | 0 | /* we're decoding a signedData, so set up the digests */ |
211 | 0 | rv = NSS_CMSSignedData_Decode_BeforeData(p7dcx->content.signedData); |
212 | 0 | break; |
213 | 0 | case SEC_OID_PKCS7_DIGESTED_DATA: |
214 | 0 | /* we're encoding a digestedData, so set up the digest */ |
215 | 0 | rv = NSS_CMSDigestedData_Decode_BeforeData(p7dcx->content.digestedData); |
216 | 0 | break; |
217 | 0 | case SEC_OID_PKCS7_ENVELOPED_DATA: |
218 | 0 | rv = NSS_CMSEnvelopedData_Decode_BeforeData( |
219 | 0 | p7dcx->content.envelopedData); |
220 | 0 | break; |
221 | 0 | case SEC_OID_PKCS7_ENCRYPTED_DATA: |
222 | 0 | rv = NSS_CMSEncryptedData_Decode_BeforeData( |
223 | 0 | p7dcx->content.encryptedData); |
224 | 0 | break; |
225 | 0 | default: |
226 | 0 | rv = NSS_CMSGenericWrapperData_Decode_BeforeData(p7dcx->type, |
227 | 0 | p7dcx->content.genericData); |
228 | 0 | } |
229 | 0 | if (rv != SECSuccess) |
230 | 0 | return SECFailure; |
231 | 0 | |
232 | 0 | /* ok, now we have a pointer to cinfo */ |
233 | 0 | /* find out what kind of data is encapsulated */ |
234 | 0 | |
235 | 0 | cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type); |
236 | 0 | childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo); |
237 | 0 |
|
238 | 0 | if (NSS_CMSType_IsData(childtype)) { |
239 | 0 | cinfo->content.pointer = (void *)nss_cms_create_decoder_data(poolp); |
240 | 0 | if (cinfo->content.pointer == NULL) |
241 | 0 | /* set memory error */ |
242 | 0 | return SECFailure; |
243 | 0 | |
244 | 0 | p7dcx->childp7dcx = NULL; |
245 | 0 | return SECSuccess; |
246 | 0 | } |
247 | 0 | |
248 | 0 | /* set up inner decoder */ |
249 | 0 | |
250 | 0 | if ((template = NSS_CMSUtil_GetTemplateByTypeTag(childtype)) == NULL) |
251 | 0 | return SECFailure; |
252 | 0 | |
253 | 0 | childp7dcx = PORT_ZNew(NSSCMSDecoderContext); |
254 | 0 | if (childp7dcx == NULL) |
255 | 0 | return SECFailure; |
256 | 0 | |
257 | 0 | mark = PORT_ArenaMark(poolp); |
258 | 0 |
|
259 | 0 | /* allocate space for the stuff we're creating */ |
260 | 0 | size = NSS_CMSUtil_GetSizeByTypeTag(childtype); |
261 | 0 | childp7dcx->content.pointer = (void *)PORT_ArenaZAlloc(poolp, size); |
262 | 0 | if (childp7dcx->content.pointer == NULL) |
263 | 0 | goto loser; |
264 | 0 | |
265 | 0 | /* give the parent a copy of the pointer so that it doesn't get lost */ |
266 | 0 | cinfo->content.pointer = childp7dcx->content.pointer; |
267 | 0 |
|
268 | 0 | /* start the child decoder */ |
269 | 0 | childp7dcx->dcx = SEC_ASN1DecoderStart(poolp, childp7dcx->content.pointer, |
270 | 0 | template); |
271 | 0 | if (childp7dcx->dcx == NULL) |
272 | 0 | goto loser; |
273 | 0 | |
274 | 0 | /* the new decoder needs to notify, too */ |
275 | 0 | SEC_ASN1DecoderSetNotifyProc(childp7dcx->dcx, nss_cms_decoder_notify, |
276 | 0 | childp7dcx); |
277 | 0 |
|
278 | 0 | /* tell the parent decoder that it needs to feed us the content data */ |
279 | 0 | p7dcx->childp7dcx = childp7dcx; |
280 | 0 |
|
281 | 0 | childp7dcx->type = childtype; /* our type */ |
282 | 0 |
|
283 | 0 | childp7dcx->cmsg = p7dcx->cmsg; /* backpointer to root message */ |
284 | 0 |
|
285 | 0 | /* should the child decoder encounter real data, |
286 | 0 | ** it must give it to the caller |
287 | 0 | */ |
288 | 0 | childp7dcx->cb = p7dcx->cb; |
289 | 0 | childp7dcx->cb_arg = p7dcx->cb_arg; |
290 | 0 | childp7dcx->first_decoded = PR_FALSE; |
291 | 0 | childp7dcx->need_indefinite_finish = PR_FALSE; |
292 | 0 | if (childtype == SEC_OID_PKCS7_SIGNED_DATA) { |
293 | 0 | childp7dcx->first_decoded = PR_TRUE; |
294 | 0 | } |
295 | 0 |
|
296 | 0 | /* now set up the parent to hand decoded data to the next level decoder */ |
297 | 0 | p7dcx->cb = (NSSCMSContentCallback)NSS_CMSDecoder_Update; |
298 | 0 | p7dcx->cb_arg = childp7dcx; |
299 | 0 |
|
300 | 0 | PORT_ArenaUnmark(poolp, mark); |
301 | 0 |
|
302 | 0 | return SECSuccess; |
303 | 0 |
|
304 | 0 | loser: |
305 | 0 | if (mark) |
306 | 0 | PORT_ArenaRelease(poolp, mark); |
307 | 0 | PORT_Free(childp7dcx); |
308 | 0 | p7dcx->childp7dcx = NULL; |
309 | 0 | return SECFailure; |
310 | 0 | } |
311 | | |
312 | | static SECStatus |
313 | | nss_cms_after_data(NSSCMSDecoderContext *p7dcx) |
314 | 0 | { |
315 | 0 | NSSCMSDecoderContext *childp7dcx; |
316 | 0 | SECStatus rv = SECFailure; |
317 | 0 |
|
318 | 0 | /* Handle last block. This is necessary to flush out the last bytes |
319 | 0 | * of a possibly incomplete block */ |
320 | 0 | nss_cms_decoder_work_data(p7dcx, NULL, 0, PR_TRUE); |
321 | 0 |
|
322 | 0 | /* finish any "inner" decoders - there's no more data coming... */ |
323 | 0 | if (p7dcx->childp7dcx != NULL) { |
324 | 0 | childp7dcx = p7dcx->childp7dcx; |
325 | 0 | if (childp7dcx->dcx != NULL) { |
326 | 0 | /* we started and indefinite sequence somewhere, not complete it */ |
327 | 0 | if (childp7dcx->need_indefinite_finish) { |
328 | 0 | static const char lbuf[2] = { 0, 0 }; |
329 | 0 | NSS_CMSDecoder_Update(childp7dcx, lbuf, sizeof(lbuf)); |
330 | 0 | childp7dcx->need_indefinite_finish = PR_FALSE; |
331 | 0 | } |
332 | 0 |
|
333 | 0 | if (SEC_ASN1DecoderFinish(childp7dcx->dcx) != SECSuccess) { |
334 | 0 | /* do what? free content? */ |
335 | 0 | rv = SECFailure; |
336 | 0 | } else { |
337 | 0 | rv = nss_cms_after_end(childp7dcx); |
338 | 0 | } |
339 | 0 | if (rv != SECSuccess) |
340 | 0 | goto done; |
341 | 0 | } |
342 | 0 | PORT_Free(p7dcx->childp7dcx); |
343 | 0 | p7dcx->childp7dcx = NULL; |
344 | 0 | } |
345 | 0 |
|
346 | 0 | switch (p7dcx->type) { |
347 | 0 | case SEC_OID_PKCS7_SIGNED_DATA: |
348 | 0 | /* this will finish the digests and verify */ |
349 | 0 | rv = NSS_CMSSignedData_Decode_AfterData(p7dcx->content.signedData); |
350 | 0 | break; |
351 | 0 | case SEC_OID_PKCS7_ENVELOPED_DATA: |
352 | 0 | rv = NSS_CMSEnvelopedData_Decode_AfterData( |
353 | 0 | p7dcx->content.envelopedData); |
354 | 0 | break; |
355 | 0 | case SEC_OID_PKCS7_DIGESTED_DATA: |
356 | 0 | rv = NSS_CMSDigestedData_Decode_AfterData( |
357 | 0 | p7dcx->content.digestedData); |
358 | 0 | break; |
359 | 0 | case SEC_OID_PKCS7_ENCRYPTED_DATA: |
360 | 0 | rv = NSS_CMSEncryptedData_Decode_AfterData( |
361 | 0 | p7dcx->content.encryptedData); |
362 | 0 | break; |
363 | 0 | case SEC_OID_PKCS7_DATA: |
364 | 0 | /* do nothing */ |
365 | 0 | break; |
366 | 0 | default: |
367 | 0 | rv = NSS_CMSGenericWrapperData_Decode_AfterData(p7dcx->type, |
368 | 0 | p7dcx->content.genericData); |
369 | 0 | break; |
370 | 0 | } |
371 | 0 | done: |
372 | 0 | return rv; |
373 | 0 | } |
374 | | |
375 | | static SECStatus |
376 | | nss_cms_after_end(NSSCMSDecoderContext *p7dcx) |
377 | 0 | { |
378 | 0 | SECStatus rv = SECSuccess; |
379 | 0 |
|
380 | 0 | switch (p7dcx->type) { |
381 | 0 | case SEC_OID_PKCS7_SIGNED_DATA: |
382 | 0 | if (p7dcx->content.signedData) |
383 | 0 | rv = NSS_CMSSignedData_Decode_AfterEnd(p7dcx->content.signedData); |
384 | 0 | break; |
385 | 0 | case SEC_OID_PKCS7_ENVELOPED_DATA: |
386 | 0 | if (p7dcx->content.envelopedData) |
387 | 0 | rv = NSS_CMSEnvelopedData_Decode_AfterEnd( |
388 | 0 | p7dcx->content.envelopedData); |
389 | 0 | break; |
390 | 0 | case SEC_OID_PKCS7_DIGESTED_DATA: |
391 | 0 | if (p7dcx->content.digestedData) |
392 | 0 | rv = NSS_CMSDigestedData_Decode_AfterEnd( |
393 | 0 | p7dcx->content.digestedData); |
394 | 0 | break; |
395 | 0 | case SEC_OID_PKCS7_ENCRYPTED_DATA: |
396 | 0 | if (p7dcx->content.encryptedData) |
397 | 0 | rv = NSS_CMSEncryptedData_Decode_AfterEnd( |
398 | 0 | p7dcx->content.encryptedData); |
399 | 0 | break; |
400 | 0 | case SEC_OID_PKCS7_DATA: |
401 | 0 | break; |
402 | 0 | default: |
403 | 0 | rv = NSS_CMSGenericWrapperData_Decode_AfterEnd(p7dcx->type, |
404 | 0 | p7dcx->content.genericData); |
405 | 0 | break; |
406 | 0 | } |
407 | 0 | return rv; |
408 | 0 | } |
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(NSSCMSDecoderContext *p7dcx, |
418 | | const unsigned char *data, unsigned long len, |
419 | | PRBool final) |
420 | 0 | { |
421 | 0 | NSSCMSContentInfo *cinfo; |
422 | 0 | unsigned char *buf = NULL; |
423 | 0 | unsigned char *dest; |
424 | 0 | unsigned int offset; |
425 | 0 | SECStatus rv; |
426 | 0 |
|
427 | 0 | /* |
428 | 0 | * We should really have data to process, or we should be trying |
429 | 0 | * to finish/flush the last block. (This is an overly paranoid |
430 | 0 | * check since all callers are in this file and simple inspection |
431 | 0 | * proves they do it right. But it could find a bug in future |
432 | 0 | * modifications/development, that is why it is here.) |
433 | 0 | */ |
434 | 0 | PORT_Assert((data != NULL && len) || final); |
435 | 0 |
|
436 | 0 | cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type); |
437 | 0 | if (!cinfo) { |
438 | 0 | /* The original programmer didn't expect this to happen */ |
439 | 0 | p7dcx->error = SEC_ERROR_LIBRARY_FAILURE; |
440 | 0 | goto loser; |
441 | 0 | } |
442 | 0 | |
443 | 0 | if (cinfo->privateInfo && cinfo->privateInfo->ciphcx != NULL) { |
444 | 0 | /* |
445 | 0 | * we are decrypting. |
446 | 0 | * |
447 | 0 | * XXX If we get an error, we do not want to do the digest or callback, |
448 | 0 | * but we want to keep decoding. Or maybe we want to stop decoding |
449 | 0 | * altogether if there is a callback, because obviously we are not |
450 | 0 | * sending the data back and they want to know that. |
451 | 0 | */ |
452 | 0 |
|
453 | 0 | unsigned int outlen = 0; /* length of decrypted data */ |
454 | 0 | unsigned int buflen; /* length available for decrypted data */ |
455 | 0 |
|
456 | 0 | /* find out about the length of decrypted data */ |
457 | 0 | buflen = NSS_CMSCipherContext_DecryptLength(cinfo->privateInfo->ciphcx, len, final); |
458 | 0 |
|
459 | 0 | /* |
460 | 0 | * it might happen that we did not provide enough data for a full |
461 | 0 | * block (decryption unit), and that there is no output available |
462 | 0 | */ |
463 | 0 |
|
464 | 0 | /* no output available, AND no input? */ |
465 | 0 | if (buflen == 0 && len == 0) |
466 | 0 | goto loser; /* bail out */ |
467 | 0 | |
468 | 0 | /* |
469 | 0 | * have inner decoder: pass the data on (means inner content type is NOT data) |
470 | 0 | * no inner decoder: we have DATA in here: either call callback or store |
471 | 0 | */ |
472 | 0 | if (buflen != 0) { |
473 | 0 | /* there will be some output - need to make room for it */ |
474 | 0 | /* allocate buffer from the heap */ |
475 | 0 | buf = (unsigned char *)PORT_Alloc(buflen); |
476 | 0 | if (buf == NULL) { |
477 | 0 | p7dcx->error = SEC_ERROR_NO_MEMORY; |
478 | 0 | goto loser; |
479 | 0 | } |
480 | 0 | } |
481 | 0 | |
482 | 0 | /* |
483 | 0 | * decrypt incoming data |
484 | 0 | * buf can still be NULL here (and buflen == 0) here if we don't expect |
485 | 0 | * any output (see above), but we still need to call NSS_CMSCipherContext_Decrypt to |
486 | 0 | * keep track of incoming data |
487 | 0 | */ |
488 | 0 | rv = NSS_CMSCipherContext_Decrypt(cinfo->privateInfo->ciphcx, buf, &outlen, buflen, |
489 | 0 | data, len, final); |
490 | 0 | if (rv != SECSuccess) { |
491 | 0 | p7dcx->error = PORT_GetError(); |
492 | 0 | goto loser; |
493 | 0 | } |
494 | 0 |
|
495 | 0 | PORT_Assert(final || outlen == buflen); |
496 | 0 |
|
497 | 0 | /* swap decrypted data in */ |
498 | 0 | data = buf; |
499 | 0 | len = outlen; |
500 | 0 | } |
501 | 0 |
|
502 | 0 | if (len == 0) |
503 | 0 | goto done; /* nothing more to do */ |
504 | 0 | |
505 | 0 | /* |
506 | 0 | * Update the running digests with plaintext bytes (if we need to). |
507 | 0 | */ |
508 | 0 | if (cinfo->privateInfo && cinfo->privateInfo->digcx) |
509 | 0 | NSS_CMSDigestContext_Update(cinfo->privateInfo->digcx, data, len); |
510 | 0 |
|
511 | 0 | /* at this point, we have the plain decoded & decrypted data |
512 | 0 | ** which is either more encoded DER (which we need to hand to the child |
513 | 0 | ** decoder) or data we need to hand back to our caller |
514 | 0 | */ |
515 | 0 |
|
516 | 0 | /* pass the content back to our caller or */ |
517 | 0 | /* feed our freshly decrypted and decoded data into child decoder */ |
518 | 0 | if (p7dcx->cb != NULL) { |
519 | 0 | (*p7dcx->cb)(p7dcx->cb_arg, (const char *)data, len); |
520 | 0 | } |
521 | 0 | #if 1 |
522 | 0 | else |
523 | 0 | #endif |
524 | 0 | if (NSS_CMSContentInfo_GetContentTypeTag(cinfo) == SEC_OID_PKCS7_DATA) { |
525 | 0 | /* store it in "inner" data item as well */ |
526 | 0 | /* find the DATA item in the encapsulated cinfo and store it there */ |
527 | 0 | NSSCMSDecoderData *decoderData = |
528 | 0 | (NSSCMSDecoderData *)cinfo->content.pointer; |
529 | 0 | SECItem *dataItem = &decoderData->data; |
530 | 0 |
|
531 | 0 | offset = dataItem->len; |
532 | 0 | if (dataItem->len + len > decoderData->totalBufferSize) { |
533 | 0 | int needLen = (dataItem->len + len) * 2; |
534 | 0 | dest = (unsigned char *) |
535 | 0 | PORT_ArenaAlloc(p7dcx->cmsg->poolp, needLen); |
536 | 0 | if (dest == NULL) { |
537 | 0 | p7dcx->error = SEC_ERROR_NO_MEMORY; |
538 | 0 | goto loser; |
539 | 0 | } |
540 | 0 | |
541 | 0 | if (dataItem->len) { |
542 | 0 | PORT_Memcpy(dest, dataItem->data, dataItem->len); |
543 | 0 | } |
544 | 0 | decoderData->totalBufferSize = needLen; |
545 | 0 | dataItem->data = dest; |
546 | 0 | } |
547 | 0 |
|
548 | 0 | /* copy it in */ |
549 | 0 | PORT_Memcpy(dataItem->data + offset, data, len); |
550 | 0 | dataItem->len += len; |
551 | 0 | } |
552 | 0 |
|
553 | 0 | done: |
554 | 0 | loser: |
555 | 0 | if (buf) |
556 | 0 | PORT_Free(buf); |
557 | 0 | } |
558 | | |
559 | | /* |
560 | | * nss_cms_decoder_update_filter - process ASN.1 data |
561 | | * |
562 | | * once we have set up a filter in nss_cms_decoder_notify(), |
563 | | * all data processed by the ASN.1 decoder is also passed through here. |
564 | | * we pass the content bytes (as opposed to length and tag bytes) on to |
565 | | * nss_cms_decoder_work_data(). |
566 | | */ |
567 | | static void |
568 | | nss_cms_decoder_update_filter(void *arg, const char *data, unsigned long len, |
569 | | int depth, SEC_ASN1EncodingPart data_kind) |
570 | 0 | { |
571 | 0 | NSSCMSDecoderContext *p7dcx; |
572 | 0 |
|
573 | 0 | PORT_Assert(len); /* paranoia */ |
574 | 0 | if (len == 0) |
575 | 0 | return; |
576 | 0 | |
577 | 0 | p7dcx = (NSSCMSDecoderContext *)arg; |
578 | 0 |
|
579 | 0 | p7dcx->saw_contents = PR_TRUE; |
580 | 0 |
|
581 | 0 | /* pass on the content bytes only */ |
582 | 0 | if (data_kind == SEC_ASN1_Contents) |
583 | 0 | nss_cms_decoder_work_data(p7dcx, (const unsigned char *)data, len, |
584 | 0 | PR_FALSE); |
585 | 0 | } |
586 | | |
587 | | /* |
588 | | * NSS_CMSDecoder_Start - set up decoding of a DER-encoded CMS message |
589 | | * |
590 | | * "poolp" - pointer to arena for message, or NULL if new pool should be created |
591 | | * "cb", "cb_arg" - callback function and argument for delivery of inner content |
592 | | * "pwfn", pwfn_arg" - callback function for getting token password |
593 | | * "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData |
594 | | */ |
595 | | NSSCMSDecoderContext * |
596 | | NSS_CMSDecoder_Start(PLArenaPool *poolp, |
597 | | NSSCMSContentCallback cb, void *cb_arg, |
598 | | PK11PasswordFunc pwfn, void *pwfn_arg, |
599 | | NSSCMSGetDecryptKeyCallback decrypt_key_cb, |
600 | | void *decrypt_key_cb_arg) |
601 | 0 | { |
602 | 0 | NSSCMSDecoderContext *p7dcx; |
603 | 0 | NSSCMSMessage *cmsg; |
604 | 0 |
|
605 | 0 | cmsg = NSS_CMSMessage_Create(poolp); |
606 | 0 | if (cmsg == NULL) |
607 | 0 | return NULL; |
608 | 0 | |
609 | 0 | NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, |
610 | 0 | decrypt_key_cb_arg, NULL, NULL); |
611 | 0 |
|
612 | 0 | p7dcx = PORT_ZNew(NSSCMSDecoderContext); |
613 | 0 | if (p7dcx == NULL) { |
614 | 0 | NSS_CMSMessage_Destroy(cmsg); |
615 | 0 | return NULL; |
616 | 0 | } |
617 | 0 | |
618 | 0 | p7dcx->dcx = SEC_ASN1DecoderStart(cmsg->poolp, cmsg, NSSCMSMessageTemplate); |
619 | 0 | if (p7dcx->dcx == NULL) { |
620 | 0 | PORT_Free(p7dcx); |
621 | 0 | NSS_CMSMessage_Destroy(cmsg); |
622 | 0 | return NULL; |
623 | 0 | } |
624 | 0 |
|
625 | 0 | SEC_ASN1DecoderSetNotifyProc(p7dcx->dcx, nss_cms_decoder_notify, p7dcx); |
626 | 0 |
|
627 | 0 | p7dcx->cmsg = cmsg; |
628 | 0 | p7dcx->type = SEC_OID_UNKNOWN; |
629 | 0 |
|
630 | 0 | p7dcx->cb = cb; |
631 | 0 | p7dcx->cb_arg = cb_arg; |
632 | 0 | p7dcx->first_decoded = PR_FALSE; |
633 | 0 | p7dcx->need_indefinite_finish = PR_FALSE; |
634 | 0 | return p7dcx; |
635 | 0 | } |
636 | | |
637 | | /* |
638 | | * NSS_CMSDecoder_Update - feed DER-encoded data to decoder |
639 | | */ |
640 | | SECStatus |
641 | | NSS_CMSDecoder_Update(NSSCMSDecoderContext *p7dcx, const char *buf, |
642 | | unsigned long len) |
643 | 0 | { |
644 | 0 | SECStatus rv = SECSuccess; |
645 | 0 | if (p7dcx->dcx != NULL && p7dcx->error == 0) { |
646 | 0 | /* if error is set already, don't bother */ |
647 | 0 | if ((p7dcx->type == SEC_OID_PKCS7_SIGNED_DATA) && (p7dcx->first_decoded == PR_TRUE) && (buf[0] == SEC_ASN1_INTEGER)) { |
648 | 0 | /* Microsoft Windows 2008 left out the Sequence wrapping in some |
649 | 0 | * of their kerberos replies. If we are here, we most likely are |
650 | 0 | * dealing with one of those replies. Supply the Sequence wrap |
651 | 0 | * as indefinite encoding (since we don't know the total length |
652 | 0 | * yet) */ |
653 | 0 | static const char lbuf[2] = |
654 | 0 | { SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED, 0x80 }; |
655 | 0 | rv = SEC_ASN1DecoderUpdate(p7dcx->dcx, lbuf, sizeof(lbuf)); |
656 | 0 | if (rv != SECSuccess) { |
657 | 0 | goto loser; |
658 | 0 | } |
659 | 0 | /* ok, we're going to need the indefinite finish when we are done */ |
660 | 0 | p7dcx->need_indefinite_finish = PR_TRUE; |
661 | 0 | } |
662 | 0 |
|
663 | 0 | rv = SEC_ASN1DecoderUpdate(p7dcx->dcx, buf, len); |
664 | 0 | } |
665 | 0 |
|
666 | 0 | loser: |
667 | 0 | p7dcx->first_decoded = PR_FALSE; |
668 | 0 | if (rv != SECSuccess) { |
669 | 0 | p7dcx->error = PORT_GetError(); |
670 | 0 | PORT_Assert(p7dcx->error); |
671 | 0 | if (p7dcx->error == 0) |
672 | 0 | p7dcx->error = -1; |
673 | 0 | } |
674 | 0 |
|
675 | 0 | if (p7dcx->error == 0) |
676 | 0 | return SECSuccess; |
677 | 0 | |
678 | 0 | /* there has been a problem, let's finish the decoder */ |
679 | 0 | if (p7dcx->dcx != NULL) { |
680 | 0 | (void)SEC_ASN1DecoderFinish(p7dcx->dcx); |
681 | 0 | p7dcx->dcx = NULL; |
682 | 0 | } |
683 | 0 | PORT_SetError(p7dcx->error); |
684 | 0 |
|
685 | 0 | return SECFailure; |
686 | 0 | } |
687 | | |
688 | | /* |
689 | | * NSS_CMSDecoder_Cancel - stop decoding in case of error |
690 | | */ |
691 | | void |
692 | | NSS_CMSDecoder_Cancel(NSSCMSDecoderContext *p7dcx) |
693 | 0 | { |
694 | 0 | if (p7dcx->dcx != NULL) |
695 | 0 | (void)SEC_ASN1DecoderFinish(p7dcx->dcx); |
696 | 0 | NSS_CMSMessage_Destroy(p7dcx->cmsg); |
697 | 0 | PORT_Free(p7dcx); |
698 | 0 | } |
699 | | |
700 | | /* |
701 | | * NSS_CMSDecoder_Finish - mark the end of inner content and finish decoding |
702 | | */ |
703 | | NSSCMSMessage * |
704 | | NSS_CMSDecoder_Finish(NSSCMSDecoderContext *p7dcx) |
705 | 0 | { |
706 | 0 | NSSCMSMessage *cmsg; |
707 | 0 |
|
708 | 0 | cmsg = p7dcx->cmsg; |
709 | 0 |
|
710 | 0 | if (p7dcx->dcx == NULL || |
711 | 0 | SEC_ASN1DecoderFinish(p7dcx->dcx) != SECSuccess || |
712 | 0 | nss_cms_after_end(p7dcx) != SECSuccess) { |
713 | 0 | NSS_CMSMessage_Destroy(cmsg); /* get rid of pool if it's ours */ |
714 | 0 | cmsg = NULL; |
715 | 0 | } |
716 | 0 |
|
717 | 0 | PORT_Free(p7dcx); |
718 | 0 | return cmsg; |
719 | 0 | } |
720 | | |
721 | | NSSCMSMessage * |
722 | | NSS_CMSMessage_CreateFromDER(SECItem *DERmessage, |
723 | | NSSCMSContentCallback cb, void *cb_arg, |
724 | | PK11PasswordFunc pwfn, void *pwfn_arg, |
725 | | NSSCMSGetDecryptKeyCallback decrypt_key_cb, |
726 | | void *decrypt_key_cb_arg) |
727 | 0 | { |
728 | 0 | NSSCMSDecoderContext *p7dcx; |
729 | 0 |
|
730 | 0 | /* first arg(poolp) == NULL => create our own pool */ |
731 | 0 | p7dcx = NSS_CMSDecoder_Start(NULL, cb, cb_arg, pwfn, pwfn_arg, |
732 | 0 | decrypt_key_cb, decrypt_key_cb_arg); |
733 | 0 | if (p7dcx == NULL) |
734 | 0 | return NULL; |
735 | 0 | NSS_CMSDecoder_Update(p7dcx, (char *)DERmessage->data, DERmessage->len); |
736 | 0 | return NSS_CMSDecoder_Finish(p7dcx); |
737 | 0 | } |