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