libclamav/7z_iface.c
0e605501
 /*
e1cbc270
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2011-2013 Sourcefire, Inc.
0e605501
  *
  *  Authors: aCaB
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
  */
 
c7ad4bea
 #if defined(_WIN32)
 #include <WinSock2.h>
 #include <Windows.h>
 #endif
0e605501
 
60d8d2c3
 #include "clamav.h"
0e605501
 #include "7z_iface.h"
 #include "lzma_iface.h"
 #include "scanners.h"
 #include "others.h"
 #include "fmap.h"
 
 #include "7z/7z.h"
 #include "7z/7zAlloc.h"
 #include "7z/7zFile.h"
 
288057e9
 static ISzAlloc allocImp = {__lzma_wrap_alloc, __lzma_wrap_free}, allocTempImp = {__lzma_wrap_alloc, __lzma_wrap_free};
0e605501
 
288057e9
 static SRes FileInStream_fmap_Read(void *pp, void *buf, size_t *size)
 {
0e605501
     CFileInStream *p = (CFileInStream *)pp;
6c03dc5d
     size_t read_sz;
0e605501
 
     if (*size == 0)
288057e9
         return 0;
0e605501
 
     read_sz = fmap_readn(p->file.fmap, buf, p->s.curpos, *size);
6c03dc5d
     if (read_sz == (size_t)-1) {
288057e9
         *size = 0;
         return SZ_ERROR_READ;
afa9976c
     }
0e605501
 
     p->s.curpos += read_sz;
 
     *size = read_sz;
     return SZ_OK;
 }
 
288057e9
 static SRes FileInStream_fmap_Seek(void *pp, Int64 *pos, ESzSeek origin)
 {
0e605501
     CFileInStream *p = (CFileInStream *)pp;
 
     switch (origin) {
288057e9
         case SZ_SEEK_SET:
             p->s.curpos = *pos;
             break;
         case SZ_SEEK_CUR:
             p->s.curpos += *pos;
             *pos = p->s.curpos;
             break;
         case SZ_SEEK_END:
             p->s.curpos = p->file.fmap->len + *pos;
             *pos        = p->s.curpos;
             break;
         default:
             return 1;
0e605501
     }
     return 0;
 }
 
 #define UTFBUFSZ 256
288057e9
 int cli_7unz(cli_ctx *ctx, size_t offset)
 {
0e605501
     CFileInStream archiveStream;
     CLookToRead lookStream;
     CSzArEx db;
     SRes res;
     UInt16 utf16buf[UTFBUFSZ], *utf16name = utf16buf;
50876732
     int namelen            = UTFBUFSZ;
     cl_error_t found       = CL_CLEAN;
9a47aa20
     Int64 begin_of_archive = offset;
288057e9
     UInt32 viruses_found   = 0;
0e605501
 
6c03dc5d
     /* Replacement for
0e605501
        FileInStream_CreateVTable(&archiveStream); */
288057e9
     archiveStream.s.Read    = FileInStream_fmap_Read;
     archiveStream.s.Seek    = FileInStream_fmap_Seek;
     archiveStream.s.curpos  = 0;
0e605501
     archiveStream.file.fmap = *ctx->fmap;
 
     LookToRead_CreateVTable(&lookStream, False);
288057e9
 
     if (archiveStream.s.Seek(&archiveStream.s, &begin_of_archive, SZ_SEEK_SET) != 0)
         return CL_CLEAN;
9a47aa20
 
0e605501
     lookStream.realStream = &archiveStream.s;
     LookToRead_Init(&lookStream);
 
     SzArEx_Init(&db);
     res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);
