~louis/ubuntu/trusty/clamav/lp799623_fix_logrotate

« back to all changes in this revision

Viewing changes to libclamav/ishield.c

  • Committer: Bazaar Package Importer
  • Author(s): Scott Kitterman
  • Date: 2010-03-12 11:30:04 UTC
  • mfrom: (0.41.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20100312113004-b0fop4bkycszdd0z
Tags: 0.96~rc1+dfsg-0ubuntu1
* New upstream RC - FFE (LP: #537636):
  - Add OfficialDatabaseOnly option to clamav-base.postinst.in
  - Add LocalSocketGroup option to clamav-base.postinst.in
  - Add LocalSocketMode option to clamav-base.postinst.in
  - Add CrossFilesystems option to clamav-base.postinst.in
  - Add ClamukoScannerCount option to clamav-base.postinst.in
  - Add BytecodeSecurity opiton to clamav-base.postinst.in
  - Add DetectionStatsHostID option to clamav-freshclam.postinst.in
  - Add Bytecode option to clamav-freshclam.postinst.in
  - Add MilterSocketGroup option to clamav-milter.postinst.in
  - Add MilterSocketMode option to clamav-milter.postinst.in
  - Add ReportHostname option to clamav-milter.postinst.in
  - Bump libclamav SO version to 6.1.0 in libclamav6.install
  - Drop clamdmon from clamav.examples (no longer shipped by upstream)
  - Drop libclamav.a from libclamav-dev.install (not built by upstream)
  - Update SO version for lintian override for libclamav6
  - Add new Bytecode Testing Tool, usr/bin/clambc, to clamav.install
  - Add build-depends on python and python-setuptools for new test suite
  - Update debian/copyright for the embedded copy of llvm (using the system
    llvm is not currently feasible)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  Copyright (C) 2009 Sourcefire, Inc.
 
3
 *
 
4
 *  Authors: aCaB <acab@clamav.net>
 
5
 *
 
6
 *  This program is free software; you can redistribute it and/or modify
 
7
 *  it under the terms of the GNU General Public License version 2 as
 
8
 *  published by the Free Software Foundation.
 
9
 *
 
10
 *  This program is distributed in the hope that it will be useful,
 
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 *  GNU General Public License for more details.
 
14
 *
 
15
 *  You should have received a copy of the GNU General Public License
 
16
 *  along with this program; if not, write to the Free Software
 
17
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
18
 *  MA 02110-1301, USA.
 
19
 */
 
20
 
 
21
/* common routines to deal with installshield archives and installers */
 
22
 
 
23
#if HAVE_CONFIG_H
 
24
#include "clamav-config.h"
 
25
#endif
 
26
 
 
27
#include <sys/types.h>
 
28
#include <sys/stat.h>
 
29
#include <fcntl.h>
 
30
#ifdef  HAVE_UNISTD_H
 
31
#include <unistd.h>
 
32
#endif
 
33
#if HAVE_STRING_H
 
34
#include <string.h>
 
35
#endif
 
36
#include <limits.h>
 
37
#if HAVE_STRINGS_H
 
38
#include <strings.h>
 
39
#endif
 
40
#if defined(HAVE_MMAP) && defined(HAVE_SYS_MMAN_H)
 
41
#include <sys/mman.h>
 
42
#endif
 
43
#include <zlib.h>
 
44
 
 
45
#include "scanners.h"
 
46
#include "cltypes.h"
 
47
#include "others.h"
 
48
#include "fmap.h"
 
49
#include "ishield.h"
 
50
 
 
51
#ifndef LONG_MAX
 
52
#define LONG_MAX ((-1UL)>>1)
 
53
#endif
 
54
 
 
55
#ifndef HAVE_ATTRIB_PACKED
 
56
#define __attribute__(x)
 
57
#endif
 
58
#ifdef HAVE_PRAGMA_PACK
 
59
#pragma pack(1)
 
60
#endif
 
61
#ifdef HAVE_PRAGMA_PACK_HPPA
 
62
#pragma pack 1
 
63
#endif
 
64
 
 
65
/* PACKED things go here */
 
66
 
 
67
struct IS_HDR {
 
68
    uint32_t magic; 
 
69
    uint32_t unk1; /* version ??? */
 
70
    uint32_t unk2; /* ??? */
 
71
    uint32_t data_off;
 
72
    uint32_t data_sz; /* ??? */
 
73
} __attribute__((packed));
 
74
 
 
75
struct IS_FB {
 
76
    char fname[0x104]; /* MAX_PATH */
 
77
    uint32_t unk1; /* 6 */
 
78
    uint32_t unk2;
 
79
    uint64_t csize;
 
80
    uint32_t unk3;
 
81
    uint32_t unk4; /* 1 */
 
82
    uint32_t unk5;
 
83
    uint32_t unk6;
 
84
    uint32_t unk7;
 
85
    uint32_t unk8;
 
86
    uint32_t unk9;
 
87
    uint32_t unk10;
 
88
    uint32_t unk11;
 
89
} __attribute__((packed));
 
90
 
 
91
struct IS_COMPONENT {
 
92
    uint32_t str_name_off;
 
93
    uint32_t unk_str1_off;
 
94
    uint32_t unk_str2_off;
 
95
    uint16_t unk_flags;
 
96
    uint32_t unk_str3_off;
 
97
    uint32_t unk_str4_off;
 
98
    uint16_t ordinal_id;
 
99
    uint32_t str_shortname_off;
 
100
    uint32_t unk_str6_off;
 
101
    uint32_t unk_str7_off;
 
102
    uint32_t unk_str8_off;
 
103
    char guid1[16];
 
104
    char guid2[16];
 
105
    uint32_t unk_str9_off;
 
106
    char unk1[3];
 
107
    uint16_t unk_flags2;
 
108
    uint32_t unk3[5];
 
109
    uint32_t unk_str10_off;
 
110
    uint32_t unk4[4];
 
111
    uint16_t unk5;
 
112
    uint16_t sub_comp_cnt;
 
113
    uint32_t sub_comp_offs_array;
 
114
    uint32_t next_comp_off;
 
115
    uint32_t unk_str11_off;
 
116
    uint32_t unk_str12_off;
 
117
    uint32_t unk_str13_off;
 
118
    uint32_t unk_str14_off;
 
119
    uint32_t str_next1_off;
 
120
    uint32_t str_next2_off;
 
121
} __attribute__((packed));
 
122
 
 
123
struct IS_INSTTYPEHDR {
 
124
    uint32_t unk1;
 
125
    uint32_t cnt;
 
126
    uint32_t off;
 
127
} __attribute__((packed));
 
128
 
 
129
struct IS_INSTTYPEITEM {
 
130
    uint32_t str_name1_off;
 
131
    uint32_t str_name2_off;
 
132
    uint32_t str_name3_off;
 
133
    uint32_t cnt;
 
134
    uint32_t off;
 
135
} __attribute__((packed));
 
136
 
 
137
 
 
138
struct IS_OBJECTS {
 
139
    /* 200 */ uint32_t strings_off;
 
140
    /* 204 */ uint32_t zero1;
 
141
    /* 208 */ uint32_t comps_off;
 
142
    /* 20c */ uint32_t dirs_off;
 
143
    /* 210 */ uint32_t zero2;
 
144
    /* 214 */ uint32_t unk1, unk2; /* 0x4a636 304694 uguali - NOT AN OFFSET! */
 
145
    /* 21c */ uint32_t dirs_cnt;
 
146
    /* 220 */ uint32_t zero3;
 
147
    /* 224 */ uint32_t dirs_sz; /* dirs_cnt * 4 */
 
148
    /* 228 */ uint32_t files_cnt;
 
149
    /* 22c */ uint32_t dir_sz2; /* same as dirs_sz ?? */
 
150
    /* 230 */ uint16_t unk5; /* 1 - comp count ?? */
 
151
    /* 232 */ uint32_t insttype_off;
 
152
    /* 234 */ uint16_t zero4;
 
153
    /* 238 */ uint32_t zero5;
 
154
    /* 23c */ uint32_t unk7; /* 0xd0 - 208 */
 
155
    /* 240 */ uint16_t unk8;
 
156
    /* 242 */ uint32_t unk9;
 
157
    /* 246 */ uint32_t unk10;   
 
158
} __attribute__((packed));
 
159
 
 
160
 
 
161
struct IS_FILEITEM {
 
162
    uint16_t flags; /* 0 = EXTERNAL | 4 = INTERNAL | 8 = NAME_fuckup_rare | c = name_fuckup_common */
 
163
    uint64_t size;
 
164
    uint64_t csize;
 
165
    uint64_t stream_off;
 
166
    uint8_t md5[16];
 
167
    uint64_t versioninfo_id;
 
168
    uint32_t zero1;
 
169
    uint32_t zero2;
 
170
    uint32_t str_name_off;
 
171
    uint16_t dir_id;
 
172
    uint32_t unk13; /* 0, 20, 21 ??? */
 
173
    uint32_t unk14; /* timestamp ??? */
 
174
    uint32_t unk15; /* begins with 1 then 2 but not the cab# ??? */
 
175
    uint32_t prev_dup_id; /* msvcrt #38(0, 97, 2) #97(38, 1181, 3) ... , 0, 1) */
 
176
    uint32_t next_dup_id;
 
177
    uint8_t flag_has_dup; /* HAS_NEXT = 2 | HAS_BOTH = 3 | HAS_PREV = 1 */
 
178
    uint16_t datafile_id;
 
179
} __attribute__((packed));
 
180
 
 
181
 
 
182
#ifdef HAVE_PRAGMA_PACK
 
183
#pragma pack()
 
184
#endif
 
185
#ifdef HAVE_PRAGMA_PACK_HPPA
 
186
#pragma pack
 
187
#endif
 
188
 
 
189
 
 
190
 
 
191
static int is_dump_and_scan(cli_ctx *ctx, off_t off, size_t fsize);
 
192
static const uint8_t skey[] = { 0xec, 0xca, 0x79, 0xf8 }; /* ~0x13, ~0x35, ~0x86, ~0x07 */
 
193
 
 
194
/* Extracts the content of MSI based IS */
 
195
int cli_scanishield_msi(cli_ctx *ctx, off_t off) {
 
196
    uint8_t *buf;
 
197
    unsigned int fcount, scanned = 0;
 
198
    int ret;
 
199
    fmap_t *map = *ctx->fmap;
 
200
 
 
201
    cli_dbgmsg("in ishield-msi\n");
 
202
    if(!(buf = fmap_need_off_once(map, off, 0x20))) {
 
203
        cli_dbgmsg("ishield-msi: short read for header\n");
 
204
        return CL_CLEAN;
 
205
    }
 
206
    off += 0x20;
 
207
    if(cli_readint32(buf + 8) | cli_readint32(buf + 0xc) | cli_readint32(buf + 0x10) | cli_readint32(buf + 0x14) | cli_readint32(buf + 0x18) | cli_readint32(buf + 0x1c))
 
208
        return CL_CLEAN;
 
209
    if(!(fcount = cli_readint32(buf))) {
 
210
        cli_dbgmsg("ishield-msi: no files?\n");
 
211
        return CL_CLEAN;
 
212
    }
 
213
    while(fcount--) {
 
214
        struct IS_FB fb;
 
215
        uint8_t obuf[BUFSIZ], *key = (uint8_t *)&fb.fname;
 
216
        char *tempfile;
 
217
        unsigned int i, lameidx=0, keylen;
 
218
        int ofd;
 
219
        uint64_t csize;
 
220
        z_stream z;
 
221
 
 
222
        if(fmap_readn(map, &fb, off, sizeof(fb)) != sizeof(fb)) {
 
223
            cli_dbgmsg("ishield-msi: short read for fileblock\n");
 
224
            return CL_CLEAN;
 
225
        }
 
226
        off += sizeof(fb);
 
227
        fb.fname[sizeof(fb.fname)-1] = '\0';
 
228
        csize = le64_to_host(fb.csize);
 
229
        if(!CLI_ISCONTAINED(0, map->len, off, csize)) {
 
230
            cli_dbgmsg("ishield-msi: next stream is out of file, giving up\n");
 
231
            return CL_CLEAN;
 
232
        }
 
233
        if(ctx->engine->maxfilesize && csize > ctx->engine->maxfilesize) {
 
234
            cli_dbgmsg("ishield-msi: skipping stream due to size limits (%lu vs %lu)\n", (unsigned long int) csize, (unsigned long int) ctx->engine->maxfilesize);
 
235
            off += csize;
 
236
            continue;
 
237
        }
 
238
 
 
239
        keylen = strlen((const char *)key);
 
240
        if(!keylen) return CL_CLEAN;
 
241
        /* FIXMEISHIELD: cleanup the spam below */
 
242
        cli_dbgmsg("ishield-msi: File %s (csize: %llx, unk1:%x unk2:%x unk3:%x unk4:%x unk5:%x unk6:%x unk7:%x unk8:%x unk9:%x unk10:%x unk11:%x)\n", key, (long long)csize, fb.unk1, fb.unk2, fb.unk3, fb.unk4, fb.unk5, fb.unk6, fb.unk7, fb.unk8, fb.unk9, fb.unk10, fb.unk11);
 
243
        if(!(tempfile = cli_gentemp(ctx->engine->tmpdir))) return CL_EMEM;
 
244
        if((ofd = open(tempfile, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRUSR|S_IWUSR)) < 0) {
 
245
            cli_dbgmsg("ishield-msi: failed to create file %s\n", tempfile);
 
246
            free(tempfile);
 
247
            return CL_ECREAT;
 
248
        }
 
249
 
 
250
        for(i=0; i<keylen; i++)
 
251
            key[i] ^= skey[i & 3];
 
252
        memset(&z, 0, sizeof(z));
 
253
        inflateInit(&z);
 
254
        ret = CL_SUCCESS;
 
255
        while(csize) {
 
256
            uint8_t buf2[BUFSIZ];
 
257
            z.avail_in = MIN(csize, sizeof(buf2));
 
258
            if(fmap_readn(map, buf2, off, z.avail_in) != z.avail_in) {
 
259
                cli_dbgmsg("ishield-msi: premature EOS or read fail\n");
 
260
                break;
 
261
            }
 
262
            off += z.avail_in;
 
263
            for(i=0; i<z.avail_in; i++, lameidx++) {
 
264
                uint8_t c = buf2[i];
 
265
                c = (c>>4) | (c<<4);
 
266
                c ^= key[(lameidx & 0x3ff) % keylen];
 
267
                buf2[i] = c;
 
268
            }
 
269
            csize -= z.avail_in;
 
270
            z.next_in = buf2;
 
271
            do {
 
272
                int inf;
 
273
                z.avail_out = sizeof(obuf);
 
274
                z.next_out = obuf;
 
275
                inf = inflate(&z, 0);
 
276
                if(inf != Z_OK && inf != Z_STREAM_END && inf != Z_BUF_ERROR) {
 
277
                    cli_dbgmsg("ishield-msi: bad stream\n");
 
278
                    csize = 0;
 
279
                    off += csize;
 
280
                    break;
 
281
                }
 
282
                if (cli_writen(ofd, obuf, sizeof(obuf) - z.avail_out) < 0) {
 
283
                    ret = CL_EWRITE;
 
284
                    csize = 0;
 
285
                    break;
 
286
                }
 
287
                if(ctx->engine->maxfilesize && z.total_out > ctx->engine->maxfilesize) {
 
288
                    cli_dbgmsg("ishield-msi: trimming output file due to size limits (%lu vs %lu)\n", z.total_out, (unsigned long int) ctx->engine->maxfilesize);
 
289
                    off += csize;
 
290
                    csize = 0;
 
291
                    break;
 
292
                }
 
293
            } while (!z.avail_out);
 
294
        }
 
295
 
 
296
        inflateEnd(&z);
 
297
 
 
298
        if (ret == CL_SUCCESS) {
 
299
            cli_dbgmsg("ishield-msi: extracted to %s\n", tempfile);
 
300
 
 
301
            lseek(ofd, 0, SEEK_SET);
 
302
            ret = cli_magic_scandesc(ofd, ctx);
 
303
        }
 
304
        close(ofd);
 
305
 
 
306
        if(!ctx->engine->keeptmp)
 
307
            if(cli_unlink(tempfile)) ret = CL_EUNLINK;
 
308
        free(tempfile);
 
309
 
 
310
        if(ret != CL_CLEAN)
 
311
            return ret;
 
312
 
 
313
        scanned++;
 
314
        if (ctx->engine->maxfiles && scanned>=ctx->engine->maxfiles) {
 
315
            cli_dbgmsg("ishield-msi: File limit reached (max: %u)\n", ctx->engine->maxfiles);
 
316
            return CL_EMAXFILES;
 
317
        }
 
318
    }
 
319
    return CL_CLEAN;
 
320
}
 
321
 
 
322
 
 
323
struct IS_CABSTUFF {
 
324
    struct CABARRAY {
 
325
        unsigned int cabno;
 
326
        off_t off;
 
327
        size_t sz;
 
328
    } *cabs;
 
329
    off_t hdr;
 
330
    size_t hdrsz;
 
331
    unsigned int cabcnt;
 
332
};
 
333
 
 
334
static void md5str(uint8_t *sum);
 
335
static int is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c);
 
336
static int is_extract_cab(cli_ctx *ctx, uint64_t off, uint64_t size, uint64_t csize);
 
337
 
 
338
/* Extract the content of older (non-MSI) IS */
 
339
int cli_scanishield(cli_ctx *ctx, off_t off, size_t sz) {
 
340
    char *fname, *path, *version, *strsz, *eostr, *data;
 
341
    int ret = CL_CLEAN;
 
342
    long fsize;
 
343
    off_t coff = off;
 
344
    struct IS_CABSTUFF c = { NULL, -1, 0, 0 };
 
345
    fmap_t *map = *ctx->fmap;
 
346
 
 
347
    while(ret == CL_CLEAN) {
 
348
        fname = fmap_need_offstr(map, coff, 2048);
 
349
        if(!fname) break;
 
350
        coff += strlen(fname) + 1;
 
351
 
 
352
        path = fmap_need_offstr(map, coff, 2048);
 
353
        if(!path) break;
 
354
        coff += strlen(path) + 1;
 
355
 
 
356
        version = fmap_need_offstr(map, coff, 2048);
 
357
        if(!version) break;
 
358
        coff += strlen(version) + 1;
 
359
 
 
360
        strsz = fmap_need_offstr(map, coff, 2048);
 
361
        if(!strsz) break;
 
362
        coff += strlen(strsz) + 1;
 
363
 
 
364
        data = &strsz[strlen(strsz) + 1];
 
365
 
 
366
        fsize = strtol(strsz, &eostr, 10);
 
367
        if(fsize < 0 || fsize == LONG_MAX ||
 
368
           !*strsz || !eostr || eostr == strsz || *eostr ||
 
369
           (unsigned long)fsize >= sz ||
 
370
           data - fname >= sz - fsize
 
371
        ) break;
 
372
 
 
373
        cli_dbgmsg("ishield: @%lx found file %s (%s) - version %s - size %lu\n", (unsigned long int) coff, fname, path, version, (unsigned long int) fsize);
 
374
        sz -= (data - fname) + fsize;
 
375
 
 
376
        if(!strncasecmp(fname, "data", 4)) {
 
377
            long cabno;
 
378
            if(!strcasecmp(fname + 4, "1.hdr")) {
 
379
                if(c.hdr == -1) {
 
380
                    cli_dbgmsg("ishield: added data1.hdr to array\n");
 
381
                    c.hdr = coff;
 
382
                    c.hdrsz = fsize;
 
383
                    coff += fsize;
 
384
                    continue;
 
385
                }
 
386
                cli_warnmsg("ishield: got multiple header files\n");
 
387
            }
 
388
            cabno = strtol(fname + 4, &eostr, 10);
 
389
            if(cabno > 0 && cabno < 65536 && fname[4] && eostr && eostr != &fname[4] && !strcasecmp(eostr, ".cab")) {
 
390
                unsigned int i;
 
391
                for(i=0; i<c.cabcnt && i!=c.cabs[i].cabno; i++) { }
 
392
                if(i==c.cabcnt) {
 
393
                    c.cabcnt++;
 
394
                    if(!(c.cabs = cli_realloc2(c.cabs, sizeof(struct CABARRAY) * c.cabcnt))) {
 
395
                        ret = CL_EMEM;
 
396
                        break;
 
397
                    }
 
398
                    cli_dbgmsg("ishield: added data%lu.cab to array\n", cabno);
 
399
                    c.cabs[i].cabno = cabno;
 
400
                    c.cabs[i].off = coff;
 
401
                    c.cabs[i].sz = fsize;
 
402
                    coff += fsize;
 
403
                    continue;
 
404
                }
 
405
                cli_warnmsg("ishield: got multiple data%lu.cab files\n", cabno);
 
406
            }
 
407
        }
 
408
 
 
409
        fmap_unneed_ptr(map, fname, data-fname);
 
410
        ret = is_dump_and_scan(ctx, coff, fsize);
 
411
        coff += fsize;
 
412
    }
 
413
 
 
414
    if(ret == CL_CLEAN && (c.cabcnt || c.hdr != -1)) {
 
415
      if((ret = is_parse_hdr(ctx, &c)) == CL_CLEAN) {
 
416
            unsigned int i;
 
417
            if(c.hdr != -1) {
 
418
                cli_dbgmsg("ishield: scanning data1.hdr\n");
 
419
                ret = is_dump_and_scan(ctx, c.hdr, c.hdrsz);
 
420
            }
 
421
            for(i=0; i<c.cabcnt && ret == CL_CLEAN; i++) {
 
422
                cli_dbgmsg("ishield: scanning data%u.cab\n", c.cabs[i].cabno);
 
423
                ret = is_dump_and_scan(ctx, c.cabs[i].off, c.cabs[i].sz);
 
424
            }
 
425
      } else if( ret == CL_BREAK ) ret = CL_CLEAN;
 
426
    }
 
427
    if(c.cabs) free(c.cabs);
 
428
    return ret;
 
429
}
 
430
 
 
431
 
 
432
/* Utility func to scan a fd @ a given offset and size */
 
433
static int is_dump_and_scan(cli_ctx *ctx, off_t off, size_t fsize) {
 
434
    char *fname, *buf;
 
435
    int ofd, ret = CL_CLEAN;
 
436
    fmap_t *map = *ctx->fmap;
 
437
 
 
438
    if(!fsize) {
 
439
        cli_dbgmsg("ishield: skipping empty file\n");
 
440
        return CL_CLEAN;
 
441
    }
 
442
    if(!(fname = cli_gentemp(ctx->engine->tmpdir)))
 
443
        return CL_EMEM;
 
444
 
 
445
    if((ofd = open(fname, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRUSR|S_IWUSR)) < 0) {
 
446
        cli_errmsg("ishield: failed to create file %s\n", fname);
 
447
        free(fname);
 
448
        return CL_ECREAT;
 
449
    }
 
450
    while(fsize) {
 
451
        size_t rd = MIN(fsize, map->pgsz);
 
452
        if(!(buf = fmap_need_off_once(map, off, rd))) {
 
453
            cli_dbgmsg("ishield: read error\n");
 
454
            ret = CL_EREAD;
 
455
            break;
 
456
        }
 
457
        if(cli_writen(ofd, buf, rd) <= 0) {
 
458
            ret = CL_EWRITE;
 
459
            break;
 
460
        }
 
461
        fsize -= rd;
 
462
        off += rd;
 
463
    }
 
464
    if(!fsize) {
 
465
        cli_dbgmsg("ishield: extracted to %s\n", fname);
 
466
        lseek(ofd, 0, SEEK_SET);
 
467
        ret = cli_magic_scandesc(ofd, ctx);
 
468
    }
 
469
    close(ofd);
 
470
    if(!ctx->engine->keeptmp)
 
471
        if(cli_unlink(fname)) ret = CL_EUNLINK;
 
472
    free(fname);
 
473
    return ret;
 
474
}
 
475
 
 
476
/* Process data1.hdr and extracts all the available files from dataX.cab */
 
477
static int is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c) { 
 
478
    uint32_t h1_data_off, objs_files_cnt, objs_dirs_off;
 
479
    unsigned int off, i, scanned = 0;
 
480
    int ret = CL_BREAK;
 
481
    char hash[33], *hdr;
 
482
    fmap_t *map = *ctx->fmap;
 
483
    size_t mp_hdrsz;
 
484
 
 
485
    struct IS_HDR *h1;
 
486
    struct IS_OBJECTS *objs;
 
487
    /* struct IS_INSTTYPEHDR *typehdr; -- UNUSED */
 
488
 
 
489
    if(!c->hdr || !c->hdrsz || !c->cabcnt) {
 
490
        cli_dbgmsg("is_parse_hdr: inconsistent hdr, maybe a false match\n");
 
491
        return CL_CLEAN;
 
492
    }
 
493
 
 
494
    if(!(h1 = fmap_need_off(map, c->hdr, c->hdrsz))) {
 
495
        cli_dbgmsg("is_parse_hdr: not enough room for H1\n");
 
496
        return CL_CLEAN;
 
497
    }
 
498
    hdr = (char *)h1;
 
499
    h1_data_off = le32_to_host(h1->data_off);
 
500
    objs = (struct IS_OBJECTS *)fmap_need_ptr(map, hdr + h1_data_off, sizeof(*objs));
 
501
    if(!objs) {
 
502
        cli_dbgmsg("is_parse_hdr: not enough room for OBJECTS\n");
 
503
        return CL_CLEAN;
 
504
    }
 
505
 
 
506
    cli_dbgmsg("is_parse_hdr: magic %x, unk1 %x, unk2 %x, data_off %x, data_sz %x\n",
 
507
               h1->magic, h1->unk1, h1->unk2, h1_data_off, h1->data_sz);
 
508
    if(le32_to_host(h1->magic) != 0x28635349) {
 
509
        cli_dbgmsg("is_parse_hdr: bad magic. wrong version?\n");
 
510
        return CL_CLEAN;
 
511
    }
 
512
 
 
513
    fmap_unneed_ptr(map, h1, sizeof(*h1));
 
514
 
 
515
/*     cli_errmsg("COMPONENTS\n"); */
 
516
/*     off = le32_to_host(objs->comps_off) + h1_data_off; */
 
517
/*     for(i=1;  ; i++) { */
 
518
/*      struct IS_COMPONENT *cmp = (struct IS_COMPONENT *)(hdr + off); */
 
519
/*      if(!CLI_ISCONTAINED(hdr, c->hdrsz, ((char *)cmp), sizeof(*cmp))) { */
 
520
/*          cli_dbgmsg("is_extract: not enough room for COMPONENT\n"); */
 
521
/*          free(hdr); */
 
522
/*          return CL_CLEAN; */
 
523
/*      } */
 
524
/*      cli_errmsg("%06u\t%s\n", i, &hdr[le32_to_host(cmp->str_name_off) + h1_data_off]); */
 
525
/*      spam_strarray(hdr, h1_data_off + cmp->sub_comp_offs_array, h1_data_off, cmp->sub_comp_cnt); */
 
526
/*      if(!cmp->next_comp_off) break; */
 
527
/*      off = le32_to_host(cmp->next_comp_off) + h1_data_off; */
 
528
/*     } */
 
529
 
 
530
/*     cli_errmsg("DIRECTORIES (%u)", le32_to_host(objs->dirs_cnt)); */
 
531
    objs_dirs_off = le32_to_host(objs->dirs_off);
 
532
/*     spam_strarray(hdr, h1_data_off + objs_dirs_off, h1_data_off + objs_dirs_off, objs->dirs_cnt); */
 
533
 
 
534
/*     typehdr = (struct INSTTYPEHDR *)&hdr[h1_data_off + le32_to_host(objs->insttype_off)]; */
 
535
/*     printf("INSTTYPES (unk1: %d)\n-----------\n", typehdr->unk1); */
 
536
/*     off = typehdr->off + h1_data_off; */
 
537
/*     for(i=1; i<=typehdr->cnt; i++) { */
 
538
/*      uint32_t x = *(uint32_t *)(&hdr[off]); */
 
539
/*      struct INSTTYPEITEM *item = (struct INSTTYPEITEM *)&hdr[x + h1_data_off]; */
 
540
/*      printf("%06u\t%s\t aka %s\taka %s\n", i, &hdr[item->str_name1_off + h1_data_off], &hdr[item->str_name2_off + h1_data_off], &hdr[item->str_name3_off + h1_data_off]); */
 
541
/*      printf("components:\n"); */
 
542
/*      spam_strarray(hdr, h1_data_off + item->off, h1_data_off, item->cnt); */
 
543
/*      off+=4; */
 
544
/*     } */
 
545
 
 
546
 
 
547
/* dir = &hdr[*(uint32_t *)(&hdr[h1_data_off + objs_dirs_off + 4 * file->dir_id]) + h1_data_off + objs_dirs_off] */
 
548
 
 
549
    objs_files_cnt = le32_to_host(objs->files_cnt);
 
550
    off = h1_data_off + objs_dirs_off + le32_to_host(objs->dir_sz2);
 
551
    fmap_unneed_ptr(map, objs, sizeof(*objs));
 
552
    for(i=0; i<objs_files_cnt ;i++) {
 
553
        struct IS_FILEITEM *file = (struct IS_FILEITEM *)fmap_need_off(map, c->hdr + off, sizeof(*file));
 
554
 
 
555
        if(file) {
 
556
            const char *emptyname = "", *dir_name = emptyname, *file_name = emptyname;
 
557
            uint32_t dir_rel = h1_data_off + objs_dirs_off + 4 * le32_to_host(file->dir_id); /* rel off of dir entry from array of rel ptrs */
 
558
            uint32_t file_rel = objs_dirs_off + h1_data_off + le32_to_host(file->str_name_off); /* rel off of fname */
 
559
            uint64_t file_stream_off, file_size, file_csize;
 
560
            uint16_t cabno;
 
561
 
 
562
            memcpy(hash, file->md5, 16);
 
563
            md5str((uint8_t *)hash);
 
564
            if(fmap_need_ptr_once(map, &hdr[dir_rel], 4)) {
 
565
                dir_rel = cli_readint32(&hdr[dir_rel]) + h1_data_off + objs_dirs_off;
 
566
                if(fmap_need_str(map, &hdr[dir_rel], c->hdrsz - dir_rel))
 
567
                    dir_name = &hdr[dir_rel];
 
568
            }
 
569
            if(fmap_need_str(map, &hdr[file_rel], c->hdrsz - file_rel))
 
570
                file_name = &hdr[file_rel];
 
571
                
 
572
            file_stream_off = le64_to_host(file->stream_off);
 
573
            file_size = le64_to_host(file->size);
 
574
            file_csize = le64_to_host(file->csize);
 
575
            cabno = le16_to_host(file->datafile_id);
 
576
 
 
577
            switch(le16_to_host(file->flags)) {
 
578
            case 0:
 
579
                /* FIXMEISHIELD: for FS scan ? */
 
580
                cli_dbgmsg("is_parse_hdr: skipped external file:%s\\%s (size: %llu csize: %llu md5:%s)\n",
 
581
                           dir_name,
 
582
                           file_name,
 
583
                           (long long)file_size, (long long)file_csize, hash);
 
584
                break;
 
585
            case 4:
 
586
                cli_dbgmsg("is_parse_hdr: file %s\\%s (size: %llu csize: %llu md5:%s offset:%llx (data%u.cab) 13:%x 14:%x 15:%x)\n",
 
587
                           dir_name,
 
588
                           file_name,
 
589
                           (long long)file_size, (long long)file_csize, hash, (long long)file_stream_off,
 
590
                           cabno, file->unk13,  file->unk14,  file->unk15);
 
591
                if(file->flag_has_dup & 1)
 
592
                    cli_dbgmsg("is_parse_hdr: not scanned (dup)\n");
 
593
                else {
 
594
                    if(file_size) {
 
595
                        unsigned int j;
 
596
                        int cabret = CL_CLEAN;
 
597
 
 
598
                        if(ctx->engine->maxfilesize && file_csize > ctx->engine->maxfilesize) {
 
599
                            cli_dbgmsg("is_parse_hdr: skipping file due to size limits (%lu vs %lu)\n", (unsigned long int) file_csize, (unsigned long int) ctx->engine->maxfilesize);
 
600
                            break;
 
601
                        }
 
602
 
 
603
                        for(j=0; j<c->cabcnt && c->cabs[j].cabno != cabno; j++) {}
 
604
                        if(j != c->cabcnt) {
 
605
                            if(CLI_ISCONTAINED(c->cabs[j].off, c->cabs[j].sz, file_stream_off + c->cabs[j].off, file_csize)) {
 
606
                                scanned++;
 
607
                                if (ctx->engine->maxfiles && scanned >= ctx->engine->maxfiles) {
 
608
                                    cli_dbgmsg("is_parse_hdr: File limit reached (max: %u)\n", ctx->engine->maxfiles);
 
609
                                    if(file_name != emptyname)
 
610
                                        fmap_unneed_ptr(map, (void *)file_name, strlen(file_name)+1);
 
611
                                    if(dir_name != emptyname)
 
612
                                        fmap_unneed_ptr(map, (void *)dir_name, strlen(dir_name)+1);
 
613
                                    return CL_EMAXFILES;
 
614
                                }
 
615
                                cabret = is_extract_cab(ctx, file_stream_off + c->cabs[j].off, file_size, file_csize);
 
616
                            } else {
 
617
                                ret = CL_CLEAN;
 
618
                                cli_dbgmsg("is_parse_hdr: stream out of file\n");
 
619
                            }
 
620
                        } else {
 
621
                            ret = CL_CLEAN;
 
622
                            cli_dbgmsg("is_parse_hdr: data%u.cab not available\n", cabno);
 
623
                        }
 
624
                        if(cabret == CL_BREAK) {
 
625
                            ret = CL_CLEAN;
 
626
                            cabret = CL_CLEAN;
 
627
                        }
 
628
                        if(cabret != CL_CLEAN) {
 
629
                            if(file_name != emptyname)
 
630
                                fmap_unneed_ptr(map, (void *)file_name, strlen(file_name)+1);
 
631
                            if(dir_name != emptyname)
 
632
                                fmap_unneed_ptr(map, (void *)dir_name, strlen(dir_name)+1);
 
633
                            return cabret;
 
634
                        }
 
635
                    } else {
 
636
                        cli_dbgmsg("is_parse_hdr: skipped empty file\n");
 
637
                    }
 
638
                }
 
639
                break;
 
640
            default:
 
641
                cli_dbgmsg("is_parse_hdr: skipped unknown file entry %u\n", i);
 
642
            }
 
643
            if(file_name != emptyname)
 
644
                fmap_unneed_ptr(map, (void *)file_name, strlen(file_name)+1);
 
645
            if(dir_name != emptyname)
 
646
                fmap_unneed_ptr(map, (void *)dir_name, strlen(dir_name)+1);
 
647
            fmap_unneed_ptr(map, file, sizeof(*file));
 
648
        } else {
 
649
            ret = CL_CLEAN;
 
650
            cli_dbgmsg("is_parse_hdr: FILEITEM out of bounds\n");
 
651
        }
 
652
        off += sizeof(*file);
 
653
    }
 
654
    return ret;
 
655
}
 
