libclamav/ole2_extract.c
c561d2a3
 /*
  *  Extract component parts of OLE2 files (e.g. MS Office Documents)
  *
  *  Copyright (C) 2004 trog@uncon.org
  *
  *  This code is based on the OpenOffice and libgsf sources.
  *                  
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
  *  (at your option) any later version.
  *
  *  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
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
8b242bb9
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
c561d2a3
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 #include <ctype.h>
 #include <stdlib.h>
 #include <clamav.h>
 
df5ddd11
 #if HAVE_MMAP
 #if HAVE_SYS_MMAN_H
 #include <sys/mman.h>
 #else /* HAVE_SYS_MMAN_H */
 #undef HAVE_MMAP
 #endif
 #endif
 
1f301ecc
 #include "cltypes.h"
3f3c6eb5
 #include "others.h"
1f301ecc
 
06f64aa7
 #ifndef FALSE
c561d2a3
 #define FALSE (0)
 #define TRUE (1)
06f64aa7
 #endif
c561d2a3
 
06f64aa7
 #ifndef MIN
c561d2a3
 #define MIN(a, b)  (((a) < (b)) ? (a) : (b))
06f64aa7
 #endif
c561d2a3
 
06bc4839
 #if WORDS_BIGENDIAN == 0
04d18609
 #define ole2_endian_convert_16(v)	(v)
 #else
6537bb75
 static uint16_t ole2_endian_convert_16(uint16_t v)
 {
 	return ((v >> 8) + (v << 8));
 }
 #endif
 
06bc4839
 #if WORDS_BIGENDIAN == 0
6537bb75
 #define ole2_endian_convert_32(v)    (v)
 #else
 static uint32_t ole2_endian_convert_32(uint32_t v)
 {
         return ((v >> 24) | ((v & 0x00FF0000) >> 8) |
                 ((v & 0x0000FF00) << 8) | (v << 24));
 }
 #endif
 
3f3c6eb5
 #ifndef HAVE_ATTRIB_PACKED
 #define __attribute__(x)
 #endif
 
 #ifdef HAVE_PRAGMA_PACK
 #pragma pack(1)
 #endif
 
c561d2a3
 typedef struct ole2_header_tag
 {
06f64aa7
 	unsigned char magic[8] __attribute__ ((packed));		/* should be: 0xd0cf11e0a1b11ae1 */
 	unsigned char clsid[16] __attribute__ ((packed));
 	uint16_t minor_version __attribute__ ((packed));
 	uint16_t dll_version __attribute__ ((packed));
 	int16_t byte_order __attribute__ ((packed));			/* -2=intel */
c561d2a3
 
06f64aa7
 	uint16_t log2_big_block_size __attribute__ ((packed));		/* usually 9 (2^9 = 512) */
 	uint32_t log2_small_block_size __attribute__ ((packed));	/* usually 6 (2^6 = 128) */
c561d2a3
 
06f64aa7
 	int32_t reserved[2] __attribute__ ((packed));
 	int32_t bat_count __attribute__ ((packed));
 	int32_t prop_start __attribute__ ((packed));
c561d2a3
 
06f64aa7
 	uint32_t signature __attribute__ ((packed));
 	uint32_t sbat_cutoff __attribute__ ((packed));			/* cutoff for files held in small blocks (4096) */
c561d2a3
 
06f64aa7
 	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));
ee5c926e
 
 	/* 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 */
06f64aa7
 	int32_t sbat_root_start __attribute__ ((packed));
df5ddd11
 	unsigned char *m_area;
 	off_t m_length;
06f64aa7
 } ole2_header_t;
c561d2a3
 
 typedef struct property_tag
 {
06f64aa7
 	unsigned char name[64] __attribute__ ((packed));		/* in unicode */
 	int16_t name_size __attribute__ ((packed));
 	unsigned char type __attribute__ ((packed));			/* 1=dir 2=file 5=root */
 	unsigned char color __attribute__ ((packed));			/* black or red */
 	int32_t prev __attribute__ ((packed));
 	int32_t next __attribute__ ((packed));
 	int32_t child __attribute__ ((packed));
 
 	unsigned char clsid[16] __attribute__ ((packed));
 	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));
 	int32_t start_block __attribute__ ((packed));
 	int32_t size __attribute__ ((packed));
 	unsigned char reserved[4] __attribute__ ((packed));
 } property_t;
