libclamav/vba_extract.c
47bbbc56
 /*
f893c0f3
  *  Extract VBA source code for component MS Office Documents
47bbbc56
  *
3863b5ce
  *  Copyright (C) 2004-2005 trog@uncon.org
47bbbc56
  *
  *  This code is based on the OpenOffice and libgsf sources.
fa53d800
  *
47bbbc56
  *  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
48b7b4a7
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
47bbbc56
  */
fa53d800
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
47bbbc56
 
 #include <stdio.h>
 #include <string.h>
b58fdfc2
 #ifdef	HAVE_UNISTD_H
47bbbc56
 #include <unistd.h>
b58fdfc2
 #endif
47bbbc56
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <stdlib.h>
 #include <ctype.h>
5f02033a
 #include <zlib.h>
 
 #include "clamav.h"
47bbbc56
 
 #include "vba_extract.h"
ca90717f
 #include "others.h"
fa53d800
 #include "blob.h"
47bbbc56
 
b58fdfc2
 #ifndef	O_BINARY
 #define	O_BINARY	0
 #endif
 
ffd168d4
 #ifndef HAVE_ATTRIB_PACKED
 #define __attribute__(x)
 #endif
 
9294cf21
 #define PPT_LZW_BUFFSIZE 8192
 #define NUM_VBA_VERSIONS 14
 #define VBA_COMPRESSION_WINDOW 4096
 
ffd168d4
 #ifdef HAVE_PRAGMA_PACK
 #pragma pack(1)
 #endif
 
 #ifdef HAVE_PRAGMA_PACK_HPPA
 #pragma pack 1
 #endif
 
 struct vba56_header {
 	unsigned char magic[2];
 	unsigned char version[4];
 	uint16_t ooff __attribute__ ((packed));	/* 0x00FF */
 	uint32_t LidA __attribute__ ((packed));  /* Language identifiers */
 	uint32_t LidB __attribute__ ((packed));
 	uint16_t CharSet __attribute__ ((packed));
 	uint16_t LenA __attribute__ ((packed));
 	uint32_t UnknownB __attribute__ ((packed));
 	uint32_t UnknownC __attribute__ ((packed));
 	uint16_t LenB __attribute__ ((packed));
 	uint16_t LenC __attribute__ ((packed));
 	uint16_t LenD __attribute__ ((packed));
 };
 
 #ifdef HAVE_PRAGMA_PACK
 #pragma pack()
 #endif
 
 #ifdef HAVE_PRAGMA_PACK_HPPA
 #pragma pack
 #endif
 
47bbbc56
 typedef struct vba_version_tag {
 	unsigned char signature[4];
 	const char *name;
 	int is_mac;
 } vba_version_t;
 
58302349
 static uint16_t vba_endian_convert_16(uint16_t value, int is_mac)
31c42eb7
 {
75282b5c
 	if (is_mac)
ffd168d4
 		return (uint16_t)be16_to_host(value);
75282b5c
 	else
 		return le16_to_host(value);
337cb206
 }
fa53d800
 
58302349
 static uint32_t vba_endian_convert_32(uint32_t value, int is_mac)
31c42eb7
 {
75282b5c
 	if (is_mac)
 		return be32_to_host(value);
 	else
 		return le32_to_host(value);
31c42eb7
 }
337cb206
 
ac8154d9
 static const vba_version_t vba_version[NUM_VBA_VERSIONS] = {
9294cf21
 	{ { 0x5e, 0x00, 0x00, 0x01 }, "Office 97",              FALSE},
 	{ { 0x5f, 0x00, 0x00, 0x01 }, "Office 97 SR1",          FALSE },
 	{ { 0x65, 0x00, 0x00, 0x01 }, "Office 2000 alpha?",     FALSE },
 	{ { 0x6b, 0x00, 0x00, 0x01 }, "Office 2000 beta?",      FALSE },
 	{ { 0x6d, 0x00, 0x00, 0x01 }, "Office 2000",            FALSE },
 	{ { 0x6f, 0x00, 0x00, 0x01 }, "Office 2000",            FALSE },
 	{ { 0x70, 0x00, 0x00, 0x01 }, "Office XP beta 1/2",     FALSE },
 	{ { 0x73, 0x00, 0x00, 0x01 }, "Office XP",              FALSE },
 	{ { 0x76, 0x00, 0x00, 0x01 }, "Office 2003",            FALSE },
 	{ { 0x79, 0x00, 0x00, 0x01 }, "Office 2003",            FALSE },
 	{ { 0x60, 0x00, 0x00, 0x0e }, "MacOffice 98",           TRUE },
 	{ { 0x62, 0x00, 0x00, 0x0e }, "MacOffice 2001",         TRUE },
 	{ { 0x63, 0x00, 0x00, 0x0e }, "MacOffice X",		TRUE },
 	{ { 0x64, 0x00, 0x00, 0x0e }, "MacOffice 2004",         TRUE },
47bbbc56
 };
 
fa53d800
 static char *
 get_unicode_name(const char *name, int size, int is_mac)
47bbbc56
 {
69380565
         int i, increment;
         char *newname, *ret;
25ba8c63
 
69380565
 	if((name == NULL) || (*name == '\0') || (size <= 0))
47bbbc56
                 return NULL;
25ba8c63
 
69380565
         newname = (char *)cli_malloc(size * 7);
         if(newname == NULL)
47bbbc56
                 return NULL;
69380565
 
 	if((!is_mac) && (size & 0x1)) {
 		cli_dbgmsg("get_unicode_name: odd number of bytes %d\n", size);
 		--size;
 	}
 
 	increment = (is_mac) ? 1 : 2;
 	ret = newname;
 
         for(i = 0; i < size; i += increment) {
                 if(isprint(name[i]))
                         *ret++ = name[i];
                 else {
 			if(name[i] < 10 && name[i] >= 0) {
 				*ret++ = '_';
 				*ret++ = (char)(name[i] + '0');
 			} else {
ffd168d4
 				const uint16_t x = (uint16_t)(((name[i]) << 8) | name[i + 1]);
 
69380565
 				*ret++ = '_';
 				*ret++ = (char)('a'+((x&0xF)));
 				*ret++ = (char)('a'+((x>>4)&0xF));
 				*ret++ = (char)('a'+((x>>8)&0xF));
ac8154d9
 #if	0
69380565
 				*ret++ = (char)('a'+((x>>16)&0xF));	/* FIXME: x>>16 MUST == 0 */
 				*ret++ = (char)('a'+((x>>24)&0xF));	/* FIXME: x>>24 MUST == 0 */
ac8154d9
 #endif
9e7e2c76
 			}
69380565
 			*ret++ = '_';
 		}
 	}
 
         *ret = '\0';
47bbbc56
         return newname;
 }
