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