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