LCOV - code coverage report
Current view: top level - src - handy_extra.c (source / functions) Hit Total Coverage
Test: burp-coverage-clean.info Lines: 109 211 51.7 %
Date: 2022-12-03 01:09:05 Functions: 5 6 83.3 %

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

Generated by: LCOV version 1.13