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