Coverage Report

Created: 2023-12-08 06:48

/src/clamav/libclamav/nsis/nulsft.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *  Copyright (C) 2007-2008 Sourcefire Inc.
3
 *
4
 *  Authors: Alberto Wu
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
#if HAVE_CONFIG_H
22
#include "clamav-config.h"
23
#endif
24
25
#include <stdio.h>
26
#include <sys/types.h>
27
#include <sys/stat.h>
28
#include <fcntl.h>
29
#include <string.h>
30
#ifdef HAVE_UNISTD_H
31
#include <unistd.h>
32
#endif
33
34
#include "clamav.h"
35
#include "others.h"
36
#include "nsis_bzlib.h"
37
/* #include "zlib.h" */
38
#include "nsis_zlib.h"
39
#include "lzma_iface.h"
40
#include "matcher.h"
41
#include "scanners.h"
42
#include "nulsft.h" /* SHUT UP GCC -Wextra */
43
#include "fmap.h"
44
45
0
#define EC32(x) le32_to_host(x)
46
47
enum {
48
    COMP_NOT_DETECTED,
49
    COMP_BZIP2,
50
    COMP_LZMA,
51
    COMP_ZLIB,
52
    COMP_NOCOMP
53
};
54
55
struct nsis_st {
56
    size_t curpos;
57
    int ofd;
58
    int opened;
59
    off_t off;
60
    off_t fullsz;
61
    char *dir;
62
    uint32_t asz;
63
    uint32_t hsz;
64
    uint32_t fno;
65
    uint8_t comp;
66
    uint8_t solid;
67
    uint8_t freecomp;
68
    uint8_t eof;
69
    struct stream_state nsis;
70
    nsis_bzstream bz;
71
    struct CLI_LZMA lz;
72
    /*   z_stream z; */
73
    nsis_z_stream z;
74
    const unsigned char *freeme;
75
    fmap_t *map;
76
    char ofn[1024];
77
};
78
79
#define LINESTR(x) #x
80
#define LINESTR2(x) LINESTR(x)
81
#define __AT__ " at "__FILE__ \
82
               ":" LINESTR2(__LINE__)
