LCOV - code coverage report
Current view: top level - src/protocol1 - handy.c (source / functions) Hit Total Coverage
Test: burp-coverage-clean.info Lines: 98 188 52.1 %
Date: 2017-12-01 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 "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             : }

Generated by: LCOV version 1.10