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 : enum send_e 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 : enum send_e ret=SEND_OK;
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 : ssize_t r;
134 :
135 : int eoutlen;
136 : uint8_t eoutbuf[ZCHUNK+EVP_MAX_BLOCK_LENGTH];
137 :
138 2 : EVP_CIPHER_CTX *enc_ctx=NULL;
139 : #ifdef HAVE_WIN32
140 : int do_known_byte_count=0;
141 : size_t datalen=bfd->datalen;
142 : if(datalen>0) do_known_byte_count=1;
143 : #endif
144 :
145 2 : if(encpassword && !(enc_ctx=enc_setup(1, encpassword)))
146 : return SEND_FATAL;
147 :
148 2 : if(!MD5_Init(&md5))
149 : {
150 0 : logp("MD5_Init() failed\n");
151 0 : return SEND_FATAL;
152 : }
153 :
154 : //logp("send_whole_file_gz: %s%s\n", fname, extrameta?" (meta)":"");
155 :
156 2 : if((metadata=extrameta))
157 : {
158 0 : metalen=elen;
159 : }
160 :
161 : /* allocate deflate state */
162 2 : strm.zalloc = Z_NULL;
163 2 : strm.zfree = Z_NULL;
164 2 : strm.opaque = Z_NULL;
165 2 : if((zret=deflateInit2(&strm, compression, Z_DEFLATED, (15+16),
166 : 8, Z_DEFAULT_STRATEGY))!=Z_OK)
167 : return SEND_FATAL;
168 :
169 : do
170 : {
171 4 : if(metadata)
172 : {
173 0 : if(metalen>ZCHUNK)
174 0 : strm.avail_in=ZCHUNK;
175 : else
176 0 : strm.avail_in=metalen;
177 0 : memcpy(in, metadata, strm.avail_in);
178 0 : metadata+=strm.avail_in;
179 0 : metalen-=strm.avail_in;
180 : }
181 : else
182 : {
183 : // Windows VSS headers give us how much data to
184 : // expect to read.
185 : #ifdef HAVE_WIN32
186 : if(do_known_byte_count)
187 : {
188 : if(datalen<=0)
189 : r=0;
190 : else
191 : {
192 : r=bfd->read(bfd, in,
193 : min((size_t)ZCHUNK, datalen));
194 : if(r>0)
195 : datalen-=r;
196 : }
197 : }
198 : else
199 : #endif
200 4 : r=bfd->read(bfd, in, ZCHUNK);
201 :
202 4 : if(r<0)
203 : {
204 0 : logw(asfd, cntr,
205 : "Error when reading %s in %s: %s\n",
206 0 : bfd->path, __func__, strerror(errno));
207 0 : ret=SEND_ERROR;
208 0 : break;
209 : }
210 4 : strm.avail_in=(uint32_t)r;
211 : }
212 4 : if(!compression && !strm.avail_in)
213 : break;
214 :
215 4 : *bytes+=strm.avail_in;
216 :
217 : // The checksum needs to be later if encryption is being used.
218 4 : if(!enc_ctx)
219 : {
220 4 : if(!MD5_Update(&md5, in, strm.avail_in))
221 : {
222 0 : logp("MD5_Update() failed\n");
223 0 : ret=SEND_FATAL;
224 0 : break;
225 : }
226 : }
227 :
228 : #ifdef HAVE_WIN32
229 : if(do_known_byte_count && datalen<=0)
230 : flush=Z_FINISH;
231 : else
232 : #endif
233 4 : if(strm.avail_in) flush=Z_NO_FLUSH;
234 2 : else flush=Z_FINISH;
235 :
236 4 : strm.next_in=in;
237 :
238 : /* run deflate() on input until output buffer not full, finish
239 : compression if all of source has been read in */
240 : do
241 : {
242 4 : if(compression)
243 : {
244 4 : strm.avail_out = ZCHUNK;
245 4 : strm.next_out = out;
246 4 : zret = deflate(&strm, flush); /* no bad return value */
247 4 : if(zret==Z_STREAM_ERROR) /* state not clobbered */
248 : {
249 0 : logw(asfd, cntr, "z_stream_error when reading %s in %s\n", bfd->path, __func__);
250 0 : ret=SEND_ERROR;
251 0 : break;
252 : }
253 4 : have = ZCHUNK-strm.avail_out;
254 : }
255 : else
256 : {
257 0 : have=strm.avail_in;
258 0 : memcpy(out, in, have);
259 : }
260 :
261 4 : if(enc_ctx)
262 : {
263 0 : if(do_encryption(asfd, enc_ctx, out, have,
264 : eoutbuf, &eoutlen, &md5))
265 : {
266 : ret=SEND_FATAL;
267 : break;
268 : }
269 : }
270 : else
271 : {
272 4 : iobuf_set(&wbuf, CMD_APPEND, (char *)out, have);
273 4 : if(asfd->write(asfd, &wbuf))
274 : {
275 : ret=SEND_FATAL;
276 : break;
277 : }
278 : }
279 4 : if(quick_read && datapth)
280 : {
281 : int qr;
282 4 : if((qr=do_quick_read(asfd, datapth, cntr))<0)
283 : {
284 : ret=SEND_FATAL;
285 : break;
286 : }
287 4 : if(qr) // client wants to interrupt
288 : {
289 : goto cleanup;
290 : }
291 : }
292 4 : if(!compression) break;
293 4 : } while (!strm.avail_out);
294 :
295 4 : if(ret!=SEND_OK) break;
296 :
297 4 : if(!compression) continue;
298 :
299 4 : if(strm.avail_in) /* all input will be used */
300 : {
301 0 : ret=SEND_FATAL;
302 0 : logp("strm.avail_in=%d\n", strm.avail_in);
303 0 : break;
304 : }
305 4 : } while(flush!=Z_FINISH);
306 :
307 2 : if(ret==SEND_OK)
308 : {
309 2 : if(compression && zret!=Z_STREAM_END)
310 : {
311 0 : logp("ret OK, but zstream not finished: %d\n", zret);
312 0 : ret=SEND_FATAL;
313 : }
314 2 : else if(enc_ctx)
315 : {
316 0 : if(!EVP_CipherFinal_ex(enc_ctx, eoutbuf, &eoutlen))
317 : {
318 0 : logp("Encryption failure at the end\n");
319 0 : ret=SEND_FATAL;
320 : }
321 0 : else if(eoutlen>0)
322 : {
323 0 : iobuf_set(&wbuf, CMD_APPEND,
324 : (char *)eoutbuf, (size_t)eoutlen);
325 0 : if(asfd->write(asfd, &wbuf))
326 : ret=SEND_FATAL;
327 0 : else if(!MD5_Update(&md5, eoutbuf, eoutlen))
328 : {
329 0 : logp("MD5_Update() failed\n");
330 0 : ret=SEND_FATAL;
331 : }
332 : }
333 : }
334 : }
335 :
336 : cleanup:
337 2 : deflateEnd(&strm);
338 :
339 2 : if(enc_ctx)
340 : {
341 0 : EVP_CIPHER_CTX_cleanup(enc_ctx);
342 0 : EVP_CIPHER_CTX_free(enc_ctx);
343 0 : enc_ctx=NULL;
344 : }
345 :
346 2 : if(ret!=SEND_FATAL)
347 : {
348 : uint8_t checksum[MD5_DIGEST_LENGTH];
349 2 : if(!MD5_Final(checksum, &md5))
350 : {
351 0 : logp("MD5_Final() failed\n");
352 0 : return SEND_FATAL;
353 : }
354 2 : if(write_endfile(asfd, *bytes, checksum))
355 : return SEND_FATAL;
356 : }
357 2 : return ret;
358 : }
359 :
360 : #ifdef HAVE_WIN32
361 : struct winbuf
362 : {
363 : MD5_CTX *md5;
364 : int quick_read;
365 : const char *datapth;
366 : struct cntr *cntr;
367 : uint64_t *bytes;
368 : struct asfd *asfd;
369 : };
370 :
371 : static DWORD WINAPI write_efs(PBYTE pbData,
372 : PVOID pvCallbackContext, ULONG ulLength)
373 : {
374 : struct iobuf wbuf;
375 : struct winbuf *mybuf=(struct winbuf *)pvCallbackContext;
376 : (*(mybuf->bytes))+=ulLength;
377 : if(!MD5_Update(mybuf->md5, pbData, ulLength))
378 : {
379 : logp("MD5_Update() failed\n");
380 : return ERROR_FUNCTION_FAILED;
381 : }
382 : iobuf_set(&wbuf, CMD_APPEND, (char *)pbData, (size_t)ulLength);
383 : if(mybuf->asfd->write(mybuf->asfd, &wbuf))
384 : {
385 : return ERROR_FUNCTION_FAILED;
386 : }
387 : if(mybuf->quick_read)
388 : {
389 : int qr;
390 : if((qr=do_quick_read(mybuf->asfd,
391 : mybuf->datapth, mybuf->cntr))<0)
392 : return ERROR_FUNCTION_FAILED;
393 : if(qr) // client wants to interrupt
394 : return ERROR_FUNCTION_FAILED;
395 : }
396 : return ERROR_SUCCESS;
397 : }
398 : #endif
399 :
400 9 : enum send_e send_whole_filel(struct asfd *asfd,
401 : #ifdef HAVE_WIN32
402 : enum cmd cmd,
403 : #endif
404 : const char *datapth,
405 : int quick_read, uint64_t *bytes, struct cntr *cntr,
406 : struct BFILE *bfd, const char *extrameta, size_t elen)
407 : {
408 9 : enum send_e ret=SEND_OK;
409 9 : ssize_t s=0;
410 : MD5_CTX md5;
411 9 : char buf[4096]="";
412 : struct iobuf wbuf;
413 :
414 9 : if(!bfd)
415 : {
416 1 : logp("No bfd in %s()\n", __func__);
417 1 : return SEND_FATAL;
418 : }
419 :
420 8 : if(!MD5_Init(&md5))
421 : {
422 0 : logp("MD5_Init() failed\n");
423 0 : return SEND_FATAL;
424 : }
425 :
426 8 : if(extrameta)
427 : {
428 : size_t metalen=0;
429 : const char *metadata=NULL;
430 :
431 : metadata=extrameta;
432 : metalen=elen;
433 :
434 : // Send metadata in chunks, rather than all at once.
435 0 : while(metalen>0)
436 : {
437 0 : if(metalen>ZCHUNK) s=ZCHUNK;
438 0 : else s=metalen;
439 :
440 0 : if(!MD5_Update(&md5, metadata, s))
441 : {
442 0 : logp("MD5_Update() failed\n");
443 0 : ret=SEND_FATAL;
444 : }
445 0 : iobuf_set(&wbuf, CMD_APPEND, (char *)metadata, s);
446 0 : if(asfd->write(asfd, &wbuf))
447 0 : ret=SEND_FATAL;
448 :
449 0 : metadata+=s;
450 0 : metalen-=s;
451 :
452 0 : *bytes+=s;
453 : }
454 : }
455 : else
456 : {
457 : #ifdef HAVE_WIN32
458 : if(ret==SEND_OK && cmd==CMD_EFS_FILE)
459 : {
460 : struct winbuf mybuf;
461 : mybuf.md5=&md5;
462 : mybuf.quick_read=quick_read;
463 : mybuf.datapth=datapth;
464 : mybuf.cntr=cntr;
465 : mybuf.bytes=bytes;
466 : mybuf.asfd=asfd;
467 : // The EFS read function, ReadEncryptedFileRaw(),
468 : // works in an annoying way. You have to give it a
469 : // function that it calls repeatedly every time the
470 : // read buffer is called.
471 : // So ReadEncryptedFileRaw() will not return until
472 : // it has read the whole file. I have no idea why
473 : // they do not have a plain 'read()' function for it.
474 :
475 : ReadEncryptedFileRaw((PFE_EXPORT_FUNC)write_efs,
476 : &mybuf, bfd->pvContext);
477 : }
478 : else
479 : #endif
480 :
481 : if(ret==SEND_OK)
482 : {
483 : #ifdef HAVE_WIN32
484 : int do_known_byte_count=0;
485 : size_t datalen=bfd->datalen;
486 : if(datalen>0) do_known_byte_count=1;
487 : #endif
488 : while(1)
489 : {
490 : #ifdef HAVE_WIN32
491 : if(do_known_byte_count)
492 : {
493 : s=bfd->read(bfd,
494 : buf, min((size_t)4096, datalen));
495 : if(s>0)
496 : datalen-=s;
497 : }
498 : else
499 : {
500 : #endif
501 10 : s=bfd->read(bfd, buf, 4096);
502 : #ifdef HAVE_WIN32
503 : }
504 : #endif
505 10 : if(!s)
506 : break;
507 2 : else if(s<0)
508 : {
509 0 : logw(asfd, cntr,
510 : "Error when reading %s in %s: %s\n",
511 0 : bfd->path, __func__, strerror(errno));
512 0 : ret=SEND_ERROR;
513 0 : break;
514 : }
515 :
516 2 : *bytes+=s;
517 2 : if(!MD5_Update(&md5, buf, s))
518 : {
519 0 : logp("MD5_Update() failed\n");
520 0 : ret=SEND_FATAL;
521 0 : break;
522 : }
523 2 : iobuf_set(&wbuf, CMD_APPEND, buf, s);
524 2 : if(asfd->write(asfd, &wbuf))
525 : {
526 : ret=SEND_FATAL;
527 : break;
528 : }
529 2 : if(quick_read)
530 : {
531 : int qr;
532 2 : if((qr=do_quick_read(asfd, datapth, cntr))<0)
533 : {
534 : ret=SEND_FATAL;
535 : break;
536 : }
537 2 : if(qr)
538 : {
539 : // client wants to interrupt
540 : break;
541 : }
542 : }
543 : #ifdef HAVE_WIN32
544 : // Windows VSS headers tell us how many bytes to
545 : // expect.
546 : if(do_known_byte_count && datalen<=0) break;
547 : #endif
548 : }
549 : }
550 : }
551 8 : if(ret!=SEND_FATAL)
552 : {
553 : uint8_t checksum[MD5_DIGEST_LENGTH];
554 8 : if(!MD5_Final(checksum, &md5))
555 : {
556 0 : logp("MD5_Final() failed\n");
557 0 : return SEND_FATAL;
558 : }
559 8 : if(write_endfile(asfd, *bytes, checksum))
560 : return SEND_FATAL;
561 : }
562 8 : return ret;
563 : }
|