349e0502
 
 static void vba56_test_middle(int fd)
 {
 	char test_middle[20];
fe0af0c1
 
 	/* MacOffice middle */
 	static const uint8_t middle1_str[20] = {
dc890a72
 		0x00, 0x01, 0x0d, 0x45, 0x2e, 0xe1, 0xe0, 0x8f, 0x10, 0x1a,
 		0x85, 0x2e, 0x02, 0x60, 0x8c, 0x4d, 0x0b, 0xb4, 0x00, 0x00
349e0502
 	};
fe0af0c1
 	/* MS Office middle */
 	static const uint8_t middle2_str[20] = {
fa53d800
 		0x00, 0x00, 0xe1, 0x2e, 0x45, 0x0d, 0x8f, 0xe0, 0x1a, 0x10,
fe0af0c1
 		0x85, 0x2e, 0x02, 0x60, 0x8c, 0x4d, 0x0b, 0xb4, 0x00, 0x00
 	};
349e0502
 
ffd168d4
 	if(cli_readn(fd, &test_middle, 20) != 20) {
 		return;
 	}
25ba8c63
 
fe0af0c1
 	if ((memcmp(test_middle, middle1_str, 20) != 0) &&
 		(memcmp(test_middle, middle2_str, 20) != 0)) {
dc890a72
 		cli_dbgmsg("middle not found\n");
ffd168d4
 		lseek(fd, -20, SEEK_CUR);
dc890a72
 	} else {
 		cli_dbgmsg("middle found\n");
349e0502
 	}
 	return;
 }
 
ac8154d9
 static int
 vba_read_project_strings(int fd, int is_mac)
dc890a72
 {
 	for (;;) {
d92098c8
 		uint32_t offset;
 		uint16_t length;
 		unsigned char *buff;
 		char *name;
 
5b25b5e8
 		if (cli_readn(fd, &length, 2) != 2) {
dc890a72
 			return FALSE;
 		}
 		length = vba_endian_convert_16(length, is_mac);
 		if (length < 6) {
 			lseek(fd, -2, SEEK_CUR);
 			break;
 		}
 		buff = (unsigned char *) cli_malloc(length);
 		if (!buff) {
 			cli_errmsg("cli_malloc failed\n");
 			return FALSE;
 		}
 		offset = lseek(fd, 0, SEEK_CUR);
ffd168d4
 
 		if (cli_readn(fd, buff, length) != (int)length) {
dc890a72
 			cli_dbgmsg("read name failed - rewinding\n");
 			lseek(fd, offset, SEEK_SET);
5b25b5e8
 			free(buff);
dc890a72
 			break;
 		}
fa53d800
 		name = get_unicode_name((const char *)buff, length, is_mac);
ac8154d9
 		if (name)
 			cli_dbgmsg("length: %d, name: %s\n", length, name);
 		else
 			cli_dbgmsg("length: %d, name: [null]\n", length);
dc890a72
 		free(buff);
 
58302349
 		/* Ignore twelve bytes from entries of type 'G'.
dc890a72
 		   Type 'C' entries come in pairs, the second also
 		   having a 12 byte trailer */
 		/* TODO: Need to check if types H(same as G) and D(same as C) exist */
ac8154d9
 		if((name == NULL) || (memcmp("*\\", name, 2) != 0) ||
 		   (strchr("GCHD", name[2]) == NULL)) {
dc890a72
 			/* Unknown type - probably ran out of strings - rewind */
 			lseek(fd, -(length+2), SEEK_CUR);
ac8154d9
 			if(name)
91aaa0ea
 				free(name);
dc890a72
 			break;
 		}
 		free(name);
ac8154d9
 
 		if (cli_readn(fd, &length, 2) != 2)
 			return FALSE;
 
 		length = vba_endian_convert_16(length, is_mac);
 		if ((length != 0) && (length != 65535)) {
 			lseek(fd, -2, SEEK_CUR);
 			continue;
 		}
 		offset = lseek(fd, 10, SEEK_CUR);
4c64f434
 		cli_dbgmsg("offset: %u\n", offset);
dc890a72
 		vba56_test_middle(fd);
 	}
 	return TRUE;
 }
349e0502
 
