libclamav/ole2_extract.c
47bbbc56
 /*
  *  Extract component parts of OLE2 files (e.g. MS Office Documents)
  *
2023340a
  *  Copyright (C) 2007-2008 Sourcefire, Inc.
  *
  *  Authors: Trog
47bbbc56
  *
  *  This program is free software; you can redistribute it and/or modify
2023340a
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
47bbbc56
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
48b7b4a7
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
47bbbc56
  */
 
6d6e8271
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
47bbbc56
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <string.h>
b58fdfc2
 #ifdef	HAVE_UNISTD_H
47bbbc56
 #include <unistd.h>
b58fdfc2
 #endif
47bbbc56
 #include <ctype.h>
 #include <stdlib.h>
b58fdfc2
 #include "clamav.h"
47bbbc56
 
8c030302
 #if HAVE_MMAP
 #if HAVE_SYS_MMAN_H
 #include <sys/mman.h>
 #else /* HAVE_SYS_MMAN_H */
 #undef HAVE_MMAP
 #endif
 #endif
 
f893c0f3
 #include "cltypes.h"
1dc96b6c
 #include "others.h"
079229d6
 #include "ole2_extract.h"
72ce4b70
 #include "scanners.h"
 #include "hashtab.h"
98a3c4ef
 
11b50569
 #include "mbox.h"
 
22af56a3
 #define ole2_endian_convert_16(v) le16_to_host((uint16_t)(v))
 #define ole2_endian_convert_32(v) le32_to_host((uint32_t)(v))
c83b8845
 
1dc96b6c
 #ifndef HAVE_ATTRIB_PACKED
 #define __attribute__(x)
 #endif
 
 #ifdef HAVE_PRAGMA_PACK
 #pragma pack(1)
 #endif
 
ef38c83d
 #ifdef HAVE_PRAGMA_PACK_HPPA
 #pragma pack 1
 #endif
 
b58fdfc2
 #ifndef	O_BINARY
 #define	O_BINARY	0
 #endif
 
72ce4b70
 
47bbbc56
 typedef struct ole2_header_tag
 {
bda5598b
 	unsigned char magic[8];		/* should be: 0xd0cf11e0a1b11ae1 */
 	unsigned char clsid[16];
e0f90556
 	uint16_t minor_version __attribute__ ((packed));
 	uint16_t dll_version __attribute__ ((packed));
 	int16_t byte_order __attribute__ ((packed));			/* -2=intel */
47bbbc56
 
e0f90556
 	uint16_t log2_big_block_size __attribute__ ((packed));		/* usually 9 (2^9 = 512) */
ec981799
 	uint32_t log2_small_block_size __attribute__ ((packed));	/* usually 6 (2^6 = 64) */
47bbbc56
 
e0f90556
 	int32_t reserved[2] __attribute__ ((packed));
 	int32_t bat_count __attribute__ ((packed));
 	int32_t prop_start __attribute__ ((packed));
47bbbc56
 
e0f90556
 	uint32_t signature __attribute__ ((packed));
 	uint32_t sbat_cutoff __attribute__ ((packed));			/* cutoff for files held in small blocks (4096) */
47bbbc56
 
e0f90556
 	int32_t sbat_start __attribute__ ((packed));
 	int32_t sbat_block_count __attribute__ ((packed));
 	int32_t xbat_start __attribute__ ((packed));
 	int32_t xbat_count __attribute__ ((packed));
 	int32_t bat_array[109] __attribute__ ((packed));
349e0502
 
 	/* not part of the ole2 header, but stuff we need in order to decode */
 	/* must take account of the size of variables below here when
 	   reading the header */
e0f90556
 	int32_t sbat_root_start __attribute__ ((packed));
b5231f5f
 	uint32_t max_block_no;
8c030302
 	unsigned char *m_area;
 	off_t m_length;
8a9c2d19
 	bitset_t *bitset;
72ce4b70
 	struct uniq *U;
 	int has_vba;
e0f90556
 } ole2_header_t;
47bbbc56
 
72ce4b70
 
47bbbc56
 typedef struct property_tag
 {
bda5598b
 	char name[64];		/* in unicode */
f8c58685
 	uint16_t name_size __attribute__ ((packed));
bda5598b
 	unsigned char type;		/* 1=dir 2=file 5=root */
 	unsigned char color;		/* black or red */
f8c58685
 	uint32_t prev __attribute__ ((packed));
 	uint32_t next __attribute__ ((packed));
 	uint32_t child __attribute__ ((packed));
e0f90556
 
bda5598b
 	unsigned char clsid[16];
e0f90556
 	uint32_t user_flags __attribute__ ((packed));
 
 	uint32_t create_lowdate __attribute__ ((packed));
 	uint32_t create_highdate __attribute__ ((packed));
 	uint32_t mod_lowdate __attribute__ ((packed));
 	uint32_t mod_highdate __attribute__ ((packed));
f8c58685
 	uint32_t start_block __attribute__ ((packed));
 	uint32_t size __attribute__ ((packed));
bda5598b
 	unsigned char reserved[4];
e0f90556
 } property_t;
47bbbc56
 
1dc96b6c
 #ifdef HAVE_PRAGMA_PACK
 #pragma pack()
 #endif
 
ef38c83d
 #ifdef HAVE_PRAGMA_PACK_HPPA
 #pragma pack
 #endif
 
bda5598b
 static unsigned char magic_id[] = { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1};
47bbbc56
 
72ce4b70
 
 static char *get_property_name2(char *name, int size)