288057e9
     if (res == SZ_ERROR_ENCRYPTED && SCAN_HEURISTIC_ENCRYPTED_ARCHIVE) {
         cli_dbgmsg("cli_7unz: Encrypted header found in archive.\n");
         found = cli_append_virus(ctx, "Heuristics.Encrypted.7Zip");
     } else if (res == SZ_OK) {
         UInt32 i, blockIndex = 0xFFFFFFFF;
         Byte *outBuffer        = 0;
         size_t outBufferSize   = 0;
         unsigned int encrypted = 0;
 
         for (i = 0; i < db.db.NumFiles; i++) {
             size_t offset           = 0;
             size_t outSizeProcessed = 0;
             const CSzFileItem *f    = db.db.Files + i;
             char *name;
             size_t j;
             int newnamelen, fd;
 
             if ((found = cli_checklimits("7unz", ctx, 0, 0, 0)))
                 break;
 
             if (f->IsDir)
                 continue;
 
             if (cli_checklimits("7unz", ctx, f->Size, 0, 0))
                 continue;
 
             if (!db.FileNameOffsets)
                 newnamelen = 0; /* no filename */
             else {
                 newnamelen = SzArEx_GetFileNameUtf16(&db, i, NULL);
                 if (newnamelen > namelen) {
                     if (namelen > UTFBUFSZ)
                         free(utf16name);
                     utf16name = cli_malloc(newnamelen * 2);
                     if (!utf16name) {
                         found = CL_EMEM;
                         break;
                     }
                     namelen = newnamelen;
                 }
                 SzArEx_GetFileNameUtf16(&db, i, utf16name);
             }
 
             name = (char *)utf16name;
             for (j = 0; j < (size_t)newnamelen; j++) /* FIXME */
                 name[j] = utf16name[j];
             name[j] = 0;
             cli_dbgmsg("cli_7unz: extracting %s\n", name);
 
             res = SzArEx_Extract(&db, &lookStream.s, i, &blockIndex, &outBuffer, &outBufferSize, &offset, &outSizeProcessed, &allocImp, &allocTempImp);
             if (res == SZ_ERROR_ENCRYPTED) {
                 encrypted = 1;
                 if (SCAN_HEURISTIC_ENCRYPTED_ARCHIVE) {
                     cli_dbgmsg("cli_7unz: Encrypted files found in archive.\n");
                     found = cli_append_virus(ctx, "Heuristics.Encrypted.7Zip");
cbf5017a
                     if (found != CL_CLEAN) {
                         if (found == CL_VIRUS) {
048a88e6
                             if (SCAN_ALLMATCHES)
cbf5017a
                                 viruses_found++;
                         } else
                             break;
288057e9
                     }
                 }
             }
             if (cli_matchmeta(ctx, name, 0, f->Size, encrypted, i, f->CrcDefined ? f->Crc : 0, NULL)) {
                 found = CL_VIRUS;
                 viruses_found++;
                 if (!SCAN_ALLMATCHES)
                     break;
             }
             if (res != SZ_OK)
                 cli_dbgmsg("cli_unz: extraction failed with %d\n", res);
50876732
             else if ((outBuffer == NULL) || (outSizeProcessed == 0)) {
                 cli_dbgmsg("cli_unz: extracted empty file\n");
             } else {
288057e9
                 if ((found = cli_gentempfd(ctx->engine->tmpdir, &name, &fd)))
                     break;
 
                 cli_dbgmsg("cli_7unz: Saving to %s\n", name);
6c03dc5d
                 if (cli_writen(fd, outBuffer + offset, outSizeProcessed) != outSizeProcessed)
288057e9
                     found = CL_EWRITE;
                 else if ((found = cli_magic_scandesc(fd, name, ctx)) == CL_VIRUS)
                     viruses_found++;
                 close(fd);
                 if (!ctx->engine->keeptmp && cli_unlink(name))
                     found = CL_EUNLINK;
 
                 free(name);
                 if (found != CL_CLEAN)
                     if (!(SCAN_ALLMATCHES && found == CL_VIRUS))
                         break;
             }
         }
         IAlloc_Free(&allocImp, outBuffer);
0e605501
     }
     SzArEx_Free(&db, &allocImp);
288057e9
     if (namelen > UTFBUFSZ)
         free(utf16name);
0e605501
 
     if (res == SZ_OK)
288057e9
         cli_dbgmsg("cli_7unz: completed successfully\n");
0e605501
     else if (res == SZ_ERROR_UNSUPPORTED)
288057e9
         cli_dbgmsg("cli_7unz: unsupported\n");
0e605501
     else if (res == SZ_ERROR_MEM)
288057e9
         cli_dbgmsg("cli_7unz: oom\n");
0e605501
     else if (res == SZ_ERROR_CRC)
288057e9
         cli_dbgmsg("cli_7unz: crc mismatch\n");
03526dcc
     else if (res == SZ_ERROR_ENCRYPTED)
288057e9
         cli_dbgmsg("cli_7unz: encrypted\n");
0e605501
     else
288057e9
         cli_dbgmsg("cli_7unz: error %d\n", res);
0e605501
 
048a88e6
     if (SCAN_ALLMATCHES && viruses_found)
288057e9
         return CL_VIRUS;
0e605501
     return found;
 }