2
* Copyright (C) 2007-2008 Sourcefire, Inc.
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.
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.
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,
22
#include "clamav-config.h"
28
#include <sys/types.h>
41
#define EC32(x) cli_readint32(&x) /* Convert little endian to host */
42
#define EC16(x) cli_readint16(&x)
45
#define CAB_FOLDER_LIMIT 5000
46
#define CAB_FILE_LIMIT 5000
48
/* Cabinet format data structures */
51
uint32_t signature; /* file signature */
52
uint32_t res1; /* reserved */
53
uint32_t cbCabinet; /* size of cabinet file */
54
uint32_t res2; /* reserved */
55
uint32_t coffFiles; /* offset of the first file entry */
56
uint32_t res3; /* reserved */
57
uint8_t versionMinor; /* file format version, minor */
58
uint8_t versionMajor; /* file format version, major */
59
uint16_t cFolders; /* number of folder entries */
60
uint16_t cFiles; /* number of file entries */
61
uint16_t flags; /* option flags */
62
uint16_t setID; /* multiple cabs related */
63
uint16_t iCabinet; /* multiple cabs related */
67
uint16_t cbCFHeader; /* size of reserved header area */
68
uint8_t cbCFFolder; /* size of reserved folder area */
69
uint8_t cbCFData; /* size of reserved block area */
74
uint32_t coffCabStart; /* offset of the first data block */
75
uint16_t cCFData; /* number of data blocks */
76
uint16_t typeCompress; /* compression type */
81
uint32_t cbFile; /* uncompressed size */
82
uint32_t uoffFolderStart; /* uncompressed offset of file in folder */
83
uint16_t iFolder; /* folder index */
84
uint16_t date; /* date stamp */
85
uint16_t time; /* time stamp */
86
uint16_t attribs; /* attribute flags */
91
uint32_t csum; /* data block checksum */
92
uint16_t cbData; /* number of compressed bytes */
93
uint16_t cbUncomp; /* number of uncompressed bytes */
96
static char *cab_readstr(fmap_t *map, off_t *offset, int *ret)
102
if(!(str = fmap_need_offstr(map, *offset, 256))) {
109
fmap_unneed_ptr(map, str, i);
115
if((retstr = cli_malloc(i)))
116
memcpy(retstr, str, i);
117
fmap_unneed_ptr(map, str, i);
128
static int cab_chkname(char *name, int san)
130
size_t i, len = strlen(name);
133
for(i = 0; i < len; i++) {
134
if(!san && (strchr("%/*?|\\\"+=<>;:\t ", name[i]) || !isascii(name[i]))) {
135
cli_dbgmsg("cab_chkname: File name contains disallowed characters\n");
137
} else if(san && !isalnum(name[i])) {
145
void cab_free(struct cab_archive *cab)
147
struct cab_folder *folder;
148
struct cab_file *file;
152
if(cab->state->stream) {
153
switch(cab->state->cmethod & 0x000f) {
155
mszip_free(cab->state->stream);
158
qtm_free(cab->state->stream);
161
lzx_free(cab->state->stream);
167
while(cab->folders) {
168
folder = cab->folders;
169
cab->folders = cab->folders->next;
175
cab->files = cab->files->next;
181
int cab_open(fmap_t *map, off_t offset, struct cab_archive *cab)
183
unsigned int i, folders = 0;
184
struct cab_file *file, *lfile = NULL;
185
struct cab_folder *folder, *lfolder = NULL;
186
const struct cab_hdr *hdr;
187
const struct cab_hdr_opt *hdr_opt;
192
off_t resfold = 0, rsize, cur_offset = offset;
194
if(!(hdr=fmap_need_off_once(map, cur_offset, sizeof(*hdr)))) {
195
cli_dbgmsg("cab_open: Can't read cabinet header\n");
196
return CL_EFORMAT; /* most likely a corrupted file */
198
cur_offset += sizeof(*hdr);
200
if(EC32(hdr->signature) != 0x4643534d) {
201
cli_dbgmsg("cab_open: Incorrect CAB signature\n");
204
cli_dbgmsg("CAB: -------------- Cabinet file ----------------\n");
209
memset(cab, 0, sizeof(struct cab_archive));
211
cab->length = EC32(hdr->cbCabinet);
212
cli_dbgmsg("CAB: Cabinet length: %u\n", cab->length);
213
if((off_t) cab->length > rsize) {
214
cli_dbgmsg("CAB: Truncating file size from %lu to %lu\n", (unsigned long int) cab->length, (unsigned long int) rsize);
215
cab->length = (uint32_t) rsize;
218
cab->nfolders = EC16(hdr->cFolders);
220
cli_dbgmsg("cab_open: No folders in cabinet (fake cab?)\n");
223
cli_dbgmsg("CAB: Folders: %u\n", cab->nfolders);
224
if(cab->nfolders > CAB_FOLDER_LIMIT) {
225
cab->nfolders = CAB_FOLDER_LIMIT;
226
cli_dbgmsg("CAB: *** Number of folders limited to %u ***\n", cab->nfolders);
230
cab->nfiles = EC16(hdr->cFiles);
232
cli_dbgmsg("cab_open: No files in cabinet (fake cab?)\n");
235
cli_dbgmsg("CAB: Files: %u\n", cab->nfiles);
236
if(cab->nfiles > CAB_FILE_LIMIT) {
237
cab->nfiles = CAB_FILE_LIMIT;
238
cli_dbgmsg("CAB: *** Number of files limited to %u ***\n", cab->nfiles);
242
cli_dbgmsg("CAB: File format version: %u.%u\n", hdr->versionMajor, hdr->versionMinor);
244
cab->flags = EC16(hdr->flags);
245
coffFiles = EC16(hdr->coffFiles);
247
if(cab->flags & 0x0004) {
248
if(!(hdr_opt = fmap_need_off_once(map, cur_offset, sizeof(*hdr_opt)))) {
249
cli_dbgmsg("cab_open: Can't read file header (fake cab?)\n");
250
return CL_EFORMAT; /* most likely a corrupted file */
253
cab->reshdr = EC16(hdr_opt->cbCFHeader);
254
resfold = hdr_opt->cbCFFolder;
255
cab->resdata = hdr_opt->cbCFData;
257
cur_offset += sizeof(*hdr_opt) + cab->reshdr;
259
if(cab->reshdr >= rsize) {
260
cli_dbgmsg("cab_open: Can't lseek to %u (fake cab?)\n", cab->reshdr);
261
return CL_EFORMAT; /* most likely a corrupted file */
266
if(cab->flags & 0x0001) { /* preceding cabinet */
268
pt = cab_readstr(map, &cur_offset, &ret);
271
if(cab_chkname(pt, 0))
272
cli_dbgmsg("CAB: Invalid name of preceding cabinet\n");
274
cli_dbgmsg("CAB: Preceding cabinet name: %s\n", pt);
277
pt = cab_readstr(map, &cur_offset, &ret);
280
if(cab_chkname(pt, 0))
281
cli_dbgmsg("CAB: Invalid info for preceding cabinet\n");
283
cli_dbgmsg("CAB: Preceding cabinet info: %s\n", pt);
287
if(cab->flags & 0x0002) { /* next cabinet */
289
pt = cab_readstr(map, &cur_offset, &ret);
292
if(cab_chkname(pt, 0))
293
cli_dbgmsg("CAB: Invalid name of next cabinet\n");
295
cli_dbgmsg("CAB: Next cabinet name: %s\n", pt);
298
pt = cab_readstr(map, &cur_offset, &ret);
301
if(cab_chkname(pt, 0))
302
cli_dbgmsg("CAB: Invalid info for next cabinet\n");
304
cli_dbgmsg("CAB: Next cabinet info: %s\n", pt);
309
for(i = 0; i < cab->nfolders; i++) {
310
const struct cab_folder_hdr *folder_hdr;
312
if(!(folder_hdr = fmap_need_off_once(map, cur_offset, sizeof(*folder_hdr)))) {
313
cli_dbgmsg("cab_open: Can't read header for folder %u\n", i);
317
cur_offset += sizeof(*folder_hdr) + resfold;
319
if(EC32(folder_hdr->coffCabStart) + offset > rsize) {
320
cli_dbgmsg("CAB: Folder out of file\n");
324
if((EC16(folder_hdr->typeCompress) & 0x000f) > 3) {
325
cli_dbgmsg("CAB: Unknown compression method\n");
329
folder = (struct cab_folder *) cli_calloc(1, sizeof(struct cab_folder));
331
cli_errmsg("cab_open: Can't allocate memory for folder\n");
336
folder->cab = (struct cab_archive *) cab;
337
folder->offset = (off_t) EC32(folder_hdr->coffCabStart) + offset;
338
folder->nblocks = EC16(folder_hdr->cCFData);
339
folder->cmethod = EC16(folder_hdr->typeCompress);
341
cli_dbgmsg("CAB: Folder record %u\n", i);
342
cli_dbgmsg("CAB: Folder offset: %u\n", (unsigned int) folder->offset);
343
cli_dbgmsg("CAB: Folder compression method: %d\n", folder->cmethod);
346
cab->folders = folder;
348
lfolder->next = folder;
353
cli_dbgmsg("CAB: Recorded folders: %u\n", folders);
356
if(cab->nfolders != folders) {
357
if(coffFiles >= rsize) {
358
cli_dbgmsg("cab_open: Can't lseek to hdr.coffFiles\n");
362
cur_offset = coffFiles;
364
for(i = 0; i < cab->nfiles; i++) {
365
const struct cab_file_hdr *file_hdr;
367
if(!(file_hdr = fmap_need_off_once(map, cur_offset, sizeof(*file_hdr)))) {
368
cli_dbgmsg("cab_open: Can't read file %u header\n", i);
371
cur_offset += sizeof(*file_hdr);
373
file = (struct cab_file *) cli_calloc(1, sizeof(struct cab_file));
375
cli_errmsg("cab_open: Can't allocate memory for file\n");
382
file->offset = EC32(file_hdr->uoffFolderStart);
383
file->length = EC32(file_hdr->cbFile);
384
file->attribs = EC16(file_hdr->attribs);
385
fidx = EC16(file_hdr->iFolder);
386
file->error = CL_SUCCESS;
388
file->name = cab_readstr(map, &cur_offset, &ret);
393
cab_chkname(file->name, 1);
395
cli_dbgmsg("CAB: File record %u\n", i);
396
cli_dbgmsg("CAB: File name: %s\n", file->name);
397
cli_dbgmsg("CAB: File offset: %u\n", (unsigned int) file->offset);
398
cli_dbgmsg("CAB: File folder index: %u\n", fidx);
399
cli_dbgmsg("CAB: File attribs: 0x%x\n", file->attribs);
400
if(file->attribs & 0x01)
401
cli_dbgmsg("CAB: * file is read-only\n");
402
if(file->attribs & 0x02)
403
cli_dbgmsg("CAB: * file is hidden\n");
404
if(file->attribs & 0x04)
405
cli_dbgmsg("CAB: * file is a system file\n");
406
if(file->attribs & 0x20)
407
cli_dbgmsg("CAB: * file modified since last backup\n");
408
if(file->attribs & 0x40)
409
cli_dbgmsg("CAB: * file to be run after extraction\n");
410
if(file->attribs & 0x80)
411
cli_dbgmsg("CAB: * file name contains UTF\n");
415
if(fidx > cab->nfolders) {
416
cli_dbgmsg("cab_open: File %s is not associated with any folder\n", file->name);
422
file->folder = cab->folders;
423
while(file->folder && fidx--)
424
file->folder = file->folder->next;
427
cli_dbgmsg("cab_open: Folder not found for file %s\n", file->name);
434
cli_dbgmsg("CAB: File is split *skipping*\n");
452
static int cab_read_block(struct cab_file *file)
454
const struct cab_block_hdr *block_hdr;
455
struct cab_state *state = file->cab->state;
457
if(!(block_hdr = fmap_need_off_once(file->cab->map, file->cab->cur_offset, sizeof(*block_hdr)))) {
458
cli_dbgmsg("cab_read_block: Can't read block header\n");
459
return CL_EFORMAT; /* most likely a corrupted file */
462
file->cab->cur_offset += sizeof(*block_hdr) + file->cab->resdata;
463
state->blklen = EC16(block_hdr->cbData);
464
state->outlen = EC16(block_hdr->cbUncomp);
466
if(fmap_readn(file->cab->map, state->block, file->cab->cur_offset, state->blklen) != state->blklen) {
467
cli_dbgmsg("cab_read_block: Can't read block data\n");
468
return CL_EFORMAT; /* most likely a corrupted file */
471
file->cab->cur_offset += state->blklen;
472
state->pt = state->end = state->block;
473
state->end += state->blklen;
478
static int cab_read(struct cab_file *file, unsigned char *buffer, int bytes)
483
if((file->cab->state->blknum > file->folder->nblocks) && !file->lread) {
484
file->error = CL_BREAK;
490
left = file->cab->state->end - file->cab->state->pt;
496
memcpy(buffer, file->cab->state->pt, left);
497
file->cab->state->pt += left;
502
if(file->cab->state->blknum++ >= file->folder->nblocks)
505
file->error = cab_read_block(file);
509
if((file->folder->cmethod & 0x000f) == 0x0002) /* Quantum hack */
510
*file->cab->state->end++ = 0xff;
512
if(file->cab->state->blknum >= file->folder->nblocks) {
513
if((file->folder->cmethod & 0x000f) == 0x0003) { /* LZX hack */
514
lzx_set_output_length(file->cab->state->stream, (off_t) ((file->cab->state->blknum - 1) * 32768 + file->cab->state->outlen));
517
if(file->cab->state->outlen != 32768) {
518
cli_dbgmsg("cab_read: WARNING: partial data block\n");
524
return file->lread = bytes - todo;
527
static int cab_unstore(struct cab_file *file)
529
int todo, bread, bytes = file->length;
530
unsigned char buff[4096];
534
cli_dbgmsg("cab_unstore: bytes < 0\n");
538
todo = MIN((unsigned int) bytes, file->max_size);
542
if((unsigned int) todo <= sizeof(buff))
545
bread = sizeof(buff);
547
if((bread = cab_read(file, buff, bread)) == -1) {
548
cli_dbgmsg("cab_unstore: cab_read failed\n");
550
} else if(cli_writen(file->ofd, buff, bread) != bread) {
551
cli_warnmsg("cab_unstore: Can't write %d bytes to descriptor %d\n", bread, file->ofd);
557
if(!bread || todo <= 0)
564
#define CAB_CHGFOLDER \
565
if(!file->cab->actfol || (file->folder != file->cab->actfol) \
566
|| (file->cab->state && file->cab->state->cmethod != file->folder->cmethod)) { \
567
if(file->cab->state) { \
568
if(file->cab->state->stream) { \
569
switch(file->cab->state->cmethod & 0x000f) { \
571
mszip_free(file->cab->state->stream); \
574
qtm_free(file->cab->state->stream); \
577
lzx_free(file->cab->state->stream); \
580
free(file->cab->state); \
581
file->cab->state = NULL; \
583
file->cab->cur_offset = file->folder->offset; \
584
file->cab->state = (struct cab_state *) cli_calloc(1, sizeof(struct cab_state)); \
585
if(!file->cab->state) { \
586
cli_errmsg("cab_extract: Can't allocate memory for internal state\n"); \
590
file->cab->state->cmethod = file->folder->cmethod; \
591
switch(file->folder->cmethod & 0x000f) { \
593
file->cab->state->stream = (struct mszip_stream *) mszip_init(file->ofd, 4096, 1, file, &cab_read); \
596
file->cab->state->stream = (struct qtm_stream *) qtm_init(file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 4096, file, &cab_read); \
599
file->cab->state->stream = (struct lzx_stream *) lzx_init(file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 0, 4096, 0, file, &cab_read); \
601
if((file->folder->cmethod & 0x000f) && !file->cab->state->stream) { \
605
file->cab->actfol = file->folder; \
607
if(file->cab->state && file->cab->state->stream) { \
608
switch(file->cab->state->cmethod & 0x000f) { \
610
((struct mszip_stream *) file->cab->state->stream)->ofd = file->ofd; \
613
((struct qtm_stream *) file->cab->state->stream)->ofd = file->ofd; \
616
((struct lzx_stream *) file->cab->state->stream)->ofd = file->ofd; \
623
int cab_extract(struct cab_file *file, const char *name)
629
cli_errmsg("cab_extract: !file || !name\n");
634
cli_errmsg("cab_extract: file->folder == NULL\n");
638
file->ofd = open(name, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU);
639
if(file->ofd == -1) {
640
cli_errmsg("cab_extract: Can't open file %s in write mode\n", name);
644
switch(file->folder->cmethod & 0x000f) {
645
case 0x0000: /* STORE */
646
cli_dbgmsg("CAB: Compression method: STORED\n");
648
if(file->length > file->cab->length) {
649
cli_dbgmsg("cab_extract: Stored file larger than archive itself, trimming down\n");
650
file->length = file->cab->length;
652
ret = cab_unstore(file);
655
case 0x0001: /* MSZIP */
656
cli_dbgmsg("CAB: Compression method: MSZIP\n");
658
ret = mszip_decompress(file->cab->state->stream, file->length);
661
case 0x0002: /* QUANTUM */
662
cli_dbgmsg("CAB: Compression method: QUANTUM\n");
664
ret = qtm_decompress(file->cab->state->stream, file->length);
667
case 0x0003: /* LZX */
668
cli_dbgmsg("CAB: Compression method: LZX\n");
670
ret = lzx_decompress(file->cab->state->stream, file->length);
674
cli_dbgmsg("CAB: Not supported compression method: 0x%x\n", file->folder->cmethod & 0x000f);