47bbbc56
 {
 	int i, j;
 	char *newname;
 
f5646d91
 	if (*name == 0 || size <= 0 || size > 64) {
47bbbc56
 		return NULL;
 	}
 
9e7e2c76
 	newname = (char *) cli_malloc(size*7);
47bbbc56
 	if (!newname) {
 		return NULL;
 	}
 	j=0;
 	/* size-2 to ignore trailing NULL */
 	for (i=0 ; i < size-2; i+=2) {
b58fdfc2
 		if((!(name[i]&0x80)) && isprint(name[i])) {
72ce4b70
 		        newname[j++] = tolower(name[i]);
47bbbc56
 		} else {
 			if (name[i] < 10 && name[i] >= 0) {
 				newname[j++] = '_';
 				newname[j++] = name[i] + '0';
 			}
9e7e2c76
 			else {
 				const uint16_t x = (((uint16_t)name[i]) << 8) | name[i+1];
 				newname[j++] = '_';
 				newname[j++] = 'a'+((x&0xF));
 				newname[j++] = 'a'+((x>>4)&0xF);
 				newname[j++] = 'a'+((x>>8)&0xF);
 				newname[j++] = 'a'+((x>>16)&0xF);
 				newname[j++] = 'a'+((x>>24)&0xF);
 			}
47bbbc56
 			newname[j++] = '_';
 		}
 	}
 	newname[j] = '\0';
349e0502
 	if (strlen(newname) == 0) {
 		free(newname);
 		return NULL;
 	}
47bbbc56
 	return newname;
 }
e0f90556
 
72ce4b70
 static char *get_property_name(char *name, int size) {
   const char *carray = "0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz._";
   int csize = size>>1;
   char *newname, *cname;
   char *oname = name;
 
77fd59a8
   if (csize<=0) return NULL;
72ce4b70
 
   newname = cname = (char *)cli_malloc(size);
   if (!newname) return NULL;
 
   while(--csize) {
     uint16_t lo, hi, u=cli_readint16(oname)-0x3800;
     oname+=2;
     if (u > 0x1040) {
       free(newname);
       return get_property_name2(name, size);
     }
     lo = u % 64;
     u >>= 6;
     hi = u % 64;
     *cname++=carray[lo];
     if(csize!=1 || u!= 64) *cname++=carray[hi];
   }
   *cname='\0';
   return newname;
d95b7372
 }
47bbbc56
 
72ce4b70
 
e0f90556
 static void print_ole2_property(property_t *property)
47bbbc56
 {
72ce4b70
 	char spam[128], *buf;
d95b7372
 	if (property->name_size > 64) {
                 cli_dbgmsg("[err name len: %d]\n", property->name_size);
                 return;
         }
72ce4b70
 	buf = get_property_name(property->name, property->name_size);
 	snprintf(spam, sizeof(spam), "OLE2: %s ", buf ? buf : "<noname>");
 	spam[sizeof(spam)-1]='\0';
 	if (buf) free(buf);
47bbbc56
 	switch (property->type) {
 	case 2:
72ce4b70
 		strncat(spam, " [file] ", sizeof(spam) - 1 - strlen(spam));
47bbbc56
 		break;
 	case 1:
72ce4b70
 		strncat(spam, " [dir ] ", sizeof(spam) - 1 - strlen(spam));
47bbbc56
 		break;
 	case 5:
72ce4b70
 		strncat(spam, " [root] ", sizeof(spam) - 1 - strlen(spam));
47bbbc56
 		break;
 	default:
72ce4b70
 		strncat(spam, " [unkn] ", sizeof(spam) - 1 - strlen(spam));
47bbbc56
 	}
72ce4b70
 	spam[sizeof(spam)-1]='\0';
47bbbc56
 	switch (property->color) {
 	case 0:
72ce4b70
 		strncat(spam, " r  ", sizeof(spam) - 1 - strlen(spam));
47bbbc56
 		break;
 	case 1:
72ce4b70
 		strncat(spam, " b  ", sizeof(spam) - 1 - strlen(spam));
47bbbc56
 		break;
 	default:
72ce4b70
 		strncat(spam, " u  ", sizeof(spam) - 1 - strlen(spam));
47bbbc56
 	}
72ce4b70
 	spam[sizeof(spam)-1]='\0';
 	cli_dbgmsg("%s size:0x%.8x flags:0x%.8x\n", spam, property->size, property->user_flags);
47bbbc56
 }
 
e0f90556
 static void print_ole2_header(ole2_header_t *hdr)
47bbbc56
 {
 	int i;
 	
 	if (!hdr) {
 		return;
 	}
 	
 	cli_dbgmsg("\nMagic:\t\t\t0x");
 	for (i=0 ; i<8; i++) {
 		cli_dbgmsg("%x", hdr->magic[i]);
 	}
 	cli_dbgmsg("\n");
 
 	cli_dbgmsg("CLSID:\t\t\t{");
 	for (i=0 ; i<16; i++) {
 		cli_dbgmsg("%x ", hdr->clsid[i]);
 	}
 	cli_dbgmsg("}\n");
 
 	cli_dbgmsg("Minor version:\t\t0x%x\n", hdr->minor_version);
 	cli_dbgmsg("DLL version:\t\t0x%x\n", hdr->dll_version);
 	cli_dbgmsg("Byte Order:\t\t%d\n", hdr->byte_order);
 	cli_dbgmsg("Big Block Size:\t\t%i\n", hdr->log2_big_block_size);
 	cli_dbgmsg("Small Block Size:\t%i\n", hdr->log2_small_block_size);
 	cli_dbgmsg("BAT count:\t\t%d\n", hdr->bat_count);
 	cli_dbgmsg("Prop start:\t\t%d\n", hdr->prop_start);
 	cli_dbgmsg("SBAT cutoff:\t\t%d\n", hdr->sbat_cutoff);
 	cli_dbgmsg("SBat start:\t\t%d\n", hdr->sbat_start);
 	cli_dbgmsg("SBat block count:\t%d\n", hdr->sbat_block_count);
 	cli_dbgmsg("XBat start:\t\t%d\n", hdr->xbat_start);
 	cli_dbgmsg("XBat block count:\t%d\n\n", hdr->xbat_count);
 	return;
 }
 
ec981799
 static int ole2_read_block(int fd, ole2_header_t *hdr, void *buff, unsigned int size, int32_t blockno)
