1 /*
2  *  Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3  *  Copyright (C) 2011-2013 Sourcefire, Inc.
4  *
5  *  Authors: aCaB
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA 02110-1301, USA.
20  */
21 
22 #if defined(_WIN32)
23 #include <WinSock2.h>
24 #include <Windows.h>
25 #endif
26 
27 #include "clamav.h"
28 #include "7z_iface.h"
29 #include "lzma_iface.h"
30 #include "scanners.h"
31 #include "others.h"
32 #include "fmap.h"
33 
34 #include "7z/7z.h"
35 #include "7z/7zAlloc.h"
36 #include "7z/7zFile.h"
37 
38 static ISzAlloc allocImp = {__lzma_wrap_alloc, __lzma_wrap_free}, allocTempImp = {__lzma_wrap_alloc, __lzma_wrap_free};
39 
FileInStream_fmap_Read(void * pp,void * buf,size_t * size)40 static SRes FileInStream_fmap_Read(void *pp, void *buf, size_t *size)
41 {
42     CFileInStream *p = (CFileInStream *)pp;
43     size_t read_sz;
44 
45     if (*size == 0)
46         return 0;
47 
48     read_sz = fmap_readn(p->file.fmap, buf, p->s.curpos, *size);
49     if (read_sz == (size_t)-1) {
50         *size = 0;
51         return SZ_ERROR_READ;
52     }
53 
54     p->s.curpos += read_sz;
55 
56     *size = read_sz;
57     return SZ_OK;
58 }
59 
FileInStream_fmap_Seek(void * pp,Int64 * pos,ESzSeek origin)60 static SRes FileInStream_fmap_Seek(void *pp, Int64 *pos, ESzSeek origin)
61 {
62     CFileInStream *p = (CFileInStream *)pp;
63 
64     switch (origin) {
65         case SZ_SEEK_SET:
66             p->s.curpos = *pos;
67             break;
68         case SZ_SEEK_CUR:
69             p->s.curpos += *pos;
70             *pos = p->s.curpos;
71             break;
72         case SZ_SEEK_END:
73             p->s.curpos = p->file.fmap->len + *pos;
74             *pos        = p->s.curpos;
75             break;
76         default:
77             return 1;
78     }
79     return 0;
80 }
81 
82 #define UTFBUFSZ 256
cli_7unz(cli_ctx * ctx,size_t offset)83 int cli_7unz(cli_ctx *ctx, size_t offset)
84 {
85     CFileInStream archiveStream;
86     CLookToRead lookStream;
87     CSzArEx db;
88     SRes res;
89     UInt16 utf16buf[UTFBUFSZ], *utf16name = utf16buf;
90     int namelen            = UTFBUFSZ;
91     cl_error_t found       = CL_CLEAN;
92     Int64 begin_of_archive = offset;
93     UInt32 viruses_found   = 0;
94 
95     /* Replacement for
96        FileInStream_CreateVTable(&archiveStream); */
97     archiveStream.s.Read    = FileInStream_fmap_Read;
98     archiveStream.s.Seek    = FileInStream_fmap_Seek;
99     archiveStream.s.curpos  = 0;
100     archiveStream.file.fmap = ctx->fmap;
101 
102     LookToRead_CreateVTable(&lookStream, False);
103 
104     if (archiveStream.s.Seek(&archiveStream.s, &begin_of_archive, SZ_SEEK_SET) != 0)
105         return CL_CLEAN;
106 
107     lookStream.realStream = &archiveStream.s;
108     LookToRead_Init(&lookStream);
109 
110     SzArEx_Init(&db);
111     res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);
112     if (res == SZ_ERROR_ENCRYPTED && SCAN_HEURISTIC_ENCRYPTED_ARCHIVE) {
113         cli_dbgmsg("cli_7unz: Encrypted header found in archive.\n");
114         found = cli_append_virus(ctx, "Heuristics.Encrypted.7Zip");
115     } else if (res == SZ_OK) {
116         UInt32 i, blockIndex = 0xFFFFFFFF;
117         Byte *outBuffer        = 0;
118         size_t outBufferSize   = 0;
119         unsigned int encrypted = 0;
120 
121         for (i = 0; i < db.db.NumFiles; i++) {
122             size_t offset           = 0;
123             size_t outSizeProcessed = 0;
124             const CSzFileItem *f    = db.db.Files + i;
125             char *name;
126             char *tmp_name;
127             size_t j;
128             int newnamelen, fd;
129 
130             if ((found = cli_checklimits("7unz", ctx, 0, 0, 0)))
131                 break;
132 
133             if (f->IsDir)
134                 continue;
135 
136             if (cli_checklimits("7unz", ctx, f->Size, 0, 0))
137                 continue;
138 
139             if (!db.FileNameOffsets)
140                 newnamelen = 0; /* no filename */
141             else {
142                 newnamelen = SzArEx_GetFileNameUtf16(&db, i, NULL);
143                 if (newnamelen > namelen) {
144                     if (namelen > UTFBUFSZ)
145                         free(utf16name);
146                     utf16name = cli_malloc(newnamelen * 2);
147                     if (!utf16name) {
148                         found = CL_EMEM;
149                         break;
150                     }
151                     namelen = newnamelen;
152                 }
153                 SzArEx_GetFileNameUtf16(&db, i, utf16name);
154             }
155 
156             name = (char *)utf16name;
157             for (j = 0; j < (size_t)newnamelen; j++) /* FIXME */
158                 name[j] = utf16name[j];
159             name[j] = 0;
160             cli_dbgmsg("cli_7unz: extracting %s\n", name);
161 
162             res = SzArEx_Extract(&db, &lookStream.s, i, &blockIndex, &outBuffer, &outBufferSize, &offset, &outSizeProcessed, &allocImp, &allocTempImp);
163             if (res == SZ_ERROR_ENCRYPTED) {
164                 encrypted = 1;
165                 if (SCAN_HEURISTIC_ENCRYPTED_ARCHIVE) {
166                     cli_dbgmsg("cli_7unz: Encrypted files found in archive.\n");
167                     found = cli_append_virus(ctx, "Heuristics.Encrypted.7Zip");
168                     if (found != CL_CLEAN) {
169                         if (found == CL_VIRUS) {
170                             if (SCAN_ALLMATCHES)
171                                 viruses_found++;
172                         } else
173                             break;
174                     }
175                 }
176             }
177             if (cli_matchmeta(ctx, name, 0, f->Size, encrypted, i, f->CrcDefined ? f->Crc : 0, NULL)) {
178                 found = CL_VIRUS;
179                 viruses_found++;
180                 if (!SCAN_ALLMATCHES)
181                     break;
182             }
183             if (res != SZ_OK)
184                 cli_dbgmsg("cli_unz: extraction failed with %d\n", res);
185             else if ((outBuffer == NULL) || (outSizeProcessed == 0)) {
186                 cli_dbgmsg("cli_unz: extracted empty file\n");
187             } else {
188                 if ((found = cli_gentempfd(ctx->sub_tmpdir, &tmp_name, &fd)))
189                     break;
190 
191                 cli_dbgmsg("cli_7unz: Saving to %s\n", tmp_name);
192                 if (cli_writen(fd, outBuffer + offset, outSizeProcessed) != outSizeProcessed)
193                     found = CL_EWRITE;
194                 else if ((found = cli_magic_scan_desc(fd, tmp_name, ctx, name)) == CL_VIRUS)
195                     viruses_found++;
196                 close(fd);
197                 if (!ctx->engine->keeptmp && cli_unlink(tmp_name))
198                     found = CL_EUNLINK;
199 
200                 free(tmp_name);
201                 if (found != CL_CLEAN)
202                     if (!(SCAN_ALLMATCHES && found == CL_VIRUS))
203                         break;
204             }
205         }
206         IAlloc_Free(&allocImp, outBuffer);
207     }
208     SzArEx_Free(&db, &allocImp);
209     if (namelen > UTFBUFSZ)
210         free(utf16name);
211 
212     if (res == SZ_OK)
213         cli_dbgmsg("cli_7unz: completed successfully\n");
214     else if (res == SZ_ERROR_UNSUPPORTED)
215         cli_dbgmsg("cli_7unz: unsupported\n");
216     else if (res == SZ_ERROR_MEM)
217         cli_dbgmsg("cli_7unz: oom\n");
218     else if (res == SZ_ERROR_CRC)
219         cli_dbgmsg("cli_7unz: crc mismatch\n");
220     else if (res == SZ_ERROR_ENCRYPTED)
221         cli_dbgmsg("cli_7unz: encrypted\n");
222     else
223         cli_dbgmsg("cli_7unz: error %d\n", res);
224 
225     if (SCAN_ALLMATCHES && viruses_found)
226         return CL_VIRUS;
227     return found;
228 }
229