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