47bbbc56
 {
c30e4a78
 	off_t offset, offend;
47bbbc56
 
767c51ae
 	if (blockno < 0) {
 		return FALSE;
 	}
 	
bf5a1ce7
 	/* other methods: (blockno+1) * 512 or (blockno * block_size) + 512; */
ec981799
 	offset = (blockno << hdr->log2_big_block_size) + MAX(512, 1 << hdr->log2_big_block_size); /* 512 is header size */
8c030302
 	
 	if (hdr->m_area == NULL) {
 		if (lseek(fd, offset, SEEK_SET) != offset) {
 			return FALSE;
 		}
ec981799
 		if (cli_readn(fd, buff, size) != size) {
8c030302
 			return FALSE;
 		}
 	} else {
ec981799
 		offend = offset + size;
c30e4a78
 		if ((offend <= 0) || (offend > hdr->m_length)) {
8c030302
 			return FALSE;
 		}
ec981799
 		memcpy(buff, hdr->m_area+offset, size);
47bbbc56
 	}
 	return TRUE;
 }
 
e0f90556
 static int32_t ole2_get_next_bat_block(int fd, ole2_header_t *hdr, int32_t current_block)
47bbbc56
 {
1dc96b6c
 	int32_t bat_array_index;
47bbbc56
 	uint32_t bat[128];
 
767c51ae
 	if (current_block < 0) {
 		return -1;
 	}
 	
47bbbc56
 	bat_array_index = current_block / 128;
 	if (bat_array_index > hdr->bat_count) {
 		cli_dbgmsg("bat_array index error\n");
 		return -10;
 	}
ec981799
 	if (!ole2_read_block(fd, hdr, &bat, 512,
767c51ae
 			ole2_endian_convert_32(hdr->bat_array[bat_array_index]))) {
 		return -1;
 	}
c83b8845
 	return ole2_endian_convert_32(bat[current_block-(bat_array_index * 128)]);
47bbbc56
 }
 
e0f90556
 static int32_t ole2_get_next_xbat_block(int fd, ole2_header_t *hdr, int32_t current_block)
47bbbc56
 {
1dc96b6c
 	int32_t xbat_index, xbat_block_index, bat_index, bat_blockno;
47bbbc56
 	uint32_t xbat[128], bat[128];
 
767c51ae
 	if (current_block < 0) {
 		return -1;
 	}
 	
47bbbc56
 	xbat_index = current_block / 128;
 
 	/* NB:	The last entry in each XBAT points to the next XBAT block.
 		This reduces the number of entries in each block by 1.
 	*/
 	xbat_block_index = (xbat_index - 109) / 127;
 	bat_blockno = (xbat_index - 109) % 127;
 
 	bat_index = current_block % 128;
 
ec981799
 	if (!ole2_read_block(fd, hdr, &xbat, 512, hdr->xbat_start)) {
767c51ae
 		return -1;
 	}
47bbbc56
 
 	/* Follow the chain of XBAT blocks */
 	while (xbat_block_index > 0) {
ec981799
 		if (!ole2_read_block(fd, hdr, &xbat, 512,
767c51ae
 				ole2_endian_convert_32(xbat[127]))) {
 			return -1;
 		}
47bbbc56
 		xbat_block_index--;
 	}
 
ec981799
 	if (!ole2_read_block(fd, hdr, &bat, 512, ole2_endian_convert_32(xbat[bat_blockno]))) {
767c51ae
 		return -1;
 	}
47bbbc56
 
c83b8845
 	return ole2_endian_convert_32(bat[bat_index]);
47bbbc56
 }
 
e0f90556
 static int32_t ole2_get_next_block_number(int fd, ole2_header_t *hdr, int32_t current_block)
47bbbc56
 {
767c51ae
 	if (current_block < 0) {
 		return -1;
 	}
 
47bbbc56
 	if ((current_block / 128) > 108) {
 		return ole2_get_next_xbat_block(fd, hdr, current_block);
 	} else {
 		return ole2_get_next_bat_block(fd, hdr, current_block);
 	}
 }
 
e0f90556
 static int32_t ole2_get_next_sbat_block(int fd, ole2_header_t *hdr, int32_t current_block)
1dc96b6c
 {
 	int32_t iter, current_bat_block;
 	uint32_t sbat[128];
 
767c51ae
 	if (current_block < 0) {
 		return -1;
 	}
 	
1dc96b6c
 	current_bat_block = hdr->sbat_start;
 	iter = current_block / 128;
 	while (iter > 0) {
 		current_bat_block = ole2_get_next_block_number(fd, hdr, current_bat_block);
 		iter--;
 	}
ec981799
 	if (!ole2_read_block(fd, hdr, &sbat, 512, current_bat_block)) {
767c51ae
 		return -1;
 	}
1dc96b6c
 	return ole2_endian_convert_32(sbat[current_block % 128]);
 }
 
47bbbc56
 /* Retrieve the block containing the data for the given sbat index */
e0f90556
 static int32_t ole2_get_sbat_data_block(int fd, ole2_header_t *hdr, void *buff, int32_t sbat_index)
47bbbc56
 {
1dc96b6c
 	int32_t block_count, current_block;
47bbbc56
 
767c51ae
 	if (sbat_index < 0) {
 		return FALSE;
 	}
 	
349e0502
 	if (hdr->sbat_root_start < 0) {
f2d79ab3
 		cli_dbgmsg("No root start block\n");
47bbbc56
 		return FALSE;
 	}
 
ec981799
 	block_count = sbat_index / (1 << (hdr->log2_big_block_size - hdr->log2_small_block_size));
349e0502
 	current_block = hdr->sbat_root_start;
47bbbc56
 	while (block_count > 0) {
1dc96b6c
 		current_block = ole2_get_next_block_number(fd, hdr, current_block);
47bbbc56
 		block_count--;
 	}
 	/* current_block now contains the block number of the sbat array
 	   containing the entry for the required small block */
 
ec981799
 	return(ole2_read_block(fd, hdr, buff, 1 << hdr->log2_big_block_size, current_block));
47bbbc56
 }
 
