Line data Source code
1 : #include "../burp.h"
2 : #include "../alloc.h"
3 : #include "../asfd.h"
4 : #include "../async.h"
5 : #include "../bfile.h"
6 : #include "../cmd.h"
7 : #include "../handy.h"
8 : #include "../hexmap.h"
9 : #include "../log.h"
10 : #include "handy.h"
11 :
12 0 : static int do_encryption(struct asfd *asfd, EVP_CIPHER_CTX *ctx,
13 : uint8_t *inbuf, int inlen, uint8_t *outbuf, int *outlen,
14 : MD5_CTX *md5)
15 : {
16 0 : if(!inlen) return 0;
17 0 : if(!EVP_CipherUpdate(ctx, outbuf, outlen, inbuf, inlen))
18 : {
19 0 : logp("Encryption failure.\n");
20 0 : return -1;
21 : }
22 0 : if(*outlen>0)
23 : {
24 0 : if(asfd->write_strn(asfd, CMD_APPEND,
25 0 : (char *)outbuf, (size_t)*outlen)) return -1;
26 0 : if(!MD5_Update(md5, outbuf, *outlen))
27 : {
28 0 : logp("MD5_Update() failed\n");
29 0 : return -1;
30 : }
31 : }
32 0 : return 0;
33 : }
34 :
35 0 : EVP_CIPHER_CTX *enc_setup(int encrypt, const char *encryption_password)
36 : {
37 0 : EVP_CIPHER_CTX *ctx=NULL;
38 : // Declare enc_iv with individual characters so that the weird last
39 : // character can be specified as a hex number in order to prevent
40 : // compilation warnings on Macs.
41 0 : uint8_t enc_iv[]={'[', 'l', 'k', 'd', '.', '$', 'G', 0xa3, '\0'};
42 :
43 0 : if(!(ctx=(EVP_CIPHER_CTX *)
44 : calloc_w(1, sizeof(EVP_CIPHER_CTX), __func__)))
45 :
46 : // Don't set key or IV because we will modify the parameters.
47 0 : EVP_CIPHER_CTX_init(ctx);
48 0 : if(!(EVP_CipherInit_ex(ctx, EVP_bf_cbc(), NULL, NULL, NULL, encrypt)))
49 : {
50 0 : logp("EVP_CipherInit_ex failed\n");
51 0 : goto error;
52 : }
53 0 : EVP_CIPHER_CTX_set_key_length(ctx, strlen(encryption_password));
54 : // We finished modifying parameters so now we can set key and IV
55 :
56 0 : if(!EVP_CipherInit_ex(ctx, NULL, NULL,
57 : (uint8_t *)encryption_password,
58 0 : enc_iv, encrypt))
59 : {
60 0 : logp("Second EVP_CipherInit_ex failed\n");
61 0 : goto error;
62 : }
63 0 : return ctx;
64 : error:
65 0 : if(ctx) free(ctx);
66 0 : return NULL;
67 : }
68 :
69 : #ifdef HAVE_WIN32
70 : struct bsid {
71 : int32_t dwStreamId;
72 : int32_t dwStreamAttributes;
73 : int64_t Size;
74 : int32_t dwStreamNameSize;
75 : };
76 : #endif
77 :
78 7298 : char *get_endfile_str(uint64_t bytes, uint8_t *checksum)
79 : {
80 : static char endmsg[128]="";
81 : snprintf(endmsg, sizeof(endmsg), "%"PRIu64 ":%s",
82 : (uint64_t)bytes,
83 7298 : checksum?bytes_to_md5str(checksum):"");
84 7298 : return endmsg;
85 : }
86 :
87 0 : int write_endfile(struct asfd *asfd, uint64_t bytes, uint8_t *checksum)
88 : {
89 : return asfd->write_str(asfd,
90 0 : CMD_END_FILE, get_endfile_str(bytes, checksum));
91 : }
92 :
93 : /* OK, this function is getting a bit out of control.
94 : One problem is that, if you give deflateInit2 compression=0, it still
95 : writes gzip headers and footers, so I had to add extra
96 : if(compression) and if(!compression) bits all over the place that would
97 : skip the actual compression.
98 : This is needed for the case where encryption is on and compression is off.
99 : Encryption off and compression off uses send_whole_file().
100 : Perhaps a separate function is needed for encryption on compression off.
101 : */
102 0 : int send_whole_file_gzl(struct asfd *asfd,
103 : const char *fname, const char *datapth, int quick_read,
104 : uint64_t *bytes, const char *encpassword, struct cntr *cntr,
105 : int compression, BFILE *bfd, const char *extrameta,
106 : size_t elen)
107 : {
108 0 : int ret=0;
109 0 : int zret=0;
110 : MD5_CTX md5;
111 0 : size_t metalen=0;
112 0 : const char *metadata=NULL;
113 :
114 : int have;
115 : z_stream strm;
116 0 : int flush=Z_NO_FLUSH;
117 : uint8_t in[ZCHUNK];
118 : uint8_t out[ZCHUNK];
119 :
120 : int eoutlen;
121 : uint8_t eoutbuf[ZCHUNK+EVP_MAX_BLOCK_LENGTH];
122 :
123 0 : EVP_CIPHER_CTX *enc_ctx=NULL;
124 : #ifdef HAVE_WIN32
125 : int do_known_byte_count=0;
126 : size_t datalen=bfd->datalen;
127 : if(datalen>0) do_known_byte_count=1;
128 : #endif
129 :
130 0 : if(encpassword && !(enc_ctx=enc_setup(1, encpassword)))
131 0 : return -1;
132 :
133 0 : if(!MD5_Init(&md5))
134 : {
135 0 : logp("MD5_Init() failed\n");
136 0 : return -1;
137 : }
138 :
139 : //logp("send_whole_file_gz: %s%s\n", fname, extrameta?" (meta)":"");
140 :
141 0 : if((metadata=extrameta))
142 : {
143 0 : metalen=elen;
144 : }
145 :
146 : /* allocate deflate state */
147 0 : strm.zalloc = Z_NULL;
148 0 : strm.zfree = Z_NULL;
149 0 : strm.opaque = Z_NULL;
150 0 : if((zret=deflateInit2(&strm, compression, Z_DEFLATED, (15+16),
151 : 8, Z_DEFAULT_STRATEGY))!=Z_OK)
152 :
153 : {
154 0 : return -1;
155 : }
156 :
157 0 : do
158 : {
159 0 : if(metadata)
160 : {
161 0 : if(metalen>ZCHUNK)
162 0 : strm.avail_in=ZCHUNK;
163 : else
164 0 : strm.avail_in=metalen;
165 0 : memcpy(in, metadata, strm.avail_in);
166 0 : metadata+=strm.avail_in;
167 0 : metalen-=strm.avail_in;
168 : }
169 : else
170 : {
171 : // Windows VSS headers give us how much data to
172 : // expect to read.
173 : #ifdef HAVE_WIN32
174 : if(do_known_byte_count)
175 : {
176 : if(datalen<=0) strm.avail_in=0;
177 : else strm.avail_in=
178 : (uint32_t)bfd->read(bfd, in,
179 : min((size_t)ZCHUNK, datalen));
180 : datalen-=strm.avail_in;
181 : }
182 : else
183 : #endif
184 : strm.avail_in=
185 0 : (uint32_t)bfd->read(bfd, in, ZCHUNK);
186 : }
187 0 : if(!compression && !strm.avail_in) break;
188 :
189 0 : *bytes+=strm.avail_in;
190 :
191 : // The checksum needs to be later if encryption is being used.
192 0 : if(!enc_ctx)
193 : {
194 0 : if(!MD5_Update(&md5, in, strm.avail_in))
195 : {
196 0 : logp("MD5_Update() failed\n");
197 0 : ret=-1;
198 0 : break;
199 : }
200 : }
201 :
202 : #ifdef HAVE_WIN32
203 : if(do_known_byte_count && datalen<=0) flush=Z_FINISH;
204 : else
205 : #endif
206 0 : if(strm.avail_in) flush=Z_NO_FLUSH;
207 0 : else flush=Z_FINISH;
208 :
209 0 : strm.next_in=in;
210 :
211 : /* run deflate() on input until output buffer not full, finish
212 : compression if all of source has been read in */
213 0 : do
214 : {
215 0 : if(compression)
216 : {
217 0 : strm.avail_out = ZCHUNK;
218 0 : strm.next_out = out;
219 0 : zret = deflate(&strm, flush); /* no bad return value */
220 0 : if(zret==Z_STREAM_ERROR) /* state not clobbered */
221 : {
222 0 : logp("z_stream_error\n");
223 0 : ret=-1;
224 0 : break;
225 : }
226 0 : have = ZCHUNK-strm.avail_out;
227 : }
228 : else
229 : {
230 0 : have=strm.avail_in;
231 0 : memcpy(out, in, have);
232 : }
233 :
234 0 : if(enc_ctx)
235 : {
236 0 : if(do_encryption(asfd, enc_ctx, out, have,
237 0 : eoutbuf, &eoutlen, &md5))
238 : {
239 0 : ret=-1;
240 0 : break;
241 : }
242 : }
243 : else
244 : {
245 0 : if(asfd->write_strn(asfd, CMD_APPEND,
246 0 : (char *)out, (size_t)have))
247 : {
248 0 : ret=-1;
249 0 : break;
250 : }
251 : }
252 0 : if(quick_read && datapth)
253 : {
254 : int qr;
255 0 : if((qr=do_quick_read(asfd, datapth, cntr))<0)
256 : {
257 0 : ret=-1;
258 0 : break;
259 : }
260 0 : if(qr) // client wants to interrupt
261 : {
262 0 : goto cleanup;
263 : }
264 : }
265 0 : if(!compression) break;
266 0 : } while (!strm.avail_out);
267 :
268 0 : if(ret) break;
269 :
270 0 : if(!compression) continue;
271 :
272 0 : if(strm.avail_in) /* all input will be used */
273 : {
274 0 : ret=-1;
275 0 : logp("strm.avail_in=%d\n", strm.avail_in);
276 0 : break;
277 : }
278 : } while(flush!=Z_FINISH);
279 :
280 0 : if(!ret)
281 : {
282 0 : if(compression && zret!=Z_STREAM_END)
283 : {
284 0 : logp("ret OK, but zstream not finished: %d\n", zret);
285 0 : ret=-1;
286 : }
287 0 : else if(enc_ctx)
288 : {
289 0 : if(!EVP_CipherFinal_ex(enc_ctx, eoutbuf, &eoutlen))
290 : {
291 0 : logp("Encryption failure at the end\n");
292 0 : ret=-1;
293 : }
294 0 : else if(eoutlen>0)
295 : {
296 0 : if(asfd->write_strn(asfd, CMD_APPEND,
297 0 : (char *)eoutbuf, (size_t)eoutlen))
298 0 : ret=-1;
299 0 : else if(!MD5_Update(&md5, eoutbuf, eoutlen))
300 : {
301 0 : logp("MD5_Update() failed\n");
302 0 : ret=-1;
303 : }
304 : }
305 : }
306 : }
307 :
308 : cleanup:
309 0 : deflateEnd(&strm);
310 :
311 0 : if(enc_ctx)
312 : {
313 0 : EVP_CIPHER_CTX_cleanup(enc_ctx);
314 0 : free(enc_ctx);
315 : }
316 :
317 0 : if(!ret)
318 : {
319 : uint8_t checksum[MD5_DIGEST_LENGTH];
320 0 : if(!MD5_Final(checksum, &md5))
321 : {
322 0 : logp("MD5_Final() failed\n");
323 0 : return -1;
324 : }
325 :
326 0 : return write_endfile(asfd, *bytes, checksum);
327 : }
328 : //logp("end of send\n");
329 0 : return ret;
330 : }
331 :
332 : #ifdef HAVE_WIN32
333 : struct winbuf
334 : {
335 : MD5_CTX *md5;
336 : int quick_read;
337 : const char *datapth;
338 : struct cntr *cntr;
339 : uint64_t *bytes;
340 : struct asfd *asfd;
341 : };
342 :
343 : static DWORD WINAPI write_efs(PBYTE pbData,
344 : PVOID pvCallbackContext, ULONG ulLength)
345 : {
346 : struct winbuf *mybuf=(struct winbuf *)pvCallbackContext;
347 : (*(mybuf->bytes))+=ulLength;
348 : if(!MD5_Update(mybuf->md5, pbData, ulLength))
349 : {
350 : logp("MD5_Update() failed\n");
351 : return ERROR_FUNCTION_FAILED;
352 : }
353 : if(mybuf->asfd->write_strn(mybuf->asfd,
354 : CMD_APPEND, (const char *)pbData, ulLength))
355 : {
356 : return ERROR_FUNCTION_FAILED;
357 : }
358 : if(mybuf->quick_read)
359 : {
360 : int qr;
361 : if((qr=do_quick_read(mybuf->asfd,
362 : mybuf->datapth, mybuf->cntr))<0)
363 : return ERROR_FUNCTION_FAILED;
364 : if(qr) // client wants to interrupt
365 : return ERROR_FUNCTION_FAILED;
366 : }
367 : return ERROR_SUCCESS;
368 : }
369 : #endif
370 :
371 0 : int send_whole_filel(struct asfd *asfd,
372 : enum cmd cmd, const char *fname, const char *datapth,
373 : int quick_read, uint64_t *bytes, struct cntr *cntr,
374 : BFILE *bfd, const char *extrameta, size_t elen)
375 : {
376 0 : int ret=0;
377 0 : size_t s=0;
378 : MD5_CTX md5;
379 0 : char buf[4096]="";
380 :
381 0 : if(!MD5_Init(&md5))
382 : {
383 0 : logp("MD5_Init() failed\n");
384 0 : return -1;
385 : }
386 :
387 0 : if(extrameta)
388 : {
389 0 : size_t metalen=0;
390 0 : const char *metadata=NULL;
391 :
392 0 : metadata=extrameta;
393 0 : metalen=elen;
394 :
395 : // Send metadata in chunks, rather than all at once.
396 0 : while(metalen>0)
397 : {
398 0 : if(metalen>ZCHUNK) s=ZCHUNK;
399 0 : else s=metalen;
400 :
401 0 : if(!MD5_Update(&md5, metadata, s))
402 : {
403 0 : logp("MD5_Update() failed\n");
404 0 : ret=-1;
405 : }
406 0 : if(asfd->write_strn(asfd, CMD_APPEND, metadata, s))
407 : {
408 0 : ret=-1;
409 : }
410 :
411 0 : metadata+=s;
412 0 : metalen-=s;
413 :
414 0 : *bytes+=s;
415 : }
416 : }
417 : else
418 : {
419 : #ifdef HAVE_WIN32
420 : if(!ret && cmd==CMD_EFS_FILE)
421 : {
422 : struct winbuf mybuf;
423 : mybuf.md5=&md5;
424 : mybuf.quick_read=quick_read;
425 : mybuf.datapth=datapth;
426 : mybuf.cntr=cntr;
427 : mybuf.bytes=bytes;
428 : mybuf.asfd=asfd;
429 : // The EFS read function, ReadEncryptedFileRaw(),
430 : // works in an annoying way. You have to give it a
431 : // function that it calls repeatedly every time the
432 : // read buffer is called.
433 : // So ReadEncryptedFileRaw() will not return until
434 : // it has read the whole file. I have no idea why
435 : // they do not have a plain 'read()' function for it.
436 :
437 : ReadEncryptedFileRaw((PFE_EXPORT_FUNC)write_efs,
438 : &mybuf, bfd->pvContext);
439 : }
440 : #endif
441 :
442 0 : if(!ret && cmd!=CMD_EFS_FILE)
443 : {
444 : #ifdef HAVE_WIN32
445 : int do_known_byte_count=0;
446 : size_t datalen=bfd->datalen;
447 : if(datalen>0) do_known_byte_count=1;
448 : #endif
449 : while(1)
450 : {
451 : #ifdef HAVE_WIN32
452 : if(do_known_byte_count)
453 : {
454 : s=(uint32_t)bfd->read(bfd,
455 : buf, min((size_t)4096, datalen));
456 : datalen-=s;
457 : }
458 : else
459 : {
460 : #endif
461 0 : s=(uint32_t)bfd->read(bfd, buf, 4096);
462 : #ifdef HAVE_WIN32
463 : }
464 : #endif
465 0 : if(s<=0) break;
466 :
467 0 : *bytes+=s;
468 0 : if(!MD5_Update(&md5, buf, s))
469 : {
470 0 : logp("MD5_Update() failed\n");
471 0 : ret=-1;
472 0 : break;
473 : }
474 0 : if(asfd->write_strn(asfd, CMD_APPEND, buf, s))
475 : {
476 0 : ret=-1;
477 0 : break;
478 : }
479 0 : if(quick_read)
480 : {
481 : int qr;
482 0 : if((qr=do_quick_read(asfd, datapth, cntr))<0)
483 : {
484 0 : ret=-1;
485 0 : break;
486 : }
487 0 : if(qr)
488 : {
489 : // client wants to interrupt
490 0 : break;
491 : }
492 : }
493 : #ifdef HAVE_WIN32
494 : // Windows VSS headers tell us how many bytes to
495 : // expect.
496 : if(do_known_byte_count && datalen<=0) break;
497 : #endif
498 0 : }
499 : }
500 : }
501 0 : if(!ret)
502 : {
503 : uint8_t checksum[MD5_DIGEST_LENGTH];
504 0 : if(!MD5_Final(checksum, &md5))
505 : {
506 0 : logp("MD5_Final() failed\n");
507 0 : return -1;
508 : }
509 0 : return write_endfile(asfd, *bytes, checksum);
510 : }
511 0 : return ret;
512 : }
|