656
 
 
657
 
 
658
static void md5str(uint8_t *sum) {
 
659
    int i;
 
660
    for(i=15; i>=0; i--) {
 
661
        uint8_t lo = (sum[i] & 0xf), hi = (sum[i] >> 4);
 
662
        lo += '0' + (lo > 9) * '\'';
 
663
        hi += '0' + (hi > 9) * '\'';
 
664
        sum[i*2+1] = lo;
 
665
        sum[i*2] = hi;
 
666
    }
 
667
    sum[32] = '\0';
 
668
}
 
669
 
 
670
 
 
671
#define IS_CABBUFSZ 65536
 
672
 
 
673
static int is_extract_cab(cli_ctx *ctx, uint64_t off, uint64_t size, uint64_t csize) {
 
674
    uint8_t *inbuf, *outbuf;
 
675
    char *tempfile;
 
676
    int ofd, ret = CL_CLEAN;
 
677
    z_stream z;
 
678
    uint64_t outsz = 0;
 
679
    int success = 0;
 
680
    fmap_t *map = *ctx->fmap;
 
681
 
 
682
    if(!(outbuf = cli_malloc(IS_CABBUFSZ)))
 
683
        return CL_EMEM;
 
684
 
 
685
    if(!(tempfile = cli_gentemp(ctx->engine->tmpdir))) {
 
686
        free(outbuf);
 
687
    }
 
688
    if((ofd = open(tempfile, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRUSR|S_IWUSR)) < 0) {
 
689
        cli_errmsg("is_extract_cab: failed to create file %s\n", tempfile);
 
690
        free(tempfile);
 
691
        free(outbuf);
 
692
        return CL_ECREAT;
 
693
    }
 
694
 
 
695
    while(csize) {
 
696
        uint16_t chunksz;
 
697
        success = 0;
 
698
        if(csize<2) {
 
699
            cli_dbgmsg("is_extract_cab: no room for chunk size\n");
 
700
            break;
 
701
        }
 
702
        csize -= 2;
 
703
        if(!(inbuf = fmap_need_off_once(map, off, 2))) {
 
704
            cli_dbgmsg("is_extract_cab: short read for chunk size\n");
 
705
            break;
 
706
        }
 
707
        off += 2;
 
708
        chunksz = inbuf[0] | (inbuf[1] << 8);
 
709
        if(!chunksz) {
 
710
            cli_dbgmsg("is_extract_cab: zero sized chunk\n");
 
711
            continue;
 
712
        }
 
713
        if(csize < chunksz) {
 
714
            cli_dbgmsg("is_extract_cab: chunk is bigger than csize\n");
 
715
            break;
 
716
        }
 
717
        csize -= chunksz;
 
718
        if(!(inbuf = fmap_need_off_once(map, off, chunksz))) {
 
719
            cli_dbgmsg("is_extract_cab: short read for chunk\n");
 
720
            break;
 
721
        }
 
722
        off += chunksz;
 
723
        memset(&z, 0, sizeof(z));
 
724
        inflateInit2(&z, -MAX_WBITS);
 
725
        z.next_in = (uint8_t *)inbuf;
 
726
        z.avail_in = chunksz;
 
727
        while(1) {
 
728
            int zret;
 
729
            z.next_out = outbuf;
 
730
            z.avail_out = IS_CABBUFSZ;
 
731
            zret = inflate(&z, 0);
 
732
            if(zret == Z_OK || zret == Z_STREAM_END || zret == Z_BUF_ERROR) {
 
733
                unsigned int umpd = IS_CABBUFSZ - z.avail_out;
 
734
                if(cli_writen(ofd, outbuf, umpd) < (ssize_t)umpd)
 
735
                    break;
 
736
                outsz += umpd;
 
737
                if(zret == Z_STREAM_END || z.avail_out == IS_CABBUFSZ /* FIXMEISHIELD: is the latter ok? */) {
 
738
                    success = 1;
 
739
                    break;
 
740
                }
 
741
                if(ctx->engine->maxfilesize && z.total_out > ctx->engine->maxfilesize) {
 
742
                    cli_dbgmsg("ishield_extract_cab: trimming output file due to size limits (%lu vs %lu)\n", z.total_out, (unsigned long int) ctx->engine->maxfilesize);
 
743
                    success = 1;
 
744
                    outsz = size;
 
745
                    break;
 
746
                }
 
747
                continue;
 
748
            }
 
749
            cli_dbgmsg("is_extract_cab: file decompression failed with %d\n", zret);
 
750
            break;
 
751
        }
 
752
        inflateEnd(&z);
 
753
        if(!success) break;
 
754
    }
 
755
    free(outbuf);
 
756
    if(success) {
 
757
        if (outsz != size)
 
758
            cli_dbgmsg("is_extract_cab: extracted %llu bytes to %s, expected %llu, scanning anyway.\n", (long long)outsz, tempfile, (long long)size);
 
759
        else
 
760
            cli_dbgmsg("is_extract_cab: extracted to %s\n", tempfile);
 
761
        lseek(ofd, 0, SEEK_SET);
 
762
        ret = cli_magic_scandesc(ofd, ctx);
 
763
    }
 
764
 
 
765
    close(ofd);
 
766
    if(!ctx->engine->keeptmp)
 
767
        if(cli_unlink(tempfile)) ret = CL_EUNLINK;
 
768
    free(tempfile);
 
769
    return success ? ret : CL_BREAK;
 
770
}