72ce4b70
 static int ole2_walk_property_tree(int fd, ole2_header_t *hdr, const char *dir, int32_t prop_index,
 				   int (*handler)(int fd, ole2_header_t *hdr, property_t *prop, const char *dir, cli_ctx *ctx),
 				   unsigned int rec_level, unsigned int *file_count, cli_ctx *ctx, unsigned long *scansize)
ad2f8396
 {
 	property_t prop_block[4];
24555841
 	int32_t idx, current_block, i;
bda5598b
 	char *dirname;
72ce4b70
 	int ret;
f8c58685
 
ad2f8396
 	current_block = hdr->prop_start;
f8c58685
 
24555841
 	if ((prop_index < 0) || (prop_index > (int32_t) hdr->max_block_no) || (rec_level > 100) || (*file_count > 100000)) {
72ce4b70
 		return CL_SUCCESS;
e5a5b2f2
 	}
724b2bf7
 	if (ctx && ctx->engine->maxfiles && (*file_count > ctx->engine->maxfiles)) {
 		cli_dbgmsg("OLE2: File limit reached (max: %d)\n", ctx->engine->maxfiles);
72ce4b70
 		return CL_SUCCESS;
ad2f8396
 	}
 	
724b2bf7
 	if (ctx && ctx->engine->maxreclevel && (rec_level > ctx->engine->maxreclevel)) {
 		cli_dbgmsg("OLE2: Recursion limit reached (max: %d)\n", ctx->engine->maxreclevel);
72ce4b70
 		return CL_SUCCESS;
42034091
 	}
7df56d47
 
24555841
 	idx = prop_index / 4;
 	for (i=0 ; i < idx ; i++) {
ad2f8396
 		current_block = ole2_get_next_block_number(fd, hdr, current_block);
 		if (current_block < 0) {
72ce4b70
 			return CL_SUCCESS;
ad2f8396
 		}
 	}
24555841
 	idx = prop_index % 4;
ec981799
 	if (!ole2_read_block(fd, hdr, prop_block, 512,
ad2f8396
 			current_block)) {
72ce4b70
 		return CL_SUCCESS;
ad2f8396
 	}	
24555841
 	if (prop_block[idx].type <= 0) {
72ce4b70
 		return CL_SUCCESS;
ad2f8396
 	}
24555841
 	prop_block[idx].name_size = ole2_endian_convert_16(prop_block[idx].name_size);
 	prop_block[idx].prev = ole2_endian_convert_32(prop_block[idx].prev);
 	prop_block[idx].next = ole2_endian_convert_32(prop_block[idx].next);
 	prop_block[idx].child = ole2_endian_convert_32(prop_block[idx].child);
 	prop_block[idx].user_flags = ole2_endian_convert_32(prop_block[idx].user_flags);
 	prop_block[idx].create_lowdate = ole2_endian_convert_32(prop_block[idx].create_lowdate);
 	prop_block[idx].create_highdate = ole2_endian_convert_32(prop_block[idx].create_highdate);
 	prop_block[idx].mod_lowdate = ole2_endian_convert_32(prop_block[idx].mod_lowdate);
 	prop_block[idx].mod_highdate = ole2_endian_convert_32(prop_block[idx].mod_highdate);
 	prop_block[idx].start_block = ole2_endian_convert_32(prop_block[idx].start_block);
 	prop_block[idx].size = ole2_endian_convert_32(prop_block[idx].size);
ad2f8396
 	
72ce4b70
 	if (dir) print_ole2_property(&prop_block[idx]);
8a9c2d19
 
 	/* Check we aren't in a loop */
 	if (cli_bitset_test(hdr->bitset, (unsigned long) prop_index)) {
 		/* Loop in property tree detected */
 		cli_dbgmsg("OLE2: Property tree loop detected at index %d\n", prop_index);
72ce4b70
 		return CL_BREAK;
8a9c2d19
 	}
 	if (!cli_bitset_set(hdr->bitset, (unsigned long) prop_index)) {
72ce4b70
 		return CL_SUCCESS;
8a9c2d19
 	}
 
24555841
 	switch (prop_block[idx].type) {
ad2f8396
 		case 5: /* Root Entry */
cd9833ef
 			if ((prop_index != 0) || (rec_level !=0) ||
e5a5b2f2
 					(*file_count != 0)) {
ad2f8396
 				/* Can only have RootEntry as the top */
 				cli_dbgmsg("ERROR: illegal Root Entry\n");
72ce4b70
 				return CL_SUCCESS;
ad2f8396
 			}
24555841
 			hdr->sbat_root_start = prop_block[idx].start_block;
72ce4b70
 			if (
 				(ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].prev, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS
 				||
 				(ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].next, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS
 				||
 				(ret=ole2_walk_property_tree(fd, hdr, dir,prop_block[idx].child, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS
 			) return ret;
ad2f8396
 			break;
 		case 2: /* File */
724b2bf7
 			if (ctx && ctx->engine->maxfiles && ctx->scannedfiles + *file_count > ctx->engine->maxfiles) {
 				cli_dbgmsg("OLE2: files limit reached (max: %u)\n", ctx->engine->maxfiles);
72ce4b70
 				return CL_BREAK;
08d0fdc5
 			}
724b2bf7
 			if (!ctx || !ctx->engine->maxfilesize || prop_block[idx].size <= ctx->engine->maxfilesize || prop_block[idx].size <= *scansize) {
08d0fdc5
 				(*file_count)++;
 				*scansize-=prop_block[idx].size;
72ce4b70
 				if ((ret=handler(fd, hdr, &prop_block[idx], dir, ctx)) != CL_SUCCESS)
 					return ret;
08d0fdc5
 			} else {
72ce4b70
 				cli_dbgmsg("OLE2: filesize exceeded\n");
ad2f8396
 			}
72ce4b70
 			if (
 				(ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].prev, handler, rec_level, file_count, ctx, scansize))!=CL_SUCCESS
 				||
 				(ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].next, handler, rec_level, file_count, ctx, scansize))!=CL_SUCCESS
 				||
 				(ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].child, handler, rec_level, file_count, ctx, scansize))!=CL_SUCCESS
 			) return ret;