c561d2a3
 
3f3c6eb5
 #ifdef HAVE_PRAGMA_PACK
 #pragma pack()
 #endif
 
 unsigned char magic_id[] = { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1};
c561d2a3
 
06f64aa7
 static char *get_property_name(char *name, int size)
c561d2a3
 {
 	int i, j;
 	char *newname;
 
77ca88c6
 	if (*name == 0 || size <= 0 || size > 64) {
c561d2a3
 		return NULL;
 	}
 
ee5c926e
 	newname = (char *) cli_malloc(size*2);
c561d2a3
 	if (!newname) {
 		return NULL;
 	}
 	j=0;
 	/* size-2 to ignore trailing NULL */
 	for (i=0 ; i < size-2; i+=2) {
 		if (isprint(name[i])) {
 			newname[j++] = name[i];
 		} else {
 			if (name[i] < 10 && name[i] >= 0) {
 				newname[j++] = '_';
 				newname[j++] = name[i] + '0';
 			}
 			newname[j++] = '_';
 		}
 	}
 	newname[j] = '\0';
ee5c926e
 	if (strlen(newname) == 0) {
 		free(newname);
 		return NULL;
 	}
c561d2a3
 	return newname;
 }
06f64aa7
 
 static void print_property_name(char *pname, int size)
655b1363
 {
         char *name;
                                                                                                                                               
         name = get_property_name(pname, size);
         if (!name) {
                 return;
         }
21aa0b4d
         cli_dbgmsg("%34s ", name);
655b1363
         free(name);
         return;
 }
c561d2a3
 
06f64aa7
 static void print_ole2_property(property_t *property)
c561d2a3
 {
655b1363
 	if (property->name_size > 64) {
                 cli_dbgmsg("[err name len: %d]\n", property->name_size);
                 return;
         }
 	print_property_name(property->name, property->name_size);
c561d2a3
 	switch (property->type) {
 	case 2:
21aa0b4d
 		cli_dbgmsg(" [file] ");
c561d2a3
 		break;
 	case 1:
21aa0b4d
 		cli_dbgmsg(" [dir ] ");
c561d2a3
 		break;
 	case 5:
21aa0b4d
 		cli_dbgmsg(" [root] ");
c561d2a3
 		break;
 	default:
 		cli_dbgmsg(" [%d]", property->type);
 	}
 	switch (property->color) {
 	case 0:
21aa0b4d
 		cli_dbgmsg(" r ");
c561d2a3
 		break;
 	case 1:
21aa0b4d
 		cli_dbgmsg(" b ");
c561d2a3
 		break;
 	default:
21aa0b4d
 		cli_dbgmsg(" u ");
c561d2a3
 	}
 	cli_dbgmsg(" %d %x\n", property->size, property->user_flags);
 }
 
06f64aa7
 static void print_ole2_header(ole2_header_t *hdr)
c561d2a3
 {
 	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;
 }
 
06f64aa7
 static int ole2_read_block(int fd, ole2_header_t *hdr, void *buff, int32_t blockno)
c561d2a3
 {
27af0365
 	off_t offset, offend;
c561d2a3
 
e02e9f9e
 	if (blockno < 0) {
 		return FALSE;
 	}
 	
8a05efc5
 	/* other methods: (blockno+1) * 512 or (blockno * block_size) + 512; */
c561d2a3
 	offset = (blockno << hdr->log2_big_block_size) + 512;	/* 512 is header size */
df5ddd11
 	
 	if (hdr->m_area == NULL) {
 		if (lseek(fd, offset, SEEK_SET) != offset) {
 			return FALSE;
 		}
 		if (cli_readn(fd, buff, (1 << hdr->log2_big_block_size)) != (1 << hdr->log2_big_block_size)) {
 			return FALSE;
 		}
 	} else {
27af0365
 		offend = offset + (1 << hdr->log2_big_block_size);
 		if ((offend <= 0) || (offend > hdr->m_length)) {
df5ddd11
 			return FALSE;
 		}
 		memcpy(buff, hdr->m_area+offset, (1 << hdr->log2_big_block_size));
c561d2a3
 	}
 	return TRUE;
 }
 
