libclamav/tnef.c
0646ab95
 /*
  *  Copyright (C) 2005 Nigel Horne <njh@bandsman.co.uk>
  *
  *  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.
  */
af5f3346
 
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
6d584c4d
 static	char	const	rcsid[] = "$Id: tnef.c,v 1.8 2005/03/25 23:00:48 nigelhorne Exp $";
0646ab95
 
5f591f08
 #include <stdio.h>
 
 #include "cltypes.h"
af5f3346
 #include "clamav.h"
5f591f08
 #include "others.h"
 #include "tnef.h"
2b227b61
 #include "blob.h"
5f591f08
 
2b227b61
 static	int	tnef_message(int desc);
 static	int	tnef_attachment(int desc, const char *dir, fileblob **fbref);
5f591f08
 
 /*
  * The algorithm will be based on kdepim/ktnef/lib/ktnefparser.cpp from
  * KDE, rewritten in C by NJH. The algorithm is released under the GPL and is
  *	Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be>
  *
  * TODO: Use mmap on systems that support it
  */
 #define	TNEF_SIGNATURE	0x223E9f78
 #define	LVL_MESSAGE	0x01
 #define	LVL_ATTACHMENT	0x02
 
 #define	attMSGCLASS	0x8008
2b227b61
 #define	attBODY		0x800c
 #define	attATTACHDATA	0x800f	/* Attachment Data */
 #define	attATTACHTITLE	0x8010	/* Attachment File Name */
5f591f08
 #define	attDATEMODIFIED	0x8020
 #define	attTNEFVERSION	0x9006
 #define	attOEMCODEPAGE	0x9007
0646ab95
 
6d584c4d
 #if WORDS_BIGENDIAN == 0
 #define host16(v)	(v)
 #define host32(v)	(v)
 #else
 #ifdef	__GNUC__
 #define	host16(v)	__bswap_16(x)
 #define	host32(v)	__bswap_32(x)
 #else
 #define	host16(v)	((v >> 8) | (v << 8))
 #define	host32(v)	((v >> 24) | ((v & 0x00FF0000) >> 8) | \
 				((v & 0x0000FF00) << 8) | (v << 24))
 #endif
 #endif
 
2b227b61
 /* FIXME: use stdio */
fb5b8ed1
 /* FIXME: only works on little endian machines */
0646ab95
 int
 cli_tnef(const char *dir, int desc)
 {
5f591f08
 	uint32_t i32;
 	uint16_t i16;
 	uint8_t i8;
2b227b61
 	fileblob *fb;
 	int ret, alldone;
c0dda7a6
 
5f591f08
 	lseek(desc, 0L, SEEK_SET);
 
 	if(cli_readn(desc, &i32, sizeof(uint32_t)) != sizeof(uint32_t))
 		return CL_EIO;
 
6d584c4d
 	if(host32(i32) != host32(TNEF_SIGNATURE))
5f591f08
 		return CL_EFORMAT;
 
 	if(cli_readn(desc, &i16, sizeof(uint16_t)) != sizeof(uint16_t))
 		return CL_EIO;
 
2b227b61
 	fb = NULL;
 	ret = CL_CLEAN;
 	alldone = 0;
5f591f08
 
2b227b61
 	do {
5f591f08
 		switch(cli_readn(desc, &i8, sizeof(uint8_t))) {
 			case -1:
 				perror("read");
2b227b61
 				ret = CL_EIO;
 				alldone = 1;
 				break;
5f591f08
 			case 0:
 				alldone = 1;
 				break;
 			case sizeof(uint8_t):
 				break;
 			default:
2b227b61
 				ret = CL_EIO;
 				alldone = 1;
 				break;
5f591f08
 		}
 		if(alldone)
 			break;
 		switch(i8) {
 			case LVL_MESSAGE:
 				/*cli_dbgmsg("TNEF - found message\n");*/
 				if(tnef_message(desc) != 0) {
 					cli_errmsg("Error reading TNEF message\n");
2b227b61
 					ret = CL_EFORMAT;
 					alldone = 1;
5f591f08
 				}
 				break;
 			case LVL_ATTACHMENT:
 				/*cli_dbgmsg("TNEF - found attachment\n");*/
2b227b61
 				if(tnef_attachment(desc, dir, &fb) != 0) {
5f591f08
 					cli_errmsg("Error reading TNEF message\n");
2b227b61
 					ret = CL_EFORMAT;
 					alldone = 1;
5f591f08
 				}
 				break;
2b227b61
 			case 0:
 				break;
5f591f08
 			default:
 				cli_errmsg("TNEF - unknown level %d\n", (int)i8);
2b227b61
 				ret = CL_EFORMAT;
 				alldone = 1;
 				break;
5f591f08
 		}
2b227b61
 	} while(!alldone);
5f591f08
 
2b227b61
 	if(fb) {
 		fileblobDestroy(fb);
 		fb = NULL;
 	}
0646ab95
 	return CL_CLEAN;
 }
