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