06f64aa7
 static int32_t ole2_get_next_bat_block(int fd, ole2_header_t *hdr, int32_t current_block)
c561d2a3
 {
3f3c6eb5
 	int32_t bat_array_index;
c561d2a3
 	uint32_t bat[128];
 
e02e9f9e
 	if (current_block < 0) {
 		return -1;
 	}
 	
c561d2a3
 	bat_array_index = current_block / 128;
 	if (bat_array_index > hdr->bat_count) {
 		cli_dbgmsg("bat_array index error\n");
 		return -10;
 	}
e02e9f9e
 	if (!ole2_read_block(fd, hdr, &bat,
 			ole2_endian_convert_32(hdr->bat_array[bat_array_index]))) {
 		return -1;
 	}
6537bb75
 	return ole2_endian_convert_32(bat[current_block-(bat_array_index * 128)]);
c561d2a3
 }
 
06f64aa7
 static int32_t ole2_get_next_xbat_block(int fd, ole2_header_t *hdr, int32_t current_block)
c561d2a3
 {
3f3c6eb5
 	int32_t xbat_index, xbat_block_index, bat_index, bat_blockno;
c561d2a3
 	uint32_t xbat[128], bat[128];
 
e02e9f9e
 	if (current_block < 0) {
 		return -1;
 	}
 	
c561d2a3
 	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;
 
e02e9f9e
 	if (!ole2_read_block(fd, hdr, &xbat, hdr->xbat_start)) {
 		return -1;
 	}
c561d2a3
 
 	/* Follow the chain of XBAT blocks */
 	while (xbat_block_index > 0) {
e02e9f9e
 		if (!ole2_read_block(fd, hdr, &xbat,
 				ole2_endian_convert_32(xbat[127]))) {
 			return -1;
 		}
c561d2a3
 		xbat_block_index--;
 	}
 
e02e9f9e
 	if (!ole2_read_block(fd, hdr, &bat, xbat[bat_blockno])) {
 		return -1;
 	}
c561d2a3
 
6537bb75
 	return ole2_endian_convert_32(bat[bat_index]);
c561d2a3
 }
 
06f64aa7
 static int32_t ole2_get_next_block_number(int fd, ole2_header_t *hdr, int32_t current_block)
c561d2a3
 {
e02e9f9e
 	if (current_block < 0) {
 		return -1;
 	}
 
c561d2a3
 	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);
 	}
 }
 
06f64aa7
 static int32_t ole2_get_next_sbat_block(int fd, ole2_header_t *hdr, int32_t current_block)
3f3c6eb5
 {
 	int32_t iter, current_bat_block;
 	uint32_t sbat[128];
 
e02e9f9e
 	if (current_block < 0) {
 		return -1;
 	}
 	
3f3c6eb5
 	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--;
 	}
e02e9f9e
 	if (!ole2_read_block(fd, hdr, &sbat, current_bat_block)) {
 		return -1;
 	}
3f3c6eb5
 	return ole2_endian_convert_32(sbat[current_block % 128]);
 }
 
c561d2a3
 /* Retrieve the block containing the data for the given sbat index */
06f64aa7
 static int32_t ole2_get_sbat_data_block(int fd, ole2_header_t *hdr, void *buff, int32_t sbat_index)