47bbbc56
 vba_project_t *vba56_dir_read(const char *dir)
 {
58302349
 	unsigned char *buff;
ffd168d4
 	const unsigned char vba56_signature[] = { 0xcc, 0x61 };
31c42eb7
 	uint16_t record_count, length;
ffd168d4
 	uint16_t ffff;
4c64f434
 	uint16_t byte_count;
47bbbc56
 	uint32_t offset;
ffd168d4
 	int i, fd, is_mac;
47bbbc56
 	vba_project_t *vba_project;
ffd168d4
 	struct vba56_header v56h;
d92098c8
 	char fullname[NAME_MAX + 1];
47bbbc56
 
f893c0f3
 	cli_dbgmsg("in vba56_dir_read()\n");
 
d92098c8
 	snprintf(fullname, sizeof(fullname) - 1, "%s/_VBA_PROJECT", dir);
b58fdfc2
         fd = open(fullname, O_RDONLY|O_BINARY);
47bbbc56
 
         if (fd == -1) {
f893c0f3
                 cli_dbgmsg("Can't open %s\n", fullname);
cdcb8f73
 		/* vba56_old_dir_read(dir); */
47bbbc56
                 return NULL;
         }
 
ffd168d4
 	if(cli_readn(fd, &v56h, sizeof(struct vba56_header)) != sizeof(struct vba56_header)) {
39ea36b7
 		close(fd);
47bbbc56
 		return NULL;
 	}
ffd168d4
 	if (memcmp(v56h.magic, vba56_signature, sizeof(v56h.magic)) != 0) {
39ea36b7
 		close(fd);
47bbbc56
 		return NULL;
 	}
 
ffd168d4
 	for(i = 0; i < NUM_VBA_VERSIONS; i++)
 		if(memcmp(v56h.version, vba_version[i].signature, sizeof(vba_version[i].signature)) == 0)
47bbbc56
 			break;
 
 	if (i == NUM_VBA_VERSIONS) {
11a1fcf5
 		cli_warnmsg("Unknown VBA version signature %x %x %x %x\n",
ffd168d4
 			v56h.version[0], v56h.version[1],
 			v56h.version[2], v56h.version[3]);
 		switch(v56h.version[3]) {
 			case 0x01:
 				cli_warnmsg("Guessing little-endian\n");
 				is_mac = FALSE;
 				break;
 			case 0x0E:
 				cli_warnmsg("Guessing big-endian\n");
 				is_mac = TRUE;
 				break;
 			default:
 				cli_warnmsg("Unable to guess VBA type\n");
 				close(fd);
 				return NULL;
fa53d800
 		}
11a1fcf5
 	} else {
9294cf21
 		cli_dbgmsg("VBA Project: %s\n", vba_version[i].name);
11a1fcf5
 		is_mac = vba_version[i].is_mac;
47bbbc56
 	}
 
dc890a72
 	if (!vba_read_project_strings(fd, is_mac)) {
39ea36b7
 		close(fd);
47bbbc56
 		return NULL;
 	}
fa53d800
 
47bbbc56
 	/* junk some more stuff */
 	do {
ffd168d4
 		if (cli_readn(fd, &ffff, 2) != 2) {
39ea36b7
 			close(fd);
47bbbc56
 			return NULL;
 		}
ffd168d4
 	} while(ffff != 0xFFFF);
cee86c13
 
 	/* check for alignment error */
 	lseek(fd, -3, SEEK_CUR);
ffd168d4
 	if (cli_readn(fd, &ffff, 2) != 2) {
fa53d800
 		close(fd);
cee86c13
 		return NULL;
 	}
ffd168d4
 	if (ffff != 0xFFFF) {
cee86c13
 		lseek(fd, 1, SEEK_CUR);
 	}
fa53d800
 
ffd168d4
 	if (cli_readn(fd, &ffff, 2) != 2) {
39ea36b7
 		close(fd);
47bbbc56
 		return NULL;
 	}
 
 	/* no idea what this stuff is */
ffd168d4
 	if (ffff != 0xFFFF) {
 		ffff = vba_endian_convert_16(ffff, is_mac);
 		lseek(fd, ffff, SEEK_CUR);
47bbbc56
 	}
ffd168d4
 	if (cli_readn(fd, &ffff, 2) != 2) {
39ea36b7
 		close(fd);
47bbbc56
 		return NULL;
 	}
ffd168d4
 	if (ffff != 0xFFFF) {
 		ffff = vba_endian_convert_16(ffff, is_mac);
 		lseek(fd, ffff, SEEK_CUR);
47bbbc56
 	}
 	lseek(fd, 100, SEEK_CUR);
 
5b25b5e8
 	if (cli_readn(fd, &record_count, 2) != 2) {
39ea36b7
 		close(fd);
47bbbc56
 		return NULL;
 	}
31c42eb7
 	record_count = vba_endian_convert_16(record_count, is_mac);
47bbbc56
 	cli_dbgmsg("\nVBA Record count: %d\n", record_count);
8bf5929e
 	if (record_count == 0) {
 		close(fd);
ffd168d4
 		return NULL;
 	}
4c64f434
 	if (record_count > 1000) {
 		/* Almost certainly an error */
 		cli_dbgmsg("\nVBA Record count too big");
 		close(fd);
 		return NULL;
 	}
fa53d800
 
47bbbc56
 	vba_project = (vba_project_t *) cli_malloc(sizeof(struct vba_project_tag));
bf34c7e7
 	if (!vba_project) {
 		close(fd);
 		return NULL;
 	}
47bbbc56
 	vba_project->name = (char **) cli_malloc(sizeof(char *) * record_count);
bf34c7e7
 	if (!vba_project->name) {
 		free(vba_project);
 		close(fd);
 		return NULL;
 	}
6f38c939
 	vba_project->dir = cli_strdup(dir);
47bbbc56
 	vba_project->offset = (uint32_t *) cli_malloc (sizeof(uint32_t) *
 					record_count);
bf34c7e7
 	if (!vba_project->offset) {
 		free(vba_project->dir);
 		free(vba_project->name);
 		free(vba_project);
 		close(fd);
 		return NULL;
 	}
47bbbc56
 	vba_project->count = record_count;
ffd168d4
 	for(i = 0; i < record_count; i++) {
 		if(cli_readn(fd, &length, 2) != 2)
 			break;
 
31c42eb7
 		length = vba_endian_convert_16(length, is_mac);
dd1f3146
 		if (length == 0) {
 			cli_dbgmsg("zero name length\n");
ffd168d4
 			break;
 		}
349e0502
 		buff = (unsigned char *) cli_malloc(length);
47bbbc56
 		if (!buff) {
 			cli_dbgmsg("cli_malloc failed\n");
ffd168d4
 			break;
47bbbc56
 		}
5b25b5e8
 		if (cli_readn(fd, buff, length) != length) {
47bbbc56
 			cli_dbgmsg("read name failed\n");
39ea36b7
 			free(buff);
ffd168d4
 			break;
47bbbc56
 		}
fa53d800
 		vba_project->name[i] = get_unicode_name((const char *)buff, length, is_mac);
ac8154d9
 		free(buff);
57babcae
 		if (!vba_project->name[i]) {
 			offset = lseek(fd, 0, SEEK_CUR);
 			vba_project->name[i] = (char *) cli_malloc(18);
ffd168d4
 			if(vba_project->name[i] == NULL) {
 				break;
 			}
 			snprintf(vba_project->name[i], 18, "clamav-%.10d", (int)offset);
57babcae
 		}
47bbbc56
 		cli_dbgmsg("project name: %s, ", vba_project->name[i]);
 
 		/* some kind of string identifier ?? */
5b25b5e8
 		if (cli_readn(fd, &length, 2) != 2) {
39ea36b7
 			free(vba_project->name[i]);
ffd168d4
 			break;
47bbbc56
 		}
31c42eb7
 		length = vba_endian_convert_16(length, is_mac);
47bbbc56
 		lseek(fd, length, SEEK_CUR);
 
 		/* unknown stuff */
ffd168d4
 		if (cli_readn(fd, &ffff, 2) != 2) {
39ea36b7
 			free(vba_project->name[i]);
ffd168d4
 			break;
47bbbc56
 		}
ffd168d4
 		ffff = vba_endian_convert_16(ffff, is_mac);
 		if (ffff == 0xFFFF) {
47bbbc56
 			lseek(fd, 2, SEEK_CUR);
ffd168d4
 			if (cli_readn(fd, &ffff, 2) != 2) {
39ea36b7
 				free(vba_project->name[i]);
ffd168d4
 				break;
47bbbc56
 			}
ffd168d4
 			ffff = vba_endian_convert_16(ffff, is_mac);
 			lseek(fd, ffff, SEEK_CUR);
47bbbc56
 		} else {
ffd168d4
 			lseek(fd, 2 + ffff, SEEK_CUR);
47bbbc56
 		}
 
 		lseek(fd, 8, SEEK_CUR);
4c64f434
 		if (cli_readn(fd, &byte_count, 2) != 2) {
39ea36b7
 			free(vba_project->name[i]);
ffd168d4
 			break;
47bbbc56
 		}
4c64f434
 		byte_count = vba_endian_convert_16(byte_count, is_mac);
ffd168d4
 		lseek(fd, (8 * byte_count) + 5, SEEK_CUR);
5b25b5e8
 		if (cli_readn(fd, &offset, 4) != 4) {
39ea36b7
 			free(vba_project->name[i]);
ffd168d4
 			break;
47bbbc56
 		}
31c42eb7
 		offset = vba_endian_convert_32(offset, is_mac);
47bbbc56
 		vba_project->offset[i] = offset;
4c64f434
 		cli_dbgmsg("offset:%u\n", offset);
47bbbc56
 		lseek(fd, 2, SEEK_CUR);
 	}
fa53d800
 
ffd168d4
 	close(fd);
fa53d800
 
ffd168d4
 	if(i < record_count) {
 		/* above loop failed */
 		while(--i >= 0)
 			free(vba_project->name[i]);
47bbbc56
 
ffd168d4
 		free(vba_project->name);
 		free(vba_project->dir);
 		free(vba_project->offset);
 		free(vba_project);
 		return NULL;
47bbbc56
 	}
39ea36b7
 
ffd168d4
 	return vba_project;
47bbbc56
 }
 