83
84
static int nsis_init(struct nsis_st *n)
85
0
{
86
0
    switch (n->comp) {
87
0
        case COMP_BZIP2:
88
0
            memset(&n->bz, 0, sizeof(nsis_bzstream));
89
0
            if (nsis_BZ2_bzDecompressInit(&n->bz, 0, 0) != BZ_OK)
90
0
                return CL_EUNPACK;
91
0
            n->freecomp = 1;
92
0
            break;
93
0
        case COMP_LZMA:
94
0
            memset(&n->lz, 0, sizeof(struct CLI_LZMA));
95
0
            if (cli_LzmaInit(&n->lz, 0xffffffffffffffffULL) != LZMA_RESULT_OK)
96
0
                return CL_EUNPACK;
97
0
            n->freecomp = 1;
98
0
            break;
99
0
        case COMP_ZLIB:
100
0
            memset(&n->z, 0, sizeof(z_stream));
101
            /*     inflateInit2(&n->z, -MAX_WBITS); */
102
            /*     n->freecomp=1; */
103
0
            nsis_inflateInit(&n->z);
104
0
            n->freecomp = 0;
105
0
    }
106
0
    return CL_SUCCESS;
107
0
}
108
109
static void nsis_shutdown(struct nsis_st *n)
110
0
{
111
0
    if (!n->freecomp)
112
0
        return;
113
114
0
    switch (n->comp) {
115
0
        case COMP_BZIP2:
116
0
            nsis_BZ2_bzDecompressEnd(&n->bz);
117
0
            break;
118
0
        case COMP_LZMA:
119
0
            cli_LzmaShutdown(&n->lz);
120
0
            break;
121
0
        case COMP_ZLIB:
122
            /*     inflateEnd(&n->z); */
123
0
            break;
124
0
    }
125
126
0
    n->freecomp = 0;
127
0
}
128
129
static int nsis_decomp(struct nsis_st *n)
130
0
{
131
0
    int ret = CL_EFORMAT;
132
0
    switch (n->comp) {
133
0
        case COMP_BZIP2:
134
0
            n->bz.avail_in  = n->nsis.avail_in;
135
0
            n->bz.next_in   = n->nsis.next_in;
136
0
            n->bz.avail_out = n->nsis.avail_out;
137
0
            n->bz.next_out  = n->nsis.next_out;
138
0
            switch (nsis_BZ2_bzDecompress(&n->bz)) {
139
0
                case BZ_OK:
140
0
                    ret = CL_SUCCESS;
141
0
                    break;
142
0
                case BZ_STREAM_END:
143
0
                    ret = CL_BREAK;
144
0
            }
145
0
            n->nsis.avail_in  = n->bz.avail_in;
146
0
            n->nsis.next_in   = n->bz.next_in;
147
0
            n->nsis.avail_out = n->bz.avail_out;
148
0
            n->nsis.next_out  = n->bz.next_out;
149
0
            break;
150
0
        case COMP_LZMA:
151
0
            n->lz.avail_in  = n->nsis.avail_in;
152
0
            n->lz.next_in   = n->nsis.next_in;
153
0
            n->lz.avail_out = n->nsis.avail_out;
154
0
            n->lz.next_out  = n->nsis.next_out;
155
0
            switch (cli_LzmaDecode(&n->lz)) {
156
0
                case LZMA_RESULT_OK:
157
0
                    ret = CL_SUCCESS;
158
0
                    break;
159
0
                case LZMA_STREAM_END:
160
0
                    ret = CL_BREAK;
161
0
            }
162
0
            n->nsis.avail_in  = n->lz.avail_in;
163
0
            n->nsis.next_in   = n->lz.next_in;
164
0
            n->nsis.avail_out = n->lz.avail_out;
165
0
            n->nsis.next_out  = n->lz.next_out;
166
0
            break;
167
0
        case COMP_ZLIB:
168
0
            n->z.avail_in  = n->nsis.avail_in;
169
0
            n->z.next_in   = n->nsis.next_in;
170
0
            n->z.avail_out = n->nsis.avail_out;
171
0
            n->z.next_out  = n->nsis.next_out;
172
            /*  switch (inflate(&n->z, Z_NO_FLUSH)) { */
173
0
            switch (nsis_inflate(&n->z)) {
174
0
                case Z_OK:
175
0
                    ret = CL_SUCCESS;
176
0
                    break;
177
0
                case Z_STREAM_END:
178
0
                    ret = CL_BREAK;
179
0
            }
180
0
            n->nsis.avail_in  = n->z.avail_in;
181
0
            n->nsis.next_in   = n->z.next_in;
182
0
            n->nsis.avail_out = n->z.avail_out;
183
0
            n->nsis.next_out  = n->z.next_out;
184
0
            break;
185
0
    }
186
0
    return ret;
187
0
}
188
189
static int nsis_unpack_next(struct nsis_st *n, cli_ctx *ctx)
190
0
{
191
0
    const unsigned char *ibuf;
192
0
    uint32_t size, loops;
193
0
    int ret, gotsome = 0;
194
0
    unsigned char obuf[BUFSIZ];
195
196
0
    if (n->eof) {
197
0
        cli_dbgmsg("NSIS: extraction complete\n");
198
0
        return CL_BREAK;
199
0
    }
200
201
0
    if ((ret = cli_checklimits("NSIS", ctx, 0, 0, 0)) != CL_CLEAN)
202
0
        return ret;
203
204
0
    if (n->fno)
205
0
        snprintf(n->ofn, 1023, "%s" PATHSEP "content.%.3u", n->dir, n->fno);
206
0
    else
207
0
        snprintf(n->ofn, 1023, "%s" PATHSEP "headers", n->dir);
208
209
0
    n->fno++;
210
0
    n->opened = 0;
211
212
0
    if (!n->solid) {
213
0
        if (fmap_readn(n->map, &size, n->curpos, 4) != 4) {
214
0
            cli_dbgmsg("NSIS: reached EOF - extraction complete\n");
215
0
            return CL_BREAK;
216
0
        }
217
0
        n->curpos += 4;
218
0
        if (n->asz == 4) {
219
0
            cli_dbgmsg("NSIS: reached CRC - extraction complete\n");
220
0
            return CL_BREAK;
221
0
        }
222
0
        loops = EC32(size);
223
0
        if (!(size = (loops & ~0x80000000))) {
224
0
            cli_dbgmsg("NSIS: empty file found\n");
225
0
            return CL_SUCCESS;
226
0
        }
227
0
        if (n->asz < 4 || size > n->asz - 4) {
228
0
            cli_dbgmsg("NSIS: next file is outside the archive\n");
229
0
            return CL_BREAK;
230
0
        }
231
232
0
        n->asz -= size + 4;
233
234
0
        if ((ret = cli_checklimits("NSIS", ctx, size, 0, 0)) != CL_CLEAN) {
235
0
            n->curpos += size;
236
0
            return ret;
237
0
        }
238
0
        if (!(ibuf = fmap_need_off_once(n->map, n->curpos, size))) {
239
0
            cli_dbgmsg("NSIS: cannot read %u bytes"__AT__
240
0
                       "\n",
241
0
                       size);
242
0
            return CL_EREAD;
243
0
        }
244
0
        if ((n->ofd = open(n->ofn, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0600)) == -1) {
245
0
            cli_errmsg("NSIS: unable to create output file %s - aborting.", n->ofn);
246
0
            return CL_ECREAT;
247
0
        }
248
0
        n->opened = 1;
249
0
        n->curpos += size;
250
0
        if (loops == size) {
251
252
0
            if (cli_writen(n->ofd, ibuf, size) != size) {
253
0
                cli_dbgmsg("NSIS: cannot write output file"__AT__
254
0
                           "\n");
255
0
                close(n->ofd);
256
0
                return CL_EWRITE;
257
0
            }
258
0
        } else {
259
0
            if ((ret = nsis_init(n)) != CL_SUCCESS) {
260
0
                cli_dbgmsg("NSIS: decompressor init failed"__AT__
261
0
                           "\n");
262
0
                close(n->ofd);
263
0
                return ret;
264
0
            }
265
266
0
            n->nsis.avail_in  = size;
267
0
            n->nsis.next_in   = (void *)ibuf;
268
0
            n->nsis.next_out  = obuf;
269
0
            n->nsis.avail_out = BUFSIZ;
270
0
            loops             = 0;
271
272
0
            while ((ret = nsis_decomp(n)) == CL_SUCCESS) {
273
0
                if ((size = n->nsis.next_out - obuf) > 0) {
274
0
                    gotsome = 1;
275
0
                    if (cli_writen(n->ofd, obuf, size) != size) {
276
0
                        cli_dbgmsg("NSIS: cannot write output file"__AT__
277
0
                                   "\n");
278
0
                        close(n->ofd);
279
0
                        nsis_shutdown(n);
280
0
                        return CL_EWRITE;
281
0
                    }
282
0
                    n->nsis.next_out  = obuf;
283
0
                    n->nsis.avail_out = BUFSIZ;
284
0
                    loops             = 0;
285
0
                    if ((ret = cli_checklimits("NSIS", ctx, size, 0, 0)) != CL_CLEAN) {
286
0
                        close(n->ofd);
287
0
                        nsis_shutdown(n);
288
0
                        return ret;
289
0
                    }
290
0
                } else if (++loops > 20) {
291
0
                    cli_dbgmsg("NSIS: xs looping, breaking out"__AT__
292
0
                               "\n");
293
0
                    ret = CL_EFORMAT;
294
0
                    break;
295
0
                }
296
0
            }
297
298
0
            nsis_shutdown(n);
299
300
0
            if ((n->nsis.next_out - obuf) > 0) {
301
0
                gotsome = 1;
302
0
                if (cli_writen(n->ofd, obuf, (size_t)(n->nsis.next_out - obuf)) != (size_t)(n->nsis.next_out - obuf)) {
303
0
                    cli_dbgmsg("NSIS: cannot write output file"__AT__
304
0
                               "\n");
305
0
                    close(n->ofd);
306
0
                    return CL_EWRITE;
307
0
                }
308
0
            }
309
310
0
            if (ret != CL_SUCCESS && ret != CL_BREAK) {
311
0
                cli_dbgmsg("NSIS: bad stream"__AT__
312
0
                           "\n");
313
0
                if (gotsome) {
314
0
                    ret = CL_SUCCESS;
315
0
                } else {
316
0
                    ret = CL_EMAXSIZE;
317
0
                    close(n->ofd);
318
0
                }
319
0
                return ret;
320
0
            }
321
0
        }
322
323
0
        return CL_SUCCESS;
324
325
0
    } else {
326
0
        if (!n->freeme) {
327
0
            if ((ret = nsis_init(n)) != CL_SUCCESS) {
328
0
                cli_dbgmsg("NSIS: decompressor init failed\n");
329
0
                return ret;
330
0
            }
331
0
            if (!(n->freeme = fmap_need_off_once(n->map, n->curpos, n->asz))) {
332
0
                cli_dbgmsg("NSIS: cannot read %u bytes"__AT__
333
0
                           "\n",
334
0
                           n->asz);
335
0
                return CL_EREAD;
336
0
            }
337
0
            n->nsis.next_in  = (void *)n->freeme;
338
0
            n->nsis.avail_in = n->asz;
339
0
        }
340
341
0
        if (n->nsis.avail_in <= 4) {
342
0
            cli_dbgmsg("NSIS: extraction complete\n");
343
0
            return CL_BREAK;
344
0
        }
345
0
        n->nsis.next_out  = obuf;
346
0
        n->nsis.avail_out = 4;
347
0
        loops             = 0;
348
349
0
        while ((ret = nsis_decomp(n)) == CL_SUCCESS) {
350
0
            if (n->nsis.next_out - obuf == 4) break;
351
0
            if (++loops > 20) {
352
0
                cli_dbgmsg("NSIS: xs looping, breaking out"__AT__
353
0
                           "\n");
354
0
                ret = CL_BREAK;
355
0
                break;
356
0
            }
357
0
        }
358
359
0
        if (ret != CL_SUCCESS) {
360
0
            cli_dbgmsg("NSIS: bad stream"__AT__
361
0
                       "\n");
362
0
            return CL_EFORMAT;
363
0
        }
364
365
0
        size = cli_readint32(obuf);
366
0
        if ((ret = cli_checklimits("NSIS", ctx, size, 0, 0)) != CL_CLEAN) {
367
0
            return ret;
368
0
        }
369
370
0
        if (size == 0) {
371
0
            cli_dbgmsg("NSIS: Empty file found.\n");
372
0
            return CL_SUCCESS;
373
0
        }
374
375
0
        n->nsis.next_out  = obuf;
376
0
        n->nsis.avail_out = MIN(BUFSIZ, size);
377
0
        loops             = 0;
378
379
0
        if ((n->ofd = open(n->ofn, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0600)) == -1) {
380
0
            cli_errmsg("NSIS: unable to create output file %s - aborting.", n->ofn);
381
0
            return CL_ECREAT;
382
0
        }
383
0
        n->opened = 1;
384
385
0
        while (size && (ret = nsis_decomp(n)) == CL_SUCCESS) {
386
0
            unsigned int wsz;
387
0
            if ((wsz = n->nsis.next_out - obuf) > 0) {
388
0
                gotsome = 1;
389
0
                if (cli_writen(n->ofd, obuf, wsz) != wsz) {
390
0
                    cli_dbgmsg("NSIS: cannot write output file"__AT__
391
0
                               "\n");
392
0
                    close(n->ofd);
393
0
                    return CL_EWRITE;
394
0
                }
395
0
                size -= wsz;
396
0
                loops             = 0;
397
0
                n->nsis.next_out  = obuf;
398
0
                n->nsis.avail_out = MIN(size, BUFSIZ);
399
0
            } else if (++loops > 20) {
400
0
                cli_dbgmsg("NSIS: xs looping, breaking out"__AT__
401
0
                           "\n");
402
0
                ret = CL_EFORMAT;
403
0
                break;
404
0
            }
405
0
        }
406
407
0
        if ((n->nsis.next_out - obuf) > 0) {
408
0
            gotsome = 1;
409
0
            if (cli_writen(n->ofd, obuf, (size_t)(n->nsis.next_out - obuf)) != (size_t)(n->nsis.next_out - obuf)) {
410
0
                cli_dbgmsg("NSIS: cannot write output file"__AT__
411
0
                           "\n");
412
0
                close(n->ofd);
413
0
                return CL_EWRITE;
414
0
            }
415
0
        }
416
417
0
        if (ret == CL_EFORMAT) {
418
0
            cli_dbgmsg("NSIS: bad stream"__AT__
419
0
                       "\n");
420
0
            if (!gotsome) {
421
0
                close(n->ofd);
422
0
                return CL_EMAXSIZE;
423
0
            }
424
0
        }
425
426
0
        if (ret == CL_EFORMAT || ret == CL_BREAK) {
427
0
            n->eof = 1;
428
0
        } else if (ret != CL_SUCCESS) {
429
0
            cli_dbgmsg("NSIS: bad stream"__AT__
430
0
                       "\n");
431
0
            close(n->ofd);
432
0
            return CL_EFORMAT;
433
0
        }
434
0
        return CL_SUCCESS;
435
0
    }
436
0
}
437
438
static uint8_t nsis_detcomp(const char *b)
439
0
{
440
0
    if (*b == '1') return COMP_BZIP2;
441
0
    if ((cli_readint32(b) & ~0x80000000) == 0x5d) return COMP_LZMA;
442
0
    return COMP_ZLIB;
443
0
}
444
445
static int nsis_headers(struct nsis_st *n, cli_ctx *ctx)
446
0
{
447
0
    const char *buf;
448
0
    uint32_t pos;
449
0
    int i;
450
0
    uint8_t comps[] = {0, 0, 0, 0}, trunc = 0;
451
452
0
    if (!(buf = fmap_need_off_once(n->map, n->off, 0x1c)))
453
0
        return CL_EREAD;
454
455
0
    n->hsz    = (uint32_t)cli_readint32(buf + 0x14);
456
0
    n->asz    = (uint32_t)cli_readint32(buf + 0x18);
457
0
    n->fullsz = n->map->len;
458
459
0
    cli_dbgmsg("NSIS: Header info - Flags=%x, Header size=%x, Archive size=%x\n", cli_readint32(buf), n->hsz, n->asz);
460
461
0
    if (n->fullsz - n->off < (off_t)n->asz) {
462
0
        cli_dbgmsg("NSIS: Possibly truncated file\n");
463
0
        n->asz = n->fullsz - n->off;
464
0
        trunc++;
465
0
    } else if (n->fullsz - n->off != (off_t)n->asz) {
466
0
        cli_dbgmsg("NSIS: Overlays found\n");
467
0
    }
468
469
0
    n->asz -= 0x1c;
470
0
    buf += 0x1c;
471
472
    /* Guess if solid */
473
0
    for (i = 0, pos = 0; pos < n->asz - 4; i++) {
474
0
        int32_t nextsz;
475
0
        if (!(buf = fmap_need_ptr_once(n->map, (void *)buf, 4))) return CL_EREAD;
476
0
        nextsz = cli_readint32(buf);
477
0
        if (!i) n->comp = nsis_detcomp(buf);
478
0
        buf += 4;
479
0
        if (nextsz & 0x80000000) {
480
0
            nextsz &= ~0x80000000;
481
0
            if (!(buf = fmap_need_ptr_once(n->map, (void *)buf, 4))) return CL_EREAD;
482
0
            comps[nsis_detcomp(buf)]++;
483
0
            nextsz -= 4;
484
0
            pos += 4;
485
0
            buf += 4;
486
0
        }
487
0
        if ((pos += (4 + ((uint32_t)nextsz))) > n->asz) {
488
0
            n->solid = 1;
489
0
            break;
490
0
        }
491
492
0
        buf += nextsz;
493
0
    }
494
495
0
    if (trunc && i >= 2) n->solid = 0;
496
497
0
    cli_dbgmsg("NSIS: solid compression%s detected\n", (n->solid) ? "" : " not");
498
499
    /* Guess the compression method */
500
0
    if (!n->solid) {
501
0
        cli_dbgmsg("NSIS: bzip2 %u - lzma %u - zlib %u\n", comps[1], comps[2], comps[3]);
502
0
        n->comp = (comps[1] < comps[2]) ? (comps[2] < comps[3] ? COMP_ZLIB : COMP_LZMA) : (comps[1] < comps[3] ? COMP_ZLIB : COMP_BZIP2);
503
0
    }
504
505
0
    n->curpos = n->off + 0x1c;
506
0
    return nsis_unpack_next(n, ctx);
507
0
}
508
509
static int cli_nsis_unpack(struct nsis_st *n, cli_ctx *ctx)
510
0
{
511
0
    return (n->fno) ? nsis_unpack_next(n, ctx) : nsis_headers(n, ctx);
512
0
}
513
514
int cli_scannulsft(cli_ctx *ctx, off_t offset)
515
0
{
516
0
    int ret;
517
0
    struct nsis_st nsist;
518
519
0
    cli_dbgmsg("in scannulsft()\n");
520
521
0
    memset(&nsist, 0, sizeof(struct nsis_st));
522
523
0
    nsist.off = offset;
524
0
    if (!(nsist.dir = cli_gentemp_with_prefix(ctx->sub_tmpdir, "nulsft-tmp")))
525
0
        return CL_ETMPDIR;
526
0
    if (mkdir(nsist.dir, 0700)) {
527
0
        cli_dbgmsg("NSIS: Can't create temporary directory %s\n", nsist.dir);
528
0
        free(nsist.dir);
529
0
        return CL_ETMPDIR;
530
0
    }
531
532
0
    nsist.map = ctx->fmap;
533
0
    if (ctx->engine->keeptmp) cli_dbgmsg("NSIS: Extracting files to %s\n", nsist.dir);
534
535
0
    do {
536
0
        ret = cli_nsis_unpack(&nsist, ctx);
537
0
        if (ret == CL_SUCCESS && nsist.opened == 0) {
538
            /* Don't scan a non-existent file */
539
0
            continue;
540
0
        }
541
0
        if (ret == CL_SUCCESS) {
542
0
            cli_dbgmsg("NSIS: Successfully extracted file #%u\n", nsist.fno);
543
0
            if (lseek(nsist.ofd, 0, SEEK_SET) == -1) {
544
0
                cli_dbgmsg("NSIS: call to lseek() failed\n");
545
0
                free(nsist.dir);
546
0
                return CL_ESEEK;
547
0
            }
548
0
            if (nsist.fno == 1) {
549
0
                ret = cli_scan_desc(nsist.ofd, ctx, CL_TYPE_ANY, false, NULL, AC_SCAN_VIR, NULL, NULL, LAYER_ATTRIBUTES_NONE); /// TODO: Extract file names
550
0
            } else {
551
0
                ret = cli_magic_scan_desc(nsist.ofd, nsist.ofn, ctx, NULL, LAYER_ATTRIBUTES_NONE); /// TODO: Extract file names
552
0
            }
553
0
            close(nsist.ofd);
554
0
            if (!ctx->engine->keeptmp) {
555
0
                if (cli_unlink(nsist.ofn)) {
556
0
                    ret = CL_EUNLINK;
557
0
                }
558
0
            }
559
0
        } else if (ret == CL_EMAXSIZE) {
560
0
            ret = nsist.solid ? CL_BREAK : CL_SUCCESS;
561
0
        }
562
0
    } while (ret == CL_SUCCESS);
563
564
0
    if (ret == CL_BREAK)
565
0
        ret = CL_CLEAN;
566
567
0
    nsis_shutdown(&nsist);
568
569
0
    if (!ctx->engine->keeptmp) {
570
0
        cli_rmdirs(nsist.dir);
571
0
    }
572
573
0
    free(nsist.dir);
574
575
0
    return ret;
576
0
}