libclamav/7z_iface.c
0e605501
 /*
c442ca9c
  *  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"
 
 
 static ISzAlloc allocImp = { __lzma_wrap_alloc, __lzma_wrap_free}, allocTempImp = { __lzma_wrap_alloc, __lzma_wrap_free};
 
 static SRes FileInStream_fmap_Read(void *pp, void *buf, size_t *size) {
     CFileInStream *p = (CFileInStream *)pp;
     int read_sz;
 
     if (*size == 0)
 	return 0;
 
     read_sz = fmap_readn(p->file.fmap, buf, p->s.curpos, *size);
afa9976c
     if(read_sz < 0) {
 	*size = 0;
0e605501
 	return SZ_ERROR_READ;
afa9976c
     }
0e605501
 
     p->s.curpos += read_sz;
 
     *size = read_sz;
     return SZ_OK;
 }
 
 static SRes FileInStream_fmap_Seek(void *pp, Int64 *pos, ESzSeek origin) {
     CFileInStream *p = (CFileInStream *)pp;
 
     switch (origin) {
     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;
     }
     return 0;
 }
 
 #define UTFBUFSZ 256
9a47aa20
 int cli_7unz (cli_ctx *ctx, size_t offset) {
0e605501
     CFileInStream archiveStream;
     CLookToRead lookStream;
     CSzArEx db;
     SRes res;
     UInt16 utf16buf[UTFBUFSZ], *utf16name = utf16buf;
     int namelen = UTFBUFSZ, found = CL_CLEAN;
9a47aa20
     Int64 begin_of_archive = offset;
6ad45a29
     UInt32 viruses_found = 0;
0e605501
 
     /* Replacement for 
        FileInStream_CreateVTable(&archiveStream); */
     archiveStream.s.Read = FileInStream_fmap_Read;
     archiveStream.s.Seek = FileInStream_fmap_Seek;
     archiveStream.s.curpos = 0;
     archiveStream.file.fmap = *ctx->fmap;
 
     LookToRead_CreateVTable(&lookStream, False);
   
9a47aa20
     if(archiveStream.s.Seek(&archiveStream.s, &begin_of_archive, SZ_SEEK_SET) != 0)
 	return CL_CLEAN;
 
0e605501
     lookStream.realStream = &archiveStream.s;
     LookToRead_Init(&lookStream);
 
     SzArEx_Init(&db);
     res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);
f61e92da
     if(res == SZ_ERROR_ENCRYPTED && SCAN_HEURISTIC_ENCRYPTED_ARCHIVE) {
03526dcc
 	cli_dbgmsg("cli_7unz: Encrypted header found in archive.\n");
cbf5017a
 	found = cli_append_virus(ctx, "Heuristics.Encrypted.7Zip");
03526dcc
     } else if(res == SZ_OK) {
0e605501
 	UInt32 i, blockIndex = 0xFFFFFFFF;
 	Byte *outBuffer = 0;
 	size_t outBufferSize = 0;
61fafd18
 	unsigned int encrypted = 0;
0e605501
 
 	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;
 
afa9976c
 	    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;
0e605501
 		}
afa9976c
 		SzArEx_GetFileNameUtf16(&db, i, utf16name);
0e605501
 	    }
afa9976c
 
0e605501
 	    name = (char *)utf16name;
cd94be7a
 	    for(j=0; j<(size_t)newnamelen; j++) /* FIXME */
0e605501
 		name[j] = utf16name[j];
afa9976c
 	    name[j] = 0;
0e605501
 	    cli_dbgmsg("cli_7unz: extracting %s\n", name);
61fafd18
 
 	    res = SzArEx_Extract(&db, &lookStream.s, i, &blockIndex, &outBuffer, &outBufferSize, &offset, &outSizeProcessed, &allocImp, &allocTempImp);
 	    if(res == SZ_ERROR_ENCRYPTED) {
 		encrypted = 1;
f61e92da
 		if(SCAN_HEURISTIC_ENCRYPTED_ARCHIVE) {
61fafd18
 		    cli_dbgmsg("cli_7unz: Encrypted files found in archive.\n");
cbf5017a
 		    found = cli_append_virus(ctx, "Heuristics.Encrypted.7Zip");
                     if (found != CL_CLEAN) {
                         if (found == CL_VIRUS) {
d7979d4f
                             if (SCAN_ALLMATCHES)
cbf5017a
                                 viruses_found++;
                         } else
                             break;
6ad45a29
 		    }
61fafd18
 		}
 	    }
 	    if(cli_matchmeta(ctx, name, 0, f->Size, encrypted, i, f->CrcDefined ? f->Crc : 0, NULL)) {
0e605501
 		found = CL_VIRUS;
6ad45a29
 		viruses_found++;
d7979d4f
 		if (!SCAN_ALLMATCHES)
6ad45a29
 		    break;
0e605501
 	    }
 	    if (res != SZ_OK)
61fafd18
 		cli_dbgmsg("cli_unz: extraction failed with %d\n", res);
0e605501
 	    else {
054ae9a1
 		if((found = cli_gentempfd(ctx->engine->tmpdir, &name, &fd)))
0e605501
 		    break;
 		    
 		cli_dbgmsg("cli_7unz: Saving to %s\n", name);
cd94be7a
 		if((size_t)cli_writen(fd, outBuffer + offset, outSizeProcessed) != outSizeProcessed)
0e605501
 		    found = CL_EWRITE;
 		else
d39cb658
 		    if ((found = cli_magic_scandesc(fd, name, ctx)) == CL_VIRUS)
6ad45a29
 			viruses_found++;
0e605501
 		close(fd);
 		if(!ctx->engine->keeptmp && cli_unlink(name))
 		    found = CL_EUNLINK;
 
 		free(name);
 		if(found != CL_CLEAN)
d7979d4f
 		    if (!(SCAN_ALLMATCHES && found == CL_VIRUS))
6ad45a29
 			break;
0e605501
 	    }
 	}
 	IAlloc_Free(&allocImp, outBuffer);
     }
     SzArEx_Free(&db, &allocImp);
     if(namelen > UTFBUFSZ)
 	free(utf16name);
 
     if (res == SZ_OK)
 	cli_dbgmsg("cli_7unz: completed successfully\n");
     else if (res == SZ_ERROR_UNSUPPORTED)
 	cli_dbgmsg("cli_7unz: unsupported\n");
     else if (res == SZ_ERROR_MEM)
 	cli_dbgmsg("cli_7unz: oom\n");
     else if (res == SZ_ERROR_CRC)
 	cli_dbgmsg("cli_7unz: crc mismatch\n");
03526dcc
     else if (res == SZ_ERROR_ENCRYPTED)
 	cli_dbgmsg("cli_7unz: encrypted\n");
0e605501
     else
 	cli_dbgmsg("cli_7unz: error %d\n", res);
 
d7979d4f
     if (SCAN_ALLMATCHES && viruses_found)
6ad45a29
 	return CL_VIRUS;
0e605501
     return found;
 }