ea399527
 unsigned char *vba_decompress(int fd, uint32_t offset, int *size)
47bbbc56
 {
 	unsigned int i, pos=0, shift, win_pos, clean=TRUE, mask, distance;
 	uint8_t flag;
 	uint16_t token, len;
fa53d800
 	size_t s;
 	blob *b;
 	unsigned char *ret;
47bbbc56
 	unsigned char buffer[VBA_COMPRESSION_WINDOW];
fa53d800
 
 	b = blobCreate();
 
 	if(b == NULL)
 		return NULL;
 
 	lseek(fd, offset+3, SEEK_SET); /* 1byte ?? , 2byte length ?? */
 
5b25b5e8
 	while (cli_readn(fd, &flag, 1) == 1) {
47bbbc56
 		for (mask = 1; mask < 0x100; mask<<=1) {
 			if (flag & mask) {
5b25b5e8
 				if (cli_readn(fd, &token, 2) != 2) {
fa53d800
 					blobDestroy(b);
ea399527
 					if (size) {
 						*size = 0;
 					}
39ea36b7
 					return NULL;
47bbbc56
 				}
31c42eb7
 				token = vba_endian_convert_16(token, FALSE);
47bbbc56
 				win_pos = pos % VBA_COMPRESSION_WINDOW;
 				if (win_pos <= 0x80) {
 					if (win_pos <= 0x20) {
 						shift = (win_pos <= 0x10) ? 12:11;
 					} else {
 						shift = (win_pos <= 0x40) ? 10:9;
 					}
 				} else {
 					if (win_pos <= 0x200) {
 						shift = (win_pos <= 0x100) ? 8:7;
 					} else if (win_pos <= 0x800) {
 						shift = (win_pos <= 0x400) ? 6:5;
 					} else {
 						shift = 4;
 					}
 				}
fa53d800
 				len = (uint16_t)((token & ((1 << shift) -1)) + 3);
47bbbc56
 				distance = token >> shift;
 				clean = TRUE;
fa53d800
 
47bbbc56
 				for (i=0 ; i < len; i++) {
 					unsigned int srcpos;
 					unsigned char c;
fa53d800
 
47bbbc56
 					srcpos = (pos - distance - 1) % VBA_COMPRESSION_WINDOW;
 					c = buffer[srcpos];
 					buffer[pos++ % VBA_COMPRESSION_WINDOW]= c;
 				}
 			} else {
 				if ((pos != 0) &&
 					((pos % VBA_COMPRESSION_WINDOW) == 0) && clean) {
fa53d800
 
5b25b5e8
 					if (cli_readn(fd, &token, 2) != 2) {
fa53d800
 						blobDestroy(b);
 						if(size)
 						       *size = 0;
39ea36b7
 						return NULL;
47bbbc56
 					}
 					clean = FALSE;
fa53d800
 					(void)blobAddData(b, buffer, VBA_COMPRESSION_WINDOW);
47bbbc56
 					break;
 				}
5b25b5e8
 				if (cli_readn(fd, buffer+(pos%VBA_COMPRESSION_WINDOW), 1) == 1){
47bbbc56
 					pos++;
 				}
 				clean = TRUE;
 			}
 		}
 	}
 
fa53d800
 	if (pos % VBA_COMPRESSION_WINDOW)
 		if(blobAddData(b, buffer, pos%VBA_COMPRESSION_WINDOW) < 0) {
 			if(size)
 			       *size = 0;
 			blobDestroy(b);
 			return NULL;
 		}
 	s = blobGetDataSize(b);
 	ret = cli_malloc(s);
 	if(ret == NULL) {
 		blobDestroy(b);
 		if(size)
 		       *size = 0;
 		return NULL;
 	}
 	if(size)
 		*size = (int)s;
 	memcpy(ret, blobGetData(b), s);
 	blobDestroy(b);
 	return ret;
47bbbc56
 }
7b9aed8c
 
892d2f56
 static uint32_t ole_copy_file_data(int ifd, int ofd, uint32_t len)
 {
         unsigned int count, rem;
fa53d800
         unsigned char data[FILEBUFF];
892d2f56
 
         rem = len;
 
         while (rem > 0) {
fa53d800
                 unsigned int todo = MIN(sizeof(data), rem);
 
892d2f56
                 count = cli_readn(ifd, data, todo);
                 if (count != todo) {
                         return len-rem;
                 }
fa53d800
                 if((unsigned int)cli_writen(ofd, data, count) != count)
892d2f56
                         return len-rem-count;
                 rem -= count;
         }
         return len;
 }
 
 int cli_decode_ole_object(int fd, const char *dir)
 {
 	int ofd;
 	struct stat statbuf;
ed0fd157
 	char ch;
892d2f56
 	uint32_t object_size;
ed0fd157
 	char fullname[NAME_MAX + 1];
892d2f56
 
 	if (fstat(fd, &statbuf) == -1) {
 		return -1;
 	}
fa53d800
 
892d2f56
 	if (cli_readn(fd, &object_size, 4) != 4) {
 		return -1;
 	}
 	object_size = vba_endian_convert_32(object_size, FALSE);
 
 	if ((statbuf.st_size -  object_size) >= 4) {
 		/* Probably the OLE type id */
 		if (lseek(fd, 2, SEEK_CUR) == -1) {
 			return -1;
 		}
fa53d800
 
892d2f56
 		/* Skip attachment name */
 		do {
 			if (cli_readn(fd, &ch, 1) != 1) {
 				return -1;
 			}
 		} while (ch);
fa53d800
 
892d2f56
 		/* Skip attachment full path */
 		do {
 			if (cli_readn(fd, &ch, 1) != 1) {
 				return -1;
 			}
 		} while (ch);
fa53d800
 
892d2f56
 		/* Skip unknown data */
 		if (lseek(fd, 8, SEEK_CUR) == -1) {
 			return -1;
 		}
fa53d800
 
892d2f56
 		/* Skip attachment full path */
 		do {
 			if (cli_readn(fd, &ch, 1) != 1) {
 				return -1;
 			}
 		} while (ch);
fa53d800
 
892d2f56
 		if (cli_readn(fd, &object_size, 4) != 4) {
 			return -1;
 		}
 		object_size = vba_endian_convert_32(object_size, FALSE);
 	}
ed0fd157
 	snprintf(fullname, sizeof(fullname) - 1, "%s/_clam_ole_object", dir);
b58fdfc2
 	ofd = open(fullname, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0600);
ffd168d4
 	if (ofd < 0) {
892d2f56
 		return -1;
 	}
 	ole_copy_file_data(fd, ofd, object_size);
c2d5447d
 	lseek(ofd, 0, SEEK_SET);
892d2f56
 	return ofd;
 }
 