5f591f08
 
 static int
 tnef_message(int desc)
 {
 	uint32_t i32, length;
 	uint16_t i16, tag, type;
 	off_t offset;
2b227b61
 #if	CL_DEBUG
5f591f08
 	char *string;
2b227b61
 #endif
5f591f08
 
 	if(cli_readn(desc, &i32, sizeof(uint32_t)) != sizeof(uint32_t))
 		return -1;
 
 	tag = i32 & 0xFFFF;
 	type = (i32 & 0xFFFF0000) >> 16;
 
 	if(cli_readn(desc, &i32, sizeof(uint32_t)) != sizeof(uint32_t))
 		return -1;
 
 	length = i32;
 
 	/*cli_dbgmsg("message tag 0x%x, type 0x%x, length %u\n", tag, type, length);*/
 
 	offset = lseek(desc, 0L, SEEK_CUR);
 
 	/*
 	 * a lot of this stuff should be only discovered in debug mode...
 	 */
 	switch(tag) {
2b227b61
 		case attBODY:
 			cli_warnmsg("TNEF body not being scanned - report to bugs@clamav.net\n");
 			break;
 #if	CL_DEBUG
5f591f08
 		case attTNEFVERSION:
 			/*assert(length == sizeof(uint32_t))*/
 			if(cli_readn(desc, &i32, sizeof(uint32_t)) != sizeof(uint32_t))
 				return -1;
 			cli_dbgmsg("TNEF version %d\n", i32);
 			break;
 		case attOEMCODEPAGE:
 			/*assert(length == sizeof(uint32_t))*/
 			if(cli_readn(desc, &i32, sizeof(uint32_t)) != sizeof(uint32_t))
 				return -1;
 			cli_dbgmsg("TNEF codepage %d\n", i32);
 			break;
 		case attDATEMODIFIED:
 			/* 14 bytes, long */
 			break;
 		case attMSGCLASS:
 			string = cli_malloc(length + 1);
 			if((unsigned int)cli_readn(desc, string, length) != length)
 				return -1;
 			string[length] = '\0';
 			cli_dbgmsg("TNEF class %s\n", string);
 			free(string);
 			break;
 		default:
2b227b61
 			cli_dbgmsg("TNEF - unsupported message tag 0x%x type 0x%d length %u\n", tag, type, length);
5f591f08
 			break;
2b227b61
 #endif
5f591f08
 	}
 
 	/*cli_dbgmsg("%lu %lu\n", offset + length, lseek(desc, 0L, SEEK_CUR));*/
 
 	lseek(desc, offset + length, SEEK_SET);	/* shouldn't be needed */
 
 	/* Checksum - TODO, verify */
 	if(cli_readn(desc, &i16, sizeof(uint16_t)) != sizeof(uint16_t))
 		return -1;
 
 	return 0;
 }
 
 static int
2b227b61
 tnef_attachment(int desc, const char *dir, fileblob **fbref)
5f591f08
 {
2b227b61
 	uint32_t i32, length, todo;
 	uint16_t i16, tag, type;
 	off_t offset;
 	char *string;
 
 	if(cli_readn(desc, &i32, sizeof(uint32_t)) != sizeof(uint32_t))
 		return -1;
 
 	tag = i32 & 0xFFFF;
 	type = (i32 & 0xFFFF0000) >> 16;
 
 	if(cli_readn(desc, &i32, sizeof(uint32_t)) != sizeof(uint32_t))
 		return -1;
 
 	length = i32;
 
 	/*cli_dbgmsg("message tag 0x%x, type 0x%x, length %u\n", tag, type, length);*/
 
 	offset = lseek(desc, 0L, SEEK_CUR);
 
 	switch(tag) {
 		case attATTACHTITLE:
 			if(*fbref != NULL)
 				fileblobDestroy(*fbref);
 			*fbref = fileblobCreate();
 
 			if(*fbref == NULL)
 				return -1;
 			string = cli_malloc(length + 1);
 
 			if((unsigned int)cli_readn(desc, string, length) != length)
 				return -1;
 			string[length] = '\0';
 			cli_dbgmsg("TNEF filename %s\n", string);
 			fileblobSetFilename(*fbref, dir, string);
 			free(string);
 			break;
 		case attATTACHDATA:
 			if(*fbref == NULL) {
 				*fbref = fileblobCreate();
 
 				if(*fbref == NULL)
 					return -1;
 			}
 			/* FIXME: use stdio */
 			todo = length;
 			while(todo) {
 				unsigned char *c;
 
 				if(cli_readn(desc, &c, 1) != 1)
 					break;
 				fileblobAddData(*fbref, (const unsigned char *)&c, 1);
 			}
 			break;
 		default:
 			cli_dbgmsg("TNEF - unsupported attachment tag 0x%x type 0x%d length %u\n", tag, type, length);
 			break;
 	}
 
 	/*cli_dbgmsg("%lu %lu\n", offset + length, lseek(desc, 0L, SEEK_CUR));*/
 
 	lseek(desc, offset + length, SEEK_SET);	/* shouldn't be needed */
 
 	/* Checksum - TODO, verify */
 	if(cli_readn(desc, &i16, sizeof(uint16_t)) != sizeof(uint16_t))
 		return -1;
 
5f591f08
 	return 0;
 }