ad2f8396
 			break;
 		case 1: /* Directory */
72ce4b70
 			if (dir) {
 				dirname = (char *) cli_malloc(strlen(dir)+8);
 				if (!dirname) return CL_BREAK;
 				snprintf(dirname, strlen(dir)+8, "%s/%.6d", dir, prop_index);
 				if (mkdir(dirname, 0700) != 0) {
 					free(dirname);
 					return CL_BREAK;
 				}
 				cli_dbgmsg("OLE2 dir entry: %s\n",dirname);
 			} else dirname = NULL;
 			if (
 				(ret=ole2_walk_property_tree(fd, hdr, dir, prop_block[idx].prev, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS
 				||
 				(ret=ole2_walk_property_tree(fd, hdr, dir,prop_block[idx].next, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS
 				||
 				(ret=ole2_walk_property_tree(fd, hdr, dirname, prop_block[idx].child, handler, rec_level+1, file_count, ctx, scansize))!=CL_SUCCESS
 			) {} 
 			if (dirname) free(dirname);
 			return ret;
ad2f8396
 			break;
 		default:
24555841
 			cli_dbgmsg("ERROR: unknown OLE2 entry type: %d\n", prop_block[idx].type);
ad2f8396
 			break;
 	}
72ce4b70
 	return CL_SUCCESS;
ad2f8396
 }
47bbbc56
 /* Write file Handler - write the contents of the entry to a file */
72ce4b70
 static int handler_writefile(int fd, ole2_header_t *hdr, property_t *prop, const char *dir, cli_ctx *ctx)
47bbbc56
 {
3c21733d
 	unsigned char *buff;
1dc96b6c
 	int32_t current_block, ofd, len, offset;
72ce4b70
 	char *name, newname[1024];
42438bd5
 	bitset_t *blk_bitset;
937ade08
 	char *hash;
 	uint32_t cnt;
47bbbc56
 
 	if (prop->type != 2) {
bf5a1ce7
 		/* Not a file */
72ce4b70
 		return CL_SUCCESS;
d95b7372
 	}
 
 	if (prop->name_size > 64) {
72ce4b70
 		cli_dbgmsg("OLE2 [handler_writefile]: property name too long: %d\n", prop->name_size);
 		return CL_SUCCESS;
bf34c7e7
 	}
b58fdfc2
 
72ce4b70
 	name = get_property_name2(prop->name, prop->name_size);
 	if (name) cnt = uniq_add(hdr->U, name, strlen(name), &hash);
 	else cnt = uniq_add(hdr->U, NULL, 0, &hash);
937ade08
 	snprintf(newname, sizeof(newname), "%s/%s_%u", dir, hash, cnt);
72ce4b70
 	newname[sizeof(newname)-1]='\0';
 	cli_dbgmsg("OLE2 [handler_writefile]: Dumping '%s' to '%s'\n", name ? name : "<empty>", newname);
 	if (name) free(name);
47bbbc56
 
b58fdfc2
 	ofd = open(newname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU);
47bbbc56
 	if (ofd < 0) {
72ce4b70
 		cli_errmsg("OLE2 [handler_writefile]: failed to create file: %s\n", newname);
 		return CL_SUCCESS;
47bbbc56
 	}
 	current_block = prop->start_block;
 	len = prop->size;
 
3c21733d
 	buff = (unsigned char *) cli_malloc(1 << hdr->log2_big_block_size);
 	if (!buff) {
 		close(ofd);
72ce4b70
 		return CL_BREAK;
3c21733d
 	}
42438bd5
 	
 	blk_bitset = cli_bitset_init();
 	if (!blk_bitset) {
72ce4b70
 		cli_errmsg("OLE2 [handler_writefile]: init bitset failed\n");
42438bd5
 		close(ofd);
ec981799
 		free(buff);
72ce4b70
 		return CL_BREAK;
42438bd5
 	}
47bbbc56
 	while((current_block >= 0) && (len > 0)) {
24555841
 		if (current_block > (int32_t) hdr->max_block_no) {
72ce4b70
                         cli_dbgmsg("OLE2 [handler_writefile]: Max block number for file size exceeded: %d\n", current_block);
ac6082ed
                         close(ofd);
                         free(buff);
                         cli_bitset_free(blk_bitset);
72ce4b70
                         return CL_SUCCESS;
ac6082ed
                 }
42438bd5
 		/* Check we aren't in a loop */
 		if (cli_bitset_test(blk_bitset, (unsigned long) current_block)) {
 			/* Loop in block list */
72ce4b70
 			cli_dbgmsg("OLE2 [handler_writefile]: Block list loop detected\n");
42438bd5
 			close(ofd);
 			free(buff);
 			cli_bitset_free(blk_bitset);
72ce4b70
 			return CL_BREAK;
42438bd5
 		}
 		if (!cli_bitset_set(blk_bitset, (unsigned long) current_block)) {
 			close(ofd);
 			free(buff);
 			cli_bitset_free(blk_bitset);
72ce4b70
 			return CL_BREAK;
42438bd5
 		}			
9d3c38ba
 		if (prop->size < (int64_t)hdr->sbat_cutoff) {
bf5a1ce7
 			/* Small block file */
3c21733d
 			if (!ole2_get_sbat_data_block(fd, hdr, buff, current_block)) {
72ce4b70
 				cli_dbgmsg("OLE2 [handler_writefile]: ole2_get_sbat_data_block failed\n");
47bbbc56
 				close(ofd);
3c21733d
 				free(buff);
42438bd5
 				cli_bitset_free(blk_bitset);
72ce4b70
 				return CL_SUCCESS;
47bbbc56
 			}
ec981799
 			/* buff now contains the block with N small blocks in it */
 			offset = (1 << hdr->log2_small_block_size) * (current_block % (1 << (hdr->log2_big_block_size - hdr->log2_small_block_size)));
 
 			if (cli_writen(ofd, &buff[offset], MIN(len,1 << hdr->log2_small_block_size)) != MIN(len,1 << hdr->log2_small_block_size)) {
47bbbc56
 				close(ofd);
3c21733d
 				free(buff);
42438bd5
 				cli_bitset_free(blk_bitset);
72ce4b70
 				return CL_BREAK;
47bbbc56
 			}
 
ec981799
 			len -= MIN(len,1 << hdr->log2_small_block_size);
47bbbc56
 			current_block = ole2_get_next_sbat_block(fd, hdr, current_block);
 		} else {
bf5a1ce7
 			/* Big block file */
ec981799
 			if (!ole2_read_block(fd, hdr, buff, 1 << hdr->log2_big_block_size, current_block)) {
47bbbc56
 				close(ofd);
3c21733d
 				free(buff);
42438bd5
 				cli_bitset_free(blk_bitset);
72ce4b70
 				return CL_SUCCESS;
47bbbc56
 			}
3c21733d
 			if (cli_writen(ofd, buff, MIN(len,(1 << hdr->log2_big_block_size))) !=
349e0502
 							MIN(len,(1 << hdr->log2_big_block_size))) {
47bbbc56
 				close(ofd);
3c21733d
 				free(buff);
42438bd5
 				cli_bitset_free(blk_bitset);
72ce4b70
 				return CL_BREAK;
47bbbc56
 			}
 
 			current_block = ole2_get_next_block_number(fd, hdr, current_block);
349e0502
 			len -= MIN(len,(1 << hdr->log2_big_block_size));
47bbbc56
 		}
 	}
 	close(ofd);
3c21733d
 	free(buff);
42438bd5
 	cli_bitset_free(blk_bitset);
72ce4b70
 	return CL_SUCCESS;
 }
 
 /* enum file Handler - checks for VBA presence */
 static int handler_enum(int fd, ole2_header_t *hdr, property_t *prop, const char *dir, cli_ctx *ctx)
 {
   char *name;
   
   if (!hdr->has_vba) {
     name = get_property_name2(prop->name, prop->name_size);
     if (name) {
       if (!strcmp(name, "_vba_project") || !strcmp(name, "powerpoint document") || !strcmp(name, "worddocument") || !strcmp(name, "_1_ole10native"))
 	hdr->has_vba = 1;
       free(name);
     }
   }
   return CL_SUCCESS;
 }
 
 
 static int handler_otf(int fd, ole2_header_t *hdr, property_t *prop, const char *dir, cli_ctx *ctx)
 {
   char *tempfile;
   unsigned char *buff;
   int32_t current_block, len, offset;
   int ofd, ret;
   bitset_t *blk_bitset;
 
   if (prop->type != 2) {
     /* Not a file */
     return CL_SUCCESS;
   }
 
   print_ole2_property(prop);
 
33068e09
   if(!(tempfile = cli_gentemp(ctx ? ctx->engine->tmpdir : NULL)))
72ce4b70
     return CL_EMEM;
 
   if((ofd = open(tempfile, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU)) < 0) {
     cli_dbgmsg("OLE2: Can't create file %s\n", tempfile);
     free(tempfile);
871177cd
     return CL_ECREAT;
72ce4b70
   }
 
   current_block = prop->start_block;
   len = prop->size;
 
   buff = (unsigned char *) cli_malloc(1 << hdr->log2_big_block_size);
   if (!buff) {
     close(ofd);
     cli_unlink(tempfile);
     free(tempfile);
     return CL_EMEM;
   }
 
   blk_bitset = cli_bitset_init();
 
   if (!blk_bitset) {
     cli_errmsg("OLE2: OTF handler init bitset failed\n");
     free(buff);
     close(ofd);
     if (cli_unlink(tempfile)) {
         free(tempfile);
871177cd
 	return CL_EUNLINK;
72ce4b70
     }
     free(tempfile);
     return CL_BREAK;
   }
   
   while((current_block >= 0) && (len > 0)) {
     if (current_block > (int32_t) hdr->max_block_no) {
       cli_dbgmsg("OLE2: Max block number for file size exceeded: %d\n", current_block);
77fd59a8
       break;
72ce4b70
     }
     /* Check we aren't in a loop */
     if (cli_bitset_test(blk_bitset, (unsigned long) current_block)) {
       /* Loop in block list */
       cli_dbgmsg("OLE2: Block list loop detected\n");
77fd59a8
       break;
72ce4b70
     }
     if (!cli_bitset_set(blk_bitset, (unsigned long) current_block)) {
77fd59a8
       break;
72ce4b70
     }			
     if (prop->size < (int64_t)hdr->sbat_cutoff) {
       /* Small block file */
       if (!ole2_get_sbat_data_block(fd, hdr, buff, current_block)) {
 	cli_dbgmsg("ole2_get_sbat_data_block failed\n");
77fd59a8
 	break;
72ce4b70
       }
ec981799
       /* buff now contains the block with N small blocks in it */
       offset = (1 << hdr->log2_small_block_size) * (current_block % (1 << (hdr->log2_big_block_size - hdr->log2_small_block_size)));
       if (cli_writen(ofd, &buff[offset], MIN(len,1 << hdr->log2_small_block_size)) != MIN(len,1 << hdr->log2_small_block_size)) {
72ce4b70
 	close(ofd);
 	free(buff);
 	cli_bitset_free(blk_bitset);
 	if (cli_unlink(tempfile)) {
 	  free(tempfile);
871177cd
 	  return CL_EUNLINK;
72ce4b70
         }
 	free(tempfile);
 	return CL_BREAK;
       }
 
ec981799
       len -= MIN(len,1 << hdr->log2_small_block_size);
72ce4b70
       current_block = ole2_get_next_sbat_block(fd, hdr, current_block);
     } else {
       /* Big block file */
ec981799
       if (!ole2_read_block(fd, hdr, buff, 1 << hdr->log2_big_block_size, current_block)) {
77fd59a8
 	break;
72ce4b70
       }
       if (cli_writen(ofd, buff, MIN(len,(1 << hdr->log2_big_block_size))) !=
 	  MIN(len,(1 << hdr->log2_big_block_size))) {
 	close(ofd);
 	free(buff);
 	cli_bitset_free(blk_bitset);
 	if (cli_unlink(tempfile)) {
 	  free(tempfile);
871177cd
 	  return CL_EUNLINK;
72ce4b70
         }
 	free(tempfile);
871177cd
 	return CL_EWRITE;
72ce4b70
       }
 
       current_block = ole2_get_next_block_number(fd, hdr, current_block);
       len -= MIN(len,(1 << hdr->log2_big_block_size));
     }
   }
 
   lseek(ofd, 0, SEEK_SET);
   ret=cli_magic_scandesc(ofd, ctx);
   close(ofd);
   free(buff);
   cli_bitset_free(blk_bitset);
33068e09
   if(ctx && !ctx->engine->keeptmp) {
72ce4b70
     if (cli_unlink(tempfile)) {
       free(tempfile);
871177cd
       return CL_EUNLINK;
72ce4b70
     }
   }
   free(tempfile);
   return ret==CL_VIRUS ? CL_VIRUS : CL_SUCCESS;
 
47bbbc56
 }
 
ef38c83d
 #if !defined(HAVE_ATTRIB_PACKED) && !defined(HAVE_PRAGMA_PACK) && !defined(HAVE_PRAGMA_PACK_HPPA)
e0f90556
 static int ole2_read_header(int fd, ole2_header_t *hdr)
1dc96b6c
 {
 	int i;
 	
5b25b5e8
 	if (cli_readn(fd, &hdr->magic, 8) != 8) {
1dc96b6c
 		return FALSE;
 	}
5b25b5e8
 	if (cli_readn(fd, &hdr->clsid, 16) != 16) {
1dc96b6c
 		return FALSE;
 	}
5b25b5e8
 	if (cli_readn(fd, &hdr->minor_version, 2) != 2) {
1dc96b6c
 		return FALSE;
 	}
5b25b5e8
 	if (cli_readn(fd, &hdr->dll_version, 2) != 2) {
1dc96b6c
 		return FALSE;
 	}
5b25b5e8
 	if (cli_readn(fd, &hdr->byte_order, 2) != 2) {
1dc96b6c
 		return FALSE;
 	}
5b25b5e8
 	if (cli_readn(fd, &hdr->log2_big_block_size, 2) != 2) {
1dc96b6c
 		return FALSE;
 	}
5b25b5e8
 	if (cli_readn(fd, &hdr->log2_small_block_size, 4) != 4) {
1dc96b6c
 		return FALSE;
 	}
5b25b5e8
 	if (cli_readn(fd, &hdr->reserved, 8) != 8) {
1dc96b6c
 		return FALSE;
 	}
5b25b5e8
 	if (cli_readn(fd, &hdr->bat_count, 4) != 4) {
1dc96b6c
 		return FALSE;
 	}
5b25b5e8
 	if (cli_readn(fd, &hdr->prop_start, 4) != 4) {
1dc96b6c
 		return FALSE;
 	}
5b25b5e8
 	if (cli_readn(fd, &hdr->signature, 4) != 4) {
1dc96b6c
 		return FALSE;
 	}
5b25b5e8
 	if (cli_readn(fd, &hdr->sbat_cutoff, 4) != 4) {
1dc96b6c
 		return FALSE;
 	}
5b25b5e8
 	if (cli_readn(fd, &hdr->sbat_start, 4) != 4) {
1dc96b6c
 		return FALSE;
 	}
5b25b5e8
 	if (cli_readn(fd, &hdr->sbat_block_count, 4) != 4) {
1dc96b6c
 		return FALSE;
 	}
5b25b5e8
 	if (cli_readn(fd, &hdr->xbat_start, 4) != 4) {
1dc96b6c
 		return FALSE;
 	}
5b25b5e8
 	if (cli_readn(fd, &hdr->xbat_count, 4) != 4) {
1dc96b6c
 		return FALSE;
 	}
 	for (i=0 ; i < 109 ; i++) {
5b25b5e8
 		if (cli_readn(fd, &hdr->bat_array[i], 4) != 4) {
1dc96b6c
 			return FALSE;
 		}
 	}
 	return TRUE;
 }
bda5598b
 #endif
1dc96b6c
 
72ce4b70
 int cli_ole2_extract(int fd, const char *dirname, cli_ctx *ctx, struct uniq **vba)
47bbbc56
 {
 	ole2_header_t hdr;
72ce4b70
 	int hdr_size, ret=CL_CLEAN;
8c030302
 	struct stat statbuf;
bda5598b
 	unsigned int file_count=0;
72ce4b70
 	unsigned long scansize, scansize2;
08d0fdc5
 
47bbbc56
 	cli_dbgmsg("in cli_ole2_extract()\n");
08d0fdc5
 
724b2bf7
 	if (ctx && ctx->engine->maxscansize) {
 	  if (ctx->engine->maxscansize > ctx->scansize)
 	    scansize = ctx->engine->maxscansize - ctx->scansize;
08d0fdc5
 	  else
 	    return CL_EMAXSIZE;
 	} else scansize = -1;
72ce4b70
 
 	scansize2 = scansize;
1dc96b6c
 	
349e0502
 	/* size of header - size of other values in struct */
72ce4b70
 	hdr_size = sizeof(struct ole2_header_tag) - sizeof(int32_t) - sizeof(uint32_t) -
ac6082ed
 			sizeof(unsigned char *) - sizeof(off_t) - sizeof(bitset_t *) -
72ce4b70
 			sizeof(struct uniq *) - sizeof(int);
1dc96b6c
 
8c030302
 	hdr.m_area = NULL;
 
 	if (fstat(fd, &statbuf) == 0) {
4c64f434
 		if (statbuf.st_size < hdr_size) {
72ce4b70
 			return CL_CLEAN;
4c64f434
 		}
ac6082ed
 #ifdef HAVE_MMAP
8c030302
 		hdr.m_length = statbuf.st_size;
 		hdr.m_area = (unsigned char *) mmap(NULL, hdr.m_length, PROT_READ, MAP_PRIVATE, fd, 0);
 		if (hdr.m_area == MAP_FAILED) {
 			hdr.m_area = NULL;
c96406a0
 		} else {
 			cli_dbgmsg("mmap'ed file\n");
 			memcpy(&hdr, hdr.m_area, hdr_size);
8c030302
 		}
1dc96b6c
 #endif
ac6082ed
 	}
349e0502
 
8c030302
 	if (hdr.m_area == NULL) {
72ce4b70
 		hdr.bitset = NULL;
ef38c83d
 #if defined(HAVE_ATTRIB_PACKED) || defined(HAVE_PRAGMA_PACK) || defined(HAVE_PRAGMA_PACK_HPPA)
8c030302
 		if (cli_readn(fd, &hdr, hdr_size) != hdr_size) {
72ce4b70
 			goto abort;
8c030302
 		}
 #else
 		if (!ole2_read_header(fd, &hdr)) {
72ce4b70
 			goto abort;
8c030302
 		}
 #endif
 	}
 	
c83b8845
 	hdr.minor_version = ole2_endian_convert_16(hdr.minor_version);
 	hdr.dll_version = ole2_endian_convert_16(hdr.dll_version);
 	hdr.byte_order = ole2_endian_convert_16(hdr.byte_order);
 	hdr.log2_big_block_size = ole2_endian_convert_16(hdr.log2_big_block_size);
 	hdr.log2_small_block_size = ole2_endian_convert_32(hdr.log2_small_block_size);
 	hdr.bat_count = ole2_endian_convert_32(hdr.bat_count);
 	hdr.prop_start = ole2_endian_convert_32(hdr.prop_start);
 	hdr.sbat_cutoff = ole2_endian_convert_32(hdr.sbat_cutoff);
 	hdr.sbat_start = ole2_endian_convert_32(hdr.sbat_start);
 	hdr.sbat_block_count = ole2_endian_convert_32(hdr.sbat_block_count);
 	hdr.xbat_start = ole2_endian_convert_32(hdr.xbat_start);
 	hdr.xbat_count = ole2_endian_convert_32(hdr.xbat_count);
 
349e0502
 	hdr.sbat_root_start = -1;
8a9c2d19
 
 	hdr.bitset = cli_bitset_init();
77fd59a8
 	if (!hdr.bitset) {
871177cd
 		ret=CL_EMEM;
77fd59a8
 		goto abort;
8a9c2d19
 	}
 
bda5598b
 	if (memcmp(hdr.magic, magic_id, 8) != 0) {
47bbbc56
 		cli_dbgmsg("OLE2 magic failed!\n");
871177cd
 		ret=CL_EFORMAT;
72ce4b70
 		goto abort;
47bbbc56
 	}
 
ec981799
 	if (hdr.log2_big_block_size < 6 || hdr.log2_big_block_size > 30) {
 		cli_dbgmsg("CAN'T PARSE: Invalid big block size (2^%u)\n", hdr.log2_big_block_size);
4b6040a4
 		goto abort;
47bbbc56
 	}
ec981799
 	if (!hdr.log2_small_block_size || hdr.log2_small_block_size > hdr.log2_big_block_size) {
 		cli_dbgmsg("CAN'T PARSE: Invalid small block size (2^%u)\n", hdr.log2_small_block_size);
4b6040a4
 		goto abort;
47bbbc56
 	}
ec981799
 
47bbbc56
 	if (hdr.sbat_cutoff != 4096) {
ec981799
 		cli_dbgmsg("WARNING: Untested sbat cutoff (%u); data may not extract correctly\n", hdr.sbat_cutoff);
47bbbc56
 	}
7df56d47
 
 	/* 8 SBAT blocks per file block */
ec981799
 	hdr.max_block_no = (statbuf.st_size - MAX(512, 1 << hdr.log2_big_block_size)) / (1 << hdr.log2_small_block_size);
 
47bbbc56
 	print_ole2_header(&hdr);
24555841
 	cli_dbgmsg("Max block number: %lu\n", (unsigned long int) hdr.max_block_no);
47bbbc56
 
72ce4b70
 	/* PASS 1 : Count files and check for VBA */
 	hdr.has_vba = 0;
 	ret = ole2_walk_property_tree(fd, &hdr, NULL, 0, handler_enum, 0, &file_count, ctx, &scansize);
 	cli_bitset_free(hdr.bitset);
 	hdr.bitset = NULL;
 	if (!file_count || !(hdr.bitset = cli_bitset_init()))
 	  goto abort;
 
 	/* If there's no VBA we scan OTF */
 	if (hdr.has_vba) {
 	  /* PASS 2/A : VBA scan */
 	  cli_dbgmsg("OLE2: VBA project found\n");
 	  if (!(hdr.U = uniq_init(file_count))) {
 	    cli_dbgmsg("OLE2: uniq_init() failed\n");
 	    ret = CL_EMEM;
 	    goto abort;
 	  }
 	  file_count = 0;
 	  ole2_walk_property_tree(fd, &hdr, dirname, 0, handler_writefile, 0, &file_count, ctx, &scansize2);
 	  ret = CL_CLEAN;
 	  *vba = hdr.U;
 	} else {
77fd59a8
 	  cli_dbgmsg("OLE2: no VBA projects found\n");
72ce4b70
 	  /* PASS 2/B : OTF scan */
 	  file_count = 0;
b0c7e984
 	  if(ctx)
 	    ret = ole2_walk_property_tree(fd, &hdr, NULL, 0, handler_otf, 0, &file_count, ctx, &scansize2);
72ce4b70
 	}
8c030302
 
4b6040a4
 abort:
8c030302
 #ifdef HAVE_MMAP
 	if (hdr.m_area != NULL) {
 		munmap(hdr.m_area, hdr.m_length);
 	}
 #endif
72ce4b70
 	if(hdr.bitset)
 	    cli_bitset_free(hdr.bitset);
 
2624c770
 	return ret == CL_BREAK ? CL_CLEAN : ret;
47bbbc56
 }