7b9aed8c
 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
cdcb8f73
 /* Code to extract Power Point Embedded OLE2 Objects		     */
5f02033a
 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
 
 typedef struct atom_header_tag {
 	off_t foffset;
 	uint16_t ver_inst;
 	uint8_t version;
 	uint16_t instance;
 	uint16_t type;
 	uint32_t length;
 } atom_header_t;
 
 static int ppt_read_atom_header(int fd, atom_header_t *atom_header)
 {
 	atom_header->foffset = lseek(fd, 0, SEEK_CUR);
 	if (cli_readn(fd, &atom_header->ver_inst, 2) != 2) {
 		cli_dbgmsg("read ppt_current_user failed\n");
 		return FALSE;
92cf99f3
 	}
 	atom_header->ver_inst = vba_endian_convert_16(atom_header->ver_inst, FALSE);
fa53d800
 	atom_header->version = (uint8_t)(atom_header->ver_inst & 0x000f);
ffd168d4
 	atom_header->instance = (uint16_t)(atom_header->ver_inst >> 4);
 	if(cli_readn(fd, &atom_header->type, sizeof(uint16_t)) != sizeof(uint16_t)) {
5f02033a
 		cli_dbgmsg("read ppt_current_user failed\n");
 		return FALSE;
 	}
 	if (cli_readn(fd, &atom_header->length, 4) != 4) {
 		cli_dbgmsg("read ppt_current_user failed\n");
 		return FALSE;
 	}
92cf99f3
 	atom_header->type = vba_endian_convert_16(atom_header->type, FALSE);
 	atom_header->length = vba_endian_convert_32(atom_header->length, FALSE);
5f02033a
 	return TRUE;
 }
 
 static void ppt_print_atom_header(atom_header_t *atom_header)
 {
 	cli_dbgmsg("Atom Hdr:\n");
 	cli_dbgmsg("  Version: 0x%.2x\n", atom_header->version);
 	cli_dbgmsg("  Instance: 0x%.4x\n", atom_header->instance);
 	cli_dbgmsg("  Type: 0x%.4x\n", atom_header->type);
 	cli_dbgmsg("  Length: 0x%.8x\n", atom_header->length);
 }
 
 static int ppt_unlzw(const char *dir, int fd, uint32_t length)
 {
 	int ofd, retval;
 	unsigned char inbuff[PPT_LZW_BUFFSIZE], outbuff[PPT_LZW_BUFFSIZE];
 	uint32_t bufflen;
 	z_stream stream;
9294cf21
 	char fullname[NAME_MAX + 1];
fa53d800
 
9294cf21
 	snprintf(fullname, sizeof(fullname) - 1, "%s/ppt%.8lx.doc",
 		dir, (long)lseek(fd, 0L, SEEK_CUR));
fa53d800
 
b58fdfc2
 	ofd = open(fullname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0600);
ffd168d4
 	if (ofd == -1) {
 		cli_dbgmsg("ppt_unlzw Open outfile failed\n");
 		return FALSE;
 	}
fa53d800
 
5f02033a
 	stream.zalloc = Z_NULL;
 	stream.zfree = Z_NULL;
 	stream.opaque = (void *)0;
fa53d800
 
5f02033a
 	stream.next_in = inbuff;
 	bufflen = stream.avail_in = MIN(length, PPT_LZW_BUFFSIZE);
fa53d800
 
9d3c38ba
 	if (cli_readn(fd, inbuff, stream.avail_in) != (int64_t)stream.avail_in) {
5f02033a
 		close(ofd);
ac8154d9
 		unlink(fullname);
5f02033a
 		return FALSE;
 	}
 	length -= stream.avail_in;
fa53d800
 
5f02033a
 	retval = inflateInit(&stream);
 	if (retval != Z_OK) {
 		cli_dbgmsg(" ppt_unlzw !Z_OK: %d\n", retval);
 	}
fa53d800
 
5f02033a
 	stream.next_out = outbuff;
 	stream.avail_out = PPT_LZW_BUFFSIZE;
fa53d800
 
5f02033a
 	do {
 		if (stream.avail_out == 0) {
 			if (cli_writen(ofd, outbuff, PPT_LZW_BUFFSIZE)
 						!= PPT_LZW_BUFFSIZE) {
 				close(ofd);
 				inflateEnd(&stream);
 				return FALSE;
 			}
 			stream.next_out = outbuff;
 			stream.avail_out = PPT_LZW_BUFFSIZE;
 		}
 		if (stream.avail_in == 0) {
 			stream.next_in = inbuff;
 			bufflen = stream.avail_in = MIN(length, PPT_LZW_BUFFSIZE);
9d3c38ba
 			if (cli_readn(fd, inbuff, stream.avail_in) != (int64_t)stream.avail_in) {
5f02033a
 				close(ofd);
 				inflateEnd(&stream);
 				return FALSE;
 			}
 			length -= stream.avail_in;
 		}
 		retval = inflate(&stream, Z_NO_FLUSH);
 	} while (retval == Z_OK);
fa53d800
 
9d3c38ba
 	if (cli_writen(ofd, outbuff, bufflen) != (int64_t)bufflen) {
5f02033a
 		close(ofd);
 		inflateEnd(&stream);
 		return FALSE;
 	}
 	inflateEnd(&stream);
ffd168d4
 	return close(ofd);
5f02033a
 }
 
42034091
 static char *ppt_stream_iter(int fd)
 {
 	uint32_t ole_id;
0c7019c5
 	char *out_dir;
3863b5ce
 	off_t offset;
fa53d800
 	atom_header_t atom_header;
 
42034091
 	/* Create a directory to store the extracted OLE2 objects */
0c7019c5
 	out_dir = cli_gentemp(NULL);
42034091
 	if(mkdir(out_dir, 0700)) {
fa53d800
 		cli_errmsg("ScanOLE2 -> Can't create temporary directory %s\n", out_dir);
 		free(out_dir);
 		return NULL;
42034091
 	}
 
fa53d800
 	while(ppt_read_atom_header(fd, &atom_header)) {
42034091
 		ppt_print_atom_header(&atom_header);
 
3863b5ce
 		if (atom_header.length == 0) {
8c601f9f
 			cli_rmdirs(out_dir);
 			free(out_dir);
 			return NULL;
 		}
 
42034091
 		if (atom_header.type == 0x1011) {
 			if (cli_readn(fd, &ole_id, 4) != 4) {
 				cli_dbgmsg("read ole_id failed\n");
 				cli_rmdirs(out_dir);
 				free(out_dir);
 				return NULL;
 			}
 			ole_id = vba_endian_convert_32(ole_id, FALSE);
 			cli_dbgmsg("OleID: %d, length: %d\n",
fa53d800
 					(int)ole_id, (int)atom_header.length-4);
42034091
 			if (!ppt_unlzw(out_dir, fd, atom_header.length-4)) {
 				cli_dbgmsg("ppt_unlzw failed\n");
 				cli_rmdirs(out_dir);
 				free(out_dir);
 				return NULL;
 			}
 
 		} else {
3863b5ce
 			offset = lseek(fd, 0, SEEK_CUR);
 			/* Check we don't wrap */
9d3c38ba
 			if ((offset + (off_t)atom_header.length) < offset) {
3863b5ce
 				break;
 			}
 			offset += atom_header.length;
 			if (lseek(fd, offset, SEEK_SET) != offset ) {
42034091
 				break;
 			}
 		}
 	}
 	return out_dir;
 }
 