c561d2a3
 {
3f3c6eb5
 	int32_t block_count, current_block;
c561d2a3
 
e02e9f9e
 	if (sbat_index < 0) {
 		return FALSE;
 	}
 	
ee5c926e
 	if (hdr->sbat_root_start < 0) {
c561d2a3
 		cli_errmsg("No root start block\n");
 		return FALSE;
 	}
 
8a05efc5
 	block_count = sbat_index / 8;			/* 8 small blocks per big block */
ee5c926e
 	current_block = hdr->sbat_root_start;
c561d2a3
 	while (block_count > 0) {
3f3c6eb5
 		current_block = ole2_get_next_block_number(fd, hdr, current_block);
c561d2a3
 		block_count--;
 	}
 	/* current_block now contains the block number of the sbat array
 	   containing the entry for the required small block */
 
 	return(ole2_read_block(fd, hdr, buff, current_block));
 }
 
 /* Read the property tree.
    It is read as just an array rather than a tree */
06f64aa7
 static void ole2_read_property_tree(int fd, ole2_header_t *hdr, const char *dir,
655b1363
 				int (*handler)(int fd, ole2_header_t *hdr, property_t *prop, const char *dir))
c561d2a3
 {
 	property_t prop_block[4];
655b1363
 	int32_t index, current_block, count=0;
c561d2a3
 	
 	current_block = hdr->prop_start;
 
 	while(current_block >= 0) {
e02e9f9e
 		if (!ole2_read_block(fd, hdr, prop_block,
 					current_block)) {
 			return;
 		}
c561d2a3
 		for (index=0 ; index < 4 ; index++) {
ee5c926e
 			if (prop_block[index].type > 0) {
6537bb75
 				prop_block[index].name_size = ole2_endian_convert_16(prop_block[index].name_size);
 				prop_block[index].prev = ole2_endian_convert_32(prop_block[index].prev);
 				prop_block[index].next = ole2_endian_convert_32(prop_block[index].next);
 				prop_block[index].child = ole2_endian_convert_32(prop_block[index].child);
736af806
 				prop_block[index].user_flags = ole2_endian_convert_32(prop_block[index].user_flags);
6537bb75
 				prop_block[index].create_lowdate = ole2_endian_convert_32(prop_block[index].create_lowdate);
 				prop_block[index].create_highdate = ole2_endian_convert_32(prop_block[index].create_highdate);
 				prop_block[index].mod_lowdate = ole2_endian_convert_32(prop_block[index].mod_lowdate);
 				prop_block[index].mod_highdate = ole2_endian_convert_32(prop_block[index].mod_highdate);
 				prop_block[index].start_block = ole2_endian_convert_32(prop_block[index].start_block);
 				prop_block[index].size = ole2_endian_convert_32(prop_block[index].size);
655b1363
 				if (prop_block[index].type > 5) {
 					cli_dbgmsg("ERROR: invalid property type: %d\n", prop_block[index].type);
 					return;
 				}
c561d2a3
 				if (prop_block[index].type == 5) {
ee5c926e
 					hdr->sbat_root_start = prop_block[index].start_block;
c561d2a3
 				}
 				print_ole2_property(&prop_block[index]);
655b1363
 				if (!handler(fd, hdr, &prop_block[index], dir)) {
 					cli_dbgmsg("ERROR: handler failed\n");
 					return;
 				}
c561d2a3
 			}
 		}
 		current_block = ole2_get_next_block_number(fd, hdr, current_block);
655b1363
 		/* Loop detection */
 		if (++count > 100000) {
 			cli_dbgmsg("ERROR: loop detected\n");
 			return;
 		}
c561d2a3
 	}
 	return;
 }
 
17874bd1
 static void 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),
21aa0b4d
 				int rec_level, int *file_count, const struct cl_limits *limits)
17874bd1
 {
 	property_t prop_block[4];
ad1366af
 	int32_t index, current_block, i;
17874bd1
 	unsigned char *dirname;
 	current_block = hdr->prop_start;
 	
21aa0b4d
 	if ((prop_index < 0) || (rec_level > 100) || (*file_count > 100000)) {
 		return;
 	}
 	
5a14dab6
 	if (limits && limits->maxfiles && (*file_count > limits->maxfiles)) {
21aa0b4d
 		cli_dbgmsg("OLE2: File limit reached (max: %d)\n", limits->maxfiles);
17874bd1
 		return;
 	}
 	
5a14dab6
 	if (limits && limits->maxreclevel && (rec_level > limits->maxreclevel)) {
 		cli_dbgmsg("OLE2: Recursion limit reached (max: %d)\n", limits->maxreclevel);
 		return;
 	}
 	
17874bd1
 	index = prop_index / 4;
 	for (i=0 ; i < index ; i++) {
 		current_block = ole2_get_next_block_number(fd, hdr, current_block);
 		if (current_block < 0) {
 			return;
 		}
 	}
 	index = prop_index % 4;
 	if (!ole2_read_block(fd, hdr, prop_block,
 			current_block)) {
 		return;
 	}	
 	if (prop_block[index].type <= 0) {
 		return;
 	}
 	prop_block[index].name_size = ole2_endian_convert_16(prop_block[index].name_size);
 	prop_block[index].prev = ole2_endian_convert_32(prop_block[index].prev);
 	prop_block[index].next = ole2_endian_convert_32(prop_block[index].next);
 	prop_block[index].child = ole2_endian_convert_32(prop_block[index].child);
 	prop_block[index].user_flags = ole2_endian_convert_32(prop_block[index].user_flags);
 	prop_block[index].create_lowdate = ole2_endian_convert_32(prop_block[index].create_lowdate);
 	prop_block[index].create_highdate = ole2_endian_convert_32(prop_block[index].create_highdate);
 	prop_block[index].mod_lowdate = ole2_endian_convert_32(prop_block[index].mod_lowdate);
 	prop_block[index].mod_highdate = ole2_endian_convert_32(prop_block[index].mod_highdate);
 	prop_block[index].start_block = ole2_endian_convert_32(prop_block[index].start_block);
 	prop_block[index].size = ole2_endian_convert_32(prop_block[index].size);
 	
 	print_ole2_property(&prop_block[index]);
 	switch (prop_block[index].type) {
 		case 5: /* Root Entry */
87d79d4e
 			if ((prop_index != 0) || (rec_level !=0) ||
21aa0b4d
 					(*file_count != 0)) {
17874bd1
 				/* Can only have RootEntry as the top */
 				cli_dbgmsg("ERROR: illegal Root Entry\n");
 				return;
 			}
 			hdr->sbat_root_start = prop_block[index].start_block;
 			ole2_walk_property_tree(fd, hdr, dir,
21aa0b4d
 				prop_block[index].prev, handler, rec_level+1, file_count, limits);
17874bd1
 			ole2_walk_property_tree(fd, hdr, dir,
21aa0b4d
 				prop_block[index].next, handler, rec_level+1, file_count, limits);
17874bd1
 			ole2_walk_property_tree(fd, hdr, dir,
21aa0b4d
 				prop_block[index].child, handler, rec_level+1, file_count, limits);
17874bd1
 			break;
 		case 2: /* File */
21aa0b4d
 			(*file_count)++;
17874bd1
 			if (!handler(fd, hdr, &prop_block[index], dir)) {
 				cli_dbgmsg("ERROR: handler failed\n");
27af0365
 				/* If we don't return on this error then
 					we can sometimes pull VBA code
 					from corrupted files.
 				*/
21aa0b4d
 			
17874bd1
 			}
 			ole2_walk_property_tree(fd, hdr, dir,
21aa0b4d
 				prop_block[index].prev, handler, rec_level, file_count, limits);
17874bd1
 			ole2_walk_property_tree(fd, hdr, dir,
21aa0b4d
 				prop_block[index].next, handler, rec_level, file_count, limits);
17874bd1
 			ole2_walk_property_tree(fd, hdr, dir,
21aa0b4d
 				prop_block[index].child, handler, rec_level, file_count, limits);
17874bd1
 			break;
 		case 1: /* Directory */
 			dirname = (char *) cli_malloc(strlen(dir)+8);
 			if (!dirname)  {
 				return;
 			}
 			snprintf(dirname, strlen(dir)+8, "%s/%.6d", dir, prop_index);
87d79d4e
 			if (mkdir(dirname, 0700) != 0) {
 				free(dirname);
 				return;
 			}
17874bd1
 			cli_dbgmsg("OLE2 dir entry: %s\n",dirname);
a19f21b6
 			ole2_walk_property_tree(fd, hdr, dir,
21aa0b4d
 				prop_block[index].prev, handler, rec_level+1, file_count, limits);
a19f21b6
 			ole2_walk_property_tree(fd, hdr, dir,
21aa0b4d
 				prop_block[index].next, handler, rec_level+1, file_count, limits);
17874bd1
 			ole2_walk_property_tree(fd, hdr, dirname,
21aa0b4d
 				prop_block[index].child, handler, rec_level+1, file_count, limits);
17874bd1
 			free(dirname);
 			break;
 		default:
3214b258
 			cli_dbgmsg("ERROR: unknown OLE2 entry type: %d\n", prop_block[index].type);
17874bd1
 			break;
 	}
 	return;
 }
c561d2a3
 /* Write file Handler - write the contents of the entry to a file */
06f64aa7
 static int handler_writefile(int fd, ole2_header_t *hdr, property_t *prop, const char *dir)
c561d2a3
 {
f09bbad9
 	unsigned char *buff;
3f3c6eb5
 	int32_t current_block, ofd, len, offset;
c561d2a3
 	char *name, *newname;
 
 	if (prop->type != 2) {
8a05efc5
 		/* Not a file */
655b1363
 		return TRUE;
 	}
 
 	if (prop->name_size > 64) {
 		cli_dbgmsg("\nERROR: property name too long: %d\n", prop->name_size);
 		return FALSE;
c561d2a3
 	}
 
 	if (! (name = get_property_name(prop->name, prop->name_size))) {
ee5c926e
 		/* File without a name - create a name for it */
ad1366af
 		off_t i;
ee5c926e
                                                                                                                             
 		i = lseek(fd, 0, SEEK_CUR);
655b1363
 		name = (char *) cli_malloc(11);
ee5c926e
 		if (!name) {
655b1363
 			return FALSE;
ee5c926e
 		}
ad1366af
 		snprintf(name, 11, "%.10ld", i + (long int) prop);
5f90b618
 	} else {
 		/* Sanitize the file name */
                 for(newname = name; *newname; newname++) {
12b1b744
 #ifdef  C_DARWIN
01ad7049
                         *newname &= '\177';
12b1b744
 #endif
193c72c5
 #if     defined(MSDOS) || defined(C_CYGWIN) || defined(WIN32) || defined(C_OS2)
5f90b618
                         if(strchr("/*?<>|\"+=,;: ", *newname))
 #else
                         if(*newname == '/')
 #endif
                                 *newname = '_';
                 }
c561d2a3
 	}
 
5f90b618
 
c561d2a3
 	newname = (char *) cli_malloc(strlen(name) + strlen(dir) + 2);
acf6a6ea
 	if (!newname) {
 		free(name);
 		return FALSE;
 	}
c561d2a3
 	sprintf(newname, "%s/%s", dir, name);
 	free(name);
 
 	ofd = open(newname, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
 	if (ofd < 0) {
873b5271
 		cli_errmsg("ERROR: failed to create file: %s\n", newname);
 		free(newname);
655b1363
 		return FALSE;
c561d2a3
 	}
 	free(newname);
 	current_block = prop->start_block;
 	len = prop->size;
 
f09bbad9
 	buff = (unsigned char *) cli_malloc(1 << hdr->log2_big_block_size);
 	if (!buff) {
 		close(ofd);
 		return FALSE;
 	}
 
c561d2a3
 	while((current_block >= 0) && (len > 0)) {
cfa196eb
 		if (prop->size < (int64_t)hdr->sbat_cutoff) {
8a05efc5
 			/* Small block file */
f09bbad9
 			if (!ole2_get_sbat_data_block(fd, hdr, buff, current_block)) {
c561d2a3
 				cli_dbgmsg("ole2_get_sbat_data_block failed\n");
 				close(ofd);
f09bbad9
 				free(buff);
655b1363
 				return FALSE;
c561d2a3
 			}
8a05efc5
 			/* buff now contains the block with 8 small blocks in it */
c561d2a3
 			offset = 64 * (current_block % 8);
66fcd9f8
 			if (cli_writen(ofd, &buff[offset], MIN(len,64)) != MIN(len,64)) {
c561d2a3
 				close(ofd);
f09bbad9
 				free(buff);
655b1363
 				return FALSE;
c561d2a3
 			}
 
 			len -= MIN(len,64);
 			current_block = ole2_get_next_sbat_block(fd, hdr, current_block);
 		} else {
8a05efc5
 			/* Big block file */
f09bbad9
 			if (!ole2_read_block(fd, hdr, buff, current_block)) {
c561d2a3
 				close(ofd);
f09bbad9
 				free(buff);
655b1363
 				return FALSE;
c561d2a3
 			}
f09bbad9
 			if (cli_writen(ofd, buff, MIN(len,(1 << hdr->log2_big_block_size))) !=
ee5c926e
 							MIN(len,(1 << hdr->log2_big_block_size))) {
c561d2a3
 				close(ofd);
f09bbad9
 				free(buff);
655b1363
 				return FALSE;
c561d2a3
 			}
 
 			current_block = ole2_get_next_block_number(fd, hdr, current_block);
ee5c926e
 			len -= MIN(len,(1 << hdr->log2_big_block_size));
c561d2a3
 		}
 	}
 	close(ofd);
f09bbad9
 	free(buff);
655b1363
 	return TRUE;
c561d2a3
 }
 
06f64aa7
 static int ole2_read_header(int fd, ole2_header_t *hdr)
3f3c6eb5
 {
 	int i;
 	
66fcd9f8
 	if (cli_readn(fd, &hdr->magic, 8) != 8) {
3f3c6eb5
 		return FALSE;
 	}
66fcd9f8
 	if (cli_readn(fd, &hdr->clsid, 16) != 16) {
3f3c6eb5
 		return FALSE;
 	}
66fcd9f8
 	if (cli_readn(fd, &hdr->minor_version, 2) != 2) {
3f3c6eb5
 		return FALSE;
 	}
66fcd9f8
 	if (cli_readn(fd, &hdr->dll_version, 2) != 2) {
3f3c6eb5
 		return FALSE;
 	}
66fcd9f8
 	if (cli_readn(fd, &hdr->byte_order, 2) != 2) {
3f3c6eb5
 		return FALSE;
 	}
66fcd9f8
 	if (cli_readn(fd, &hdr->log2_big_block_size, 2) != 2) {
3f3c6eb5
 		return FALSE;
 	}
66fcd9f8
 	if (cli_readn(fd, &hdr->log2_small_block_size, 4) != 4) {
3f3c6eb5
 		return FALSE;
 	}
66fcd9f8
 	if (cli_readn(fd, &hdr->reserved, 8) != 8) {
3f3c6eb5
 		return FALSE;
 	}
66fcd9f8
 	if (cli_readn(fd, &hdr->bat_count, 4) != 4) {
3f3c6eb5
 		return FALSE;
 	}
66fcd9f8
 	if (cli_readn(fd, &hdr->prop_start, 4) != 4) {
3f3c6eb5
 		return FALSE;
 	}
66fcd9f8
 	if (cli_readn(fd, &hdr->signature, 4) != 4) {
3f3c6eb5
 		return FALSE;
 	}
66fcd9f8
 	if (cli_readn(fd, &hdr->sbat_cutoff, 4) != 4) {
3f3c6eb5
 		return FALSE;
 	}
66fcd9f8
 	if (cli_readn(fd, &hdr->sbat_start, 4) != 4) {
3f3c6eb5
 		return FALSE;
 	}
66fcd9f8
 	if (cli_readn(fd, &hdr->sbat_block_count, 4) != 4) {
3f3c6eb5
 		return FALSE;
 	}
66fcd9f8
 	if (cli_readn(fd, &hdr->xbat_start, 4) != 4) {
3f3c6eb5
 		return FALSE;
 	}
66fcd9f8
 	if (cli_readn(fd, &hdr->xbat_count, 4) != 4) {
3f3c6eb5
 		return FALSE;
 	}
 	for (i=0 ; i < 109 ; i++) {
66fcd9f8
 		if (cli_readn(fd, &hdr->bat_array[i], 4) != 4) {
3f3c6eb5
 			return FALSE;
 		}
 	}
 	return TRUE;
 }
 
21aa0b4d
 int cli_ole2_extract(int fd, const char *dirname, const struct cl_limits *limits)
c561d2a3
 {
 	ole2_header_t hdr;
3f3c6eb5
 	int hdr_size;
df5ddd11
 	struct stat statbuf;
21aa0b4d
 	int file_count=0;
3f3c6eb5
 	
c561d2a3
 	cli_dbgmsg("in cli_ole2_extract()\n");
3f3c6eb5
 	
ee5c926e
 	/* size of header - size of other values in struct */
df5ddd11
 	hdr_size = sizeof(struct ole2_header_tag) - sizeof(int32_t) -
 			sizeof(unsigned char *) - sizeof(off_t);
3f3c6eb5
 
df5ddd11
 	hdr.m_area = NULL;
 
 #ifdef HAVE_MMAP
 	if (fstat(fd, &statbuf) == 0) {
d15bc08f
 		if (statbuf.st_size < hdr_size) {
 			return 0;
 		}
df5ddd11
 		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;
e1d357c4
 		} else {
 			cli_dbgmsg("mmap'ed file\n");
 			memcpy(&hdr, hdr.m_area, hdr_size);
df5ddd11
 		}
3f3c6eb5
 	}
 #endif
ee5c926e
 
df5ddd11
 	if (hdr.m_area == NULL) {
 #if defined(HAVE_ATTRIB_PACKED) || defined(HAVE_PRAGMA_PACK)
 		if (cli_readn(fd, &hdr, hdr_size) != hdr_size) {
 			return 0;
 		}
 #else
 		if (!ole2_read_header(fd, &hdr)) {
 			return 0;
 		}
 #endif
 	}
 	
6537bb75
 	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);
 
ee5c926e
 	hdr.sbat_root_start = -1;
df5ddd11
 	
c561d2a3
 	if (strncmp(hdr.magic, magic_id, 8) != 0) {
 		cli_dbgmsg("OLE2 magic failed!\n");
873b5271
 #ifdef HAVE_MMAP
 		if (hdr.m_area != NULL) {
 			munmap(hdr.m_area, hdr.m_length);
 		}
 #endif
c561d2a3
 		return CL_EOLE2;
 	}
 
 	if (hdr.log2_big_block_size != 9) {
aa6994e7
 		cli_errmsg("WARNING: not scanned; untested big block size - please report\n");
 		goto abort;
c561d2a3
 	}
 	if (hdr.log2_small_block_size != 6) {
aa6994e7
 		cli_errmsg("WARNING: not scanned; untested small block size - please report\n");
 		goto abort;
c561d2a3
 	}
 	if (hdr.sbat_cutoff != 4096) {
aa6994e7
 		cli_errmsg("WARNING: not scanned; untested sbat cutoff - please report\n");
 		goto abort;
c561d2a3
 	}
df5ddd11
 	
c561d2a3
 	print_ole2_header(&hdr);
 
17874bd1
 	/* NOTE: Select only ONE of the following two methods */
 	
a19f21b6
 	/* ole2_read_property_tree(fd, &hdr, dirname, handler_writefile); */
17874bd1
 	
 	/* OR */
 	
21aa0b4d
 	ole2_walk_property_tree(fd, &hdr, dirname, 0, handler_writefile, 0, &file_count, limits);
df5ddd11
 
aa6994e7
 abort:
df5ddd11
 #ifdef HAVE_MMAP
 	if (hdr.m_area != NULL) {
 		munmap(hdr.m_area, hdr.m_length);
 	}
 #endif
c561d2a3
 	return 0;
 }