5f02033a
 char *ppt_vba_read(const char *dir)
 {
ac8154d9
 	char *out_dir;
42034091
 	int fd;
ac8154d9
 	char fullname[NAME_MAX + 1];
42034091
 
ac8154d9
 	snprintf(fullname, sizeof(fullname) - 1, "%s/PowerPoint Document", dir);
b58fdfc2
 	fd = open(fullname, O_RDONLY|O_BINARY);
42034091
 	if (fd == -1) {
d92098c8
 		cli_dbgmsg("Open PowerPoint Document failed\n");
42034091
 		return NULL;
 	}
fa53d800
 
42034091
 	out_dir = ppt_stream_iter(fd);
 	close(fd);
 	return out_dir;
 }
 
5f02033a
 
 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
cdcb8f73
 /* Code to extract Word6 macros					     */
7b9aed8c
 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
 
 typedef struct mso_fib_tag {
d92098c8
 	char ununsed[sizeof(uint16_t) + sizeof(uint16_t) +
 		sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) +
 		sizeof(uint16_t)];
7b9aed8c
 	uint32_t macro_offset;
 	uint32_t macro_len;
 } mso_fib_t;
 
 typedef struct macro_entry_tag {
 	unsigned char version;
 	unsigned char key;
 	uint16_t intname_i;
 	uint16_t extname_i;
 	uint16_t xname_i;
 	uint32_t unknown;
 	uint32_t len;
 	uint32_t state;
 	uint32_t offset;
 } macro_entry_t;
 
 typedef struct macro_info_tag {
 	uint16_t count;
 	struct macro_entry_tag *macro_entry;
 } macro_info_t;
 
fa53d800
 static	void	wm_free_macro_info(macro_info_t *macro_info);
 
7b9aed8c
 static void wm_print_fib(mso_fib_t *fib)
 {
 	cli_dbgmsg("macro offset: 0x%.4x\n", fib->macro_offset);
 	cli_dbgmsg("macro len: 0x%.4x\n\n", fib->macro_len);
 }
fa53d800
 
7b9aed8c
 static int wm_read_fib(int fd, mso_fib_t *fib)
 {
 	/* don't need the information is this block, so seek forward */
 	if (lseek(fd, 0x118, SEEK_SET) != 0x118) {
bf79f6c3
 		cli_dbgmsg("lseek wm_fib failed\n");
7b9aed8c
 		return FALSE;
 	}
fa53d800
 
7b9aed8c
 	if (cli_readn(fd, &fib->macro_offset, 4) != 4) {
bf79f6c3
 		cli_dbgmsg("read wm_fib failed\n");
7b9aed8c
 		return FALSE;
 	}
 	if (cli_readn(fd, &fib->macro_len, 4) != 4) {
bf79f6c3
 		cli_dbgmsg("read wm_fib failed\n");
7b9aed8c
 		return FALSE;
 	}
 	fib->macro_offset = vba_endian_convert_32(fib->macro_offset, FALSE);
 	fib->macro_len = vba_endian_convert_32(fib->macro_len, FALSE);
fa53d800
 
7b9aed8c
 	return TRUE;
 }
 
 static int wm_read_macro_entry(int fd, macro_entry_t *macro_entry)
 {
 	if (cli_readn(fd, &macro_entry->version, 1) != 1) {
bf79f6c3
 		cli_dbgmsg("read macro_entry failed\n");
7b9aed8c
 		return FALSE;
 	}
 	if (cli_readn(fd, &macro_entry->key, 1) != 1) {
bf79f6c3
 		cli_dbgmsg("read macro_entry failed\n");
7b9aed8c
 		return FALSE;
 	}
 	if (cli_readn(fd, &macro_entry->intname_i, 2) != 2) {
bf79f6c3
 		cli_dbgmsg("read macro_entry failed\n");
7b9aed8c
 		return FALSE;
fa53d800
 	}
7b9aed8c
 	if (cli_readn(fd, &macro_entry->extname_i, 2) != 2) {
bf79f6c3
 		cli_dbgmsg("read macro_entry failed\n");
7b9aed8c
 		return FALSE;
 	}
 	if (cli_readn(fd, &macro_entry->xname_i, 2) != 2) {
bf79f6c3
 		cli_dbgmsg("read macro_entry failed\n");
7b9aed8c
 		return FALSE;
 	}
 	if (cli_readn(fd, &macro_entry->unknown, 4) != 4) {
bf79f6c3
 		cli_dbgmsg("read macro_entry failed\n");
7b9aed8c
 		return FALSE;
 	}
 	if (cli_readn(fd, &macro_entry->len, 4) != 4) {
bf79f6c3
 		cli_dbgmsg("read macro_entry failed\n");
7b9aed8c
 		return FALSE;
 	}
 	if (cli_readn(fd, &macro_entry->state, 4) != 4) {
bf79f6c3
 		cli_dbgmsg("read macro_entry failed\n");
7b9aed8c
 		return FALSE;
 	}
 	if (cli_readn(fd, &macro_entry->offset, 4) != 4) {
bf79f6c3
 		cli_dbgmsg("read macro_entry failed\n");
7b9aed8c
 		return FALSE;
 	}
fa53d800
 
bc8dc960
 	macro_entry->intname_i = vba_endian_convert_16(macro_entry->intname_i, FALSE);
 	macro_entry->extname_i = vba_endian_convert_16(macro_entry->extname_i, FALSE);
 	macro_entry->xname_i = vba_endian_convert_16(macro_entry->xname_i, FALSE);
 	macro_entry->len = vba_endian_convert_32(macro_entry->len, FALSE);
 	macro_entry->state = vba_endian_convert_32(macro_entry->state, FALSE);
 	macro_entry->offset = vba_endian_convert_32(macro_entry->offset, FALSE);
7b9aed8c
 	return TRUE;
 }
 
 static macro_info_t *wm_read_macro_info(int fd)
 {
 	int i;
 	macro_info_t *macro_info;
 
 	macro_info = (macro_info_t *) cli_malloc(sizeof(macro_info_t));
 	if (!macro_info) {
 		return NULL;
 	}
 	if (cli_readn(fd, &macro_info->count, 2) != 2) {
bf79f6c3
 		cli_dbgmsg("read macro_info failed\n");
4a84ea33
 		free(macro_info);
7b9aed8c
 		return NULL;
 	}
bc8dc960
 	macro_info->count = vba_endian_convert_16(macro_info->count, FALSE);
7b9aed8c
 	cli_dbgmsg("macro count: %d\n", macro_info->count);
 	macro_info->macro_entry = (macro_entry_t *)
 			cli_malloc(sizeof(macro_entry_t) * macro_info->count);
 	if (!macro_info->macro_entry) {
 		free(macro_info);
 		return NULL;
 	}
 	for (i=0 ; i < macro_info->count ; i++) {
 		if (!wm_read_macro_entry(fd,
 				&macro_info->macro_entry[i])) {
fa53d800
 			wm_free_macro_info(macro_info);
7b9aed8c
 			return NULL;
 		}
 	}
 	return macro_info;
 }
 
fa53d800
 static	void
 wm_free_macro_info(macro_info_t *macro_info)
7b9aed8c
 {
 	if (macro_info) {
 		free(macro_info->macro_entry);
 		free(macro_info);
 	}
 }
 
 static int wm_read_oxo3(int fd)
 {
 	uint8_t count;
 
 	if (cli_readn(fd, &count, 1) != 1) {
 		cli_dbgmsg("read oxo3 record1 failed\n");
 		return FALSE;
 	}
 	if (lseek(fd, count*14, SEEK_CUR) == -1) {
 		cli_dbgmsg("lseek oxo3 record1 failed\n");
 		return FALSE;
 	}
 	cli_dbgmsg("oxo3 records1: %d\n", count);
fa53d800
 
7b9aed8c
 	if (cli_readn(fd, &count, 1) != 1) {
bf79f6c3
 		cli_dbgmsg("read oxo3 record2 failed\n");
7b9aed8c
 		return FALSE;
 	}
 	if (count == 0) {
 		if (cli_readn(fd, &count, 1) != 1) {
bf79f6c3
 			cli_dbgmsg("read oxo3 failed\n");
7b9aed8c
 			return FALSE;
 		}
 		if (count != 2) {
 			lseek(fd, -1, SEEK_CUR);
 			return TRUE;
 		}
 		if (cli_readn(fd, &count, 1) != 1) {
bf79f6c3
 			cli_dbgmsg("read oxo3 failed\n");
7b9aed8c
 			return FALSE;
 		}
 	}
 	if (count > 0) {
 		if (lseek(fd, (count*4)+1, SEEK_CUR) == -1) {
bf79f6c3
 			cli_dbgmsg("lseek oxo3 failed\n");
7b9aed8c
 			return FALSE;
 		}
fa53d800
 	}
7b9aed8c
 	cli_dbgmsg("oxo3 records2: %d\n", count);
 	return TRUE;
 }
 
ac8154d9
 static int
 wm_skip_menu_info(int fd)
7b9aed8c
 {
ac8154d9
 	uint16_t count;
fa53d800
 
ac8154d9
 	if (cli_readn(fd, &count, sizeof(uint16_t)) != sizeof(uint16_t)) {
bf79f6c3
 		cli_dbgmsg("read menu_info failed\n");
ac8154d9
 		return FALSE;
7b9aed8c
 	}
ac8154d9
 	count = vba_endian_convert_16(count, FALSE);
 	cli_dbgmsg("menu_info count: %d\n", count);
7b9aed8c
 
ac8154d9
 	if(count)
 		if(lseek(fd, count * 12, SEEK_CUR) == -1)
 			return FALSE;
 	return TRUE;
7b9aed8c
 }
 
a27be3c7
 static int
 wm_skip_macro_extnames(int fd)
7b9aed8c
 {
a27be3c7
 	int is_unicode;
7b9aed8c
 	int16_t size;
a27be3c7
 	off_t offset_end = lseek(fd, 0, SEEK_CUR);
fa53d800
 
a27be3c7
 	if(cli_readn(fd, &size, sizeof(int16_t)) != sizeof(int16_t)) {
7b9aed8c
 		cli_dbgmsg("read macro_extnames failed\n");
a27be3c7
 		return FALSE;
7b9aed8c
 	}
bc8dc960
 	size = vba_endian_convert_16(size, FALSE);
7b9aed8c
 	if (size == -1) { /* Unicode flag */
a27be3c7
 		if(cli_readn(fd, &size, sizeof(int16_t)) != sizeof(int16_t)) {
bf79f6c3
 			cli_dbgmsg("read macro_extnames failed\n");
a27be3c7
 			return FALSE;
7b9aed8c
 		}
bc8dc960
 		size = vba_endian_convert_16(size, FALSE);
a27be3c7
 		is_unicode = 1;
 	} else
 		is_unicode = 0;
 
7b9aed8c
 	cli_dbgmsg("ext names size: 0x%x\n", size);
 
 	offset_end += size;
a27be3c7
 	while(lseek(fd, 0, SEEK_CUR) < offset_end) {
 		uint8_t length;
96911b50
 		off_t offset;
a27be3c7
 
 		if (cli_readn(fd, &length, 1) != 1) {
69435d2d
 			cli_dbgmsg("read macro_extnames failed\n");
a27be3c7
 			return FALSE;
7b9aed8c
 		}
69435d2d
 
96911b50
 		if(is_unicode)
d9a9e1fc
 			offset = (off_t)length * 2 + 1;
96911b50
 		else
d9a9e1fc
 			offset = (off_t)length;
96911b50
 
 		offset += sizeof(uint16_t);	/* numref */
 		if(lseek(fd, offset, SEEK_CUR) == -1) {
 			cli_dbgmsg("read macro_extnames failed to seek\n");
a27be3c7
 			return FALSE;
fa53d800
 		}
7b9aed8c
 	}
a27be3c7
 	return TRUE;
7b9aed8c
 }
 
d9a9e1fc
 static int
 wm_skip_macro_intnames(int fd)
7b9aed8c
 {
d9a9e1fc
 	uint16_t i, count;
fa53d800
 
d9a9e1fc
 	if (cli_readn(fd, &count, sizeof(uint16_t)) != sizeof(uint16_t)) {
bf79f6c3
 		cli_dbgmsg("read macro_intnames failed\n");
d9a9e1fc
 		return FALSE;
7b9aed8c
 	}
d9a9e1fc
 	count = vba_endian_convert_16(count, FALSE);
 	cli_dbgmsg("int names count: %u\n", count);
fa53d800
 
d9a9e1fc
 	for(i = 0; i < count; i++) {
 		uint8_t length;
 
 		/* id */
 		if(lseek(fd, sizeof(uint16_t), SEEK_CUR) == -1) {
 			cli_dbgmsg("skip_macro_intnames failed\n");
 			return FALSE;
7b9aed8c
 		}
 
d9a9e1fc
 		if(cli_readn(fd, &length, sizeof(uint8_t)) != sizeof(uint8_t)) {
 			cli_dbgmsg("skip_macro_intnames failed\n");
 			return FALSE;
 		}
fa53d800
 
d9a9e1fc
 		/* Internal name, plus one byte of unknown data */
 		if(lseek(fd, length + 1, SEEK_CUR) == -1) {
 			cli_dbgmsg("skip_macro_intnames failed\n");
 			return FALSE;
 		}
7b9aed8c
 	}
d9a9e1fc
 	return TRUE;
7b9aed8c
 }
 
 vba_project_t *wm_dir_read(const char *dir)
 {
ed0fd157
 	int fd, done;
7b9aed8c
 	off_t end_offset;
ac8154d9
 	unsigned char info_id;
7b9aed8c
 	macro_info_t *macro_info=NULL;
a27be3c7
 	vba_project_t *vba_project;
 	mso_fib_t fib;
ac8154d9
 	char fullname[NAME_MAX + 1];
fa53d800
 
ac8154d9
 	snprintf(fullname, sizeof(fullname) - 1, "%s/WordDocument", dir);
b58fdfc2
 	fd = open(fullname, O_RDONLY|O_BINARY);
7b9aed8c
 	if (fd == -1) {
 		cli_dbgmsg("Open WordDocument failed\n");
 		return NULL;
 	}
fa53d800
 
7b9aed8c
 	if (!wm_read_fib(fd, &fib)) {
1feea75d
 		close(fd);
7b9aed8c
 		return NULL;
 	}
ac8154d9
 	if(fib.macro_len == 0) {
 		cli_dbgmsg("No macros detected\n");
 		/* Must be clean */
 		close(fd);
 		return NULL;
 	}
7b9aed8c
 	wm_print_fib(&fib);
fa53d800
 
ac8154d9
 	/* Go one past the start to ignore start_id */
 	if (lseek(fd, fib.macro_offset + 1, SEEK_SET) != (off_t)(fib.macro_offset + 1)) {
7b9aed8c
 		cli_dbgmsg("lseek macro_offset failed\n");
1feea75d
 		close(fd);
7b9aed8c
 		return NULL;
 	}
fa53d800
 
7b9aed8c
 	end_offset = fib.macro_offset + fib.macro_len;
a27be3c7
 	done = FALSE;
fa53d800
 
7b9aed8c
 	while ((lseek(fd, 0, SEEK_CUR) < end_offset) && !done) {
 		if (cli_readn(fd, &info_id, 1) != 1) {
bf79f6c3
 			cli_dbgmsg("read macro_info failed\n");
1feea75d
 			close(fd);
7b9aed8c
 			return NULL;
 		}
 		switch (info_id) {
 			case 0x01:
d9a9e1fc
 				if(macro_info)
 					wm_free_macro_info(macro_info);
7b9aed8c
 				macro_info = wm_read_macro_info(fd);
d9a9e1fc
 				if(macro_info == NULL)
7b9aed8c
 					done = TRUE;
 				break;
 			case 0x03:
ac8154d9
 				if(!wm_read_oxo3(fd))
7b9aed8c
 					done = TRUE;
 				break;
 			case 0x05:
ac8154d9
 				if(!wm_skip_menu_info(fd))
7b9aed8c
 					done = TRUE;
 				break;
 			case 0x10:
a27be3c7
 				if(!wm_skip_macro_extnames(fd))
7b9aed8c
 					done = TRUE;
 				break;
 			case 0x11:
d9a9e1fc
 				if(!wm_skip_macro_intnames(fd))
7b9aed8c
 					done = TRUE;
 				break;
 			case 0x12:
 				/* No sure about these, always seems to
 				come after the macros though, so finish
 				*/
ac8154d9
 				done = TRUE;
7b9aed8c
 				break;
 			case 0x40:
 				/* end marker */
ac8154d9
 				done = TRUE;
7b9aed8c
 				break;
 			default:
a27be3c7
 				cli_dbgmsg("unknown type: 0x%x\n", info_id);
ac8154d9
 				done = TRUE;
7b9aed8c
 		}
 	}
fa53d800
 
a27be3c7
 	close(fd);
 
7b9aed8c
 	if (macro_info) {
 		vba_project = (vba_project_t *) cli_malloc(sizeof(struct vba_project_tag));
 		if (!vba_project) {
 			goto abort;
 		}
 		vba_project->name = (char **) cli_malloc(sizeof(char *) *macro_info->count);
 		if (!vba_project->name) {
 			free(vba_project);
 			vba_project = NULL;
 			goto abort;
 		}
6f38c939
 		vba_project->dir = cli_strdup(dir);
7b9aed8c
 		vba_project->offset = (uint32_t *) cli_malloc(sizeof(uint32_t) *
 					macro_info->count);
 		if (!vba_project->offset) {
 			free(vba_project->name);
fa53d800
 			if(vba_project->dir)
 				free(vba_project->dir);
7b9aed8c
 			free(vba_project);
 			vba_project = NULL;
 			goto abort;
 		}
 		vba_project->length = (uint32_t *) cli_malloc(sizeof(uint32_t) *
 					macro_info->count);
 		if (!vba_project->length) {
 			free(vba_project->offset);
 			free(vba_project->name);
 			free(vba_project->dir);
 			free(vba_project);
 			vba_project = NULL;
 			goto abort;
 		}
 		vba_project->key = (unsigned char *) cli_malloc(sizeof(unsigned char) *
 					macro_info->count);
 		if (!vba_project->key) {
 			free(vba_project->length);
 			free(vba_project->offset);
 			free(vba_project->name);
 			free(vba_project->dir);
 			free(vba_project);
 			vba_project = NULL;
d92098c8
 		} else {
ed0fd157
 			int i;
 
d92098c8
 			vba_project->count = macro_info->count;
 			for(i = 0; i < macro_info->count; i++) {
 				vba_project->name[i] = cli_strdup("WordDocument");
 				vba_project->offset[i] = macro_info->macro_entry[i].offset;
 				vba_project->length[i] = macro_info->macro_entry[i].len;
 				vba_project->key[i] = macro_info->macro_entry[i].key;
 			}
7b9aed8c
 		}
ed0fd157
 abort:
 		wm_free_macro_info(macro_info);
 		/* Fall through */
a27be3c7
 	} else
 		vba_project = NULL;
 
7b9aed8c
 	return vba_project;
 }
 
 unsigned char *wm_decrypt_macro(int fd, uint32_t offset, uint32_t len,
 					unsigned char key)
 {
 	unsigned char *buff;
 	uint32_t i;
fa53d800
 
9d3c38ba
 	if (lseek(fd, offset, SEEK_SET) != (int64_t)offset) {
7b9aed8c
 		return NULL;
 	}
 	buff = (unsigned char *) cli_malloc(len);
 	if (!buff) {
 		return NULL;
 	}
 
d92098c8
 	if (cli_readn(fd, buff, len) != (int)len) {
7b9aed8c
 		free(buff);
 		return NULL;
 	}
fa53d800
 	if (key != 0)
 		for (i=0 ; i < len; i++)
 			buff[i] ^= key;
7b9aed8c
 	return buff;
 }