5f1ab8db |
/* |
2023340a |
* Copyright (C) 2007-2008 Sourcefire, Inc.
*
* Authors: Nigel Horne |
5f1ab8db |
*
* 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. |
5f1ab8db |
*
* 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. |
5f1ab8db |
*/ |
f0bc32bd |
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
|
27395a6e |
static char const rcsid[] = "$Id: tnef.c,v 1.41 2007/02/12 22:22:27 njh Exp $"; |
5f1ab8db |
|
36196180 |
#include <stdio.h> |
49584961 |
#include <fcntl.h> |
36196180 |
|
a8efe447 |
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
|
36196180 |
#include "cltypes.h" |
f0bc32bd |
#include "clamav.h" |
36196180 |
#include "others.h" |
d4a7dd82 |
|
273250b6 |
#include "mbox.h" |
d4a7dd82 |
#include "tnef.h" |
36196180 |
|
3d8c1fc7 |
static int tnef_message(fmap_t *map, off_t *pos, uint16_t type, uint16_t tag, int32_t length, off_t fsize);
static int tnef_attachment(fmap_t *map, off_t *pos, uint16_t type, uint16_t tag, int32_t length, const char *dir, fileblob **fbref, off_t fsize);
static int tnef_header(fmap_t *map, off_t *pos, uint8_t *part, uint16_t *type, uint16_t *tag, int32_t *length); |
36196180 |
#define TNEF_SIGNATURE 0x223E9f78
#define LVL_MESSAGE 0x01
#define LVL_ATTACHMENT 0x02
#define attMSGCLASS 0x8008 |
a1727087 |
#define attBODY 0x800c
#define attATTACHDATA 0x800f /* Attachment Data */
#define attATTACHTITLE 0x8010 /* Attachment File Name */ |
36196180 |
#define attDATEMODIFIED 0x8020 |
9c570283 |
#define attTNEFVERSION 0x9006 |
36196180 |
#define attOEMCODEPAGE 0x9007 |
5f1ab8db |
|
75282b5c |
#define host16(v) le16_to_host(v)
#define host32(v) le32_to_host(v) |
0f016f88 |
|
5d40533e |
/* a TNEF file must be at least this size */
#define MIN_SIZE (sizeof(uint32_t) + sizeof(uint16_t)) |
49584961 |
|
5f1ab8db |
int |
3d8c1fc7 |
cli_tnef(const char *dir, cli_ctx *ctx) |
5f1ab8db |
{ |
36196180 |
uint32_t i32;
uint16_t i16; |
a1727087 |
fileblob *fb; |
3d8c1fc7 |
int ret, alldone;
off_t fsize, pos = 0; |
a2a004df |
STATBUF statb; |
de45ce28 |
|
36196180 |
|
3d8c1fc7 |
fsize = ctx->fmap[0]->len; |
33eb424c |
|
a45c7039 |
if(fsize < (off_t) MIN_SIZE) { |
5d40533e |
cli_dbgmsg("cli_tngs: file too small, ignoring\n");
return CL_CLEAN;
}
|
3d8c1fc7 |
if (fmap_readn(*ctx->fmap, &i32, pos, sizeof(uint32_t)) != sizeof(uint32_t)) { |
5d40533e |
/* The file is at least MIN_SIZE bytes, so it "can't" fail */ |
871177cd |
return CL_EREAD; |
273250b6 |
} |
3d8c1fc7 |
pos += sizeof(uint32_t);
|
4113686d |
if(host32(i32) != TNEF_SIGNATURE) { |
36196180 |
return CL_EFORMAT; |
273250b6 |
} |
36196180 |
|
3d8c1fc7 |
if(fmap_readn(*ctx->fmap, &i16, pos, sizeof(uint16_t)) != sizeof(uint16_t)) { |
5d40533e |
/* The file is at least MIN_SIZE bytes, so it "can't" fail */ |
871177cd |
return CL_EREAD; |
273250b6 |
} |
3d8c1fc7 |
pos += sizeof(uint16_t); |
36196180 |
|
a1727087 |
fb = NULL; |
b7121c3b |
ret = CL_CLEAN; /* we don't know if it's clean or not :-) */ |
a1727087 |
alldone = 0; |
36196180 |
|
a1727087 |
do { |
3dbb92ed |
uint8_t part = 0;
uint16_t type = 0, tag = 0;
int32_t length = 0; |
22bfcbf1 |
|
3d8c1fc7 |
switch(tnef_header(*ctx->fmap, &pos, &part, &type, &tag, &length)) { |
36196180 |
case 0:
alldone = 1;
break; |
273250b6 |
case 1: |
36196180 |
break;
default: |
5d40533e |
/*
* Assume truncation, not file I/O error
*/
cli_warnmsg("cli_tnef: file truncated, returning CLEAN\n");
ret = CL_CLEAN; |
a1727087 |
alldone = 1;
break; |
36196180 |
} |
3dbb92ed |
if(length == 0)
continue;
if(length < 0) { |
75173f19 |
cli_warnmsg("Corrupt TNEF header detected - length %d\n",
(int)length); |
3dbb92ed |
ret = CL_EFORMAT;
break;
} |
36196180 |
if(alldone)
break; |
22bfcbf1 |
switch(part) { |
36196180 |
case LVL_MESSAGE: |
00dbe3f3 |
cli_dbgmsg("TNEF - found message\n"); |
cd453277 |
if(fb != NULL) {
fileblobDestroy(fb);
fb = NULL;
}
fb = fileblobCreate(); |
3d8c1fc7 |
if(tnef_message(*ctx->fmap, &pos, type, tag, length, fsize) != 0) { |
91b8682a |
cli_dbgmsg("TNEF: Error reading TNEF message\n"); |
a1727087 |
ret = CL_EFORMAT;
alldone = 1; |
36196180 |
}
break;
case LVL_ATTACHMENT: |
00dbe3f3 |
cli_dbgmsg("TNEF - found attachment\n"); |
3d8c1fc7 |
if(tnef_attachment(*ctx->fmap, &pos, type, tag, length, dir, &fb, fsize) != 0) { |
91b8682a |
cli_dbgmsg("TNEF: Error reading TNEF attachment\n"); |
a1727087 |
ret = CL_EFORMAT;
alldone = 1; |
36196180 |
}
break; |
a1727087 |
case 0:
break; |
36196180 |
default: |
22bfcbf1 |
cli_warnmsg("TNEF - unknown level %d tag 0x%x\n", (int)part, (int)tag); |
33eb424c |
|
49584961 |
/*
* Dump the file incase it was part of an
* email that's about to be deleted
*/
if(cli_debug_flag) { |
5fc380f1 |
int fout = -1; |
33068e09 |
char *filename = cli_gentemp(ctx->engine->tmpdir); |
49584961 |
char buffer[BUFSIZ];
|
5fc380f1 |
if(filename)
fout = open(filename, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC|O_BINARY, 0600); |
49584961 |
if(fout >= 0) {
int count;
|
27395a6e |
cli_warnmsg("Saving dump to %s: refer to http://www.clamav.net/bugs\n", filename); |
49584961 |
|
3d8c1fc7 |
pos = 0;
while ((count = fmap_readn(*ctx->fmap, buffer, pos, sizeof(buffer))) > 0) {
pos += count; |
49584961 |
cli_writen(fout, buffer, count); |
3d8c1fc7 |
} |
49584961 |
close(fout);
}
free(filename);
} |
a1727087 |
ret = CL_EFORMAT;
alldone = 1;
break; |
36196180 |
} |
a1727087 |
} while(!alldone); |
36196180 |
|
a1727087 |
if(fb) { |
21992fd1 |
cli_dbgmsg("cli_tnef: flushing final data\n");
if(fileblobGetFilename(fb) == NULL) { |
7047626b |
cli_dbgmsg("Saving TNEF portion with an unknown name\n"); |
504b463f |
fileblobSetFilename(fb, dir, "tnef"); |
21992fd1 |
} |
a1727087 |
fileblobDestroy(fb);
fb = NULL;
} |
4113686d |
|
b7121c3b |
cli_dbgmsg("cli_tnef: returning %d\n", ret);
return ret; |
5f1ab8db |
} |
36196180 |
static int |
3d8c1fc7 |
tnef_message(fmap_t *map, off_t *pos, uint16_t type, uint16_t tag, int32_t length, off_t fsize) |
36196180 |
{ |
22bfcbf1 |
uint16_t i16; |
3dbb92ed |
off_t offset; |
37948a1b |
#ifdef CL_DEBUG |
18cec9a3 |
uint32_t i32; |
36196180 |
char *string; |
a1727087 |
#endif |
36196180 |
|
75173f19 |
cli_dbgmsg("message tag 0x%x, type 0x%x, length %d\n", tag, type,
(int)length); |
36196180 |
|
3d8c1fc7 |
offset = *pos; |
36196180 |
/*
* a lot of this stuff should be only discovered in debug mode...
*/
switch(tag) { |
a1727087 |
case attBODY: |
6fcf5624 |
cli_warnmsg("TNEF body not being scanned - if you believe this file contains a virus, submit it to www.clamav.net\n"); |
a1727087 |
break; |
37948a1b |
#ifdef CL_DEBUG |
36196180 |
case attTNEFVERSION:
/*assert(length == sizeof(uint32_t))*/ |
3d8c1fc7 |
if(fmap_readn(map, &i32, *pos, sizeof(uint32_t)) != sizeof(uint32_t)) |
36196180 |
return -1; |
3d8c1fc7 |
(*pos) += sizeof(uint32_t); |
4113686d |
i32 = host32(i32); |
36196180 |
cli_dbgmsg("TNEF version %d\n", i32);
break;
case attOEMCODEPAGE: |
273250b6 |
/* 8 bytes, but just print the first 4 */ |
36196180 |
/*assert(length == sizeof(uint32_t))*/ |
3d8c1fc7 |
if(fmap_readn(map, &i32, *pos, sizeof(uint32_t)) != sizeof(uint32_t)) |
36196180 |
return -1; |
3d8c1fc7 |
(*pos) += sizeof(uint32_t); |
4113686d |
i32 = host32(i32); |
36196180 |
cli_dbgmsg("TNEF codepage %d\n", i32);
break;
case attDATEMODIFIED:
/* 14 bytes, long */
break;
case attMSGCLASS: |
7c46bfb8 |
if(length <= 0)
return -1; |
36196180 |
string = cli_malloc(length + 1); |
241e7eb1 |
if(string == NULL) {
cli_errmsg("tnef_message: Unable to allocate memory for string\n"); |
7c46bfb8 |
return -1; |
241e7eb1 |
} |
3d8c1fc7 |
if(fmap_readn(map, string, *pos, (uint32_t)length) != (uint32_t)length) { |
675cc5ae |
free(string); |
36196180 |
return -1; |
675cc5ae |
} |
3d8c1fc7 |
(*pos) += (uint32_t)length; |
36196180 |
string[length] = '\0';
cli_dbgmsg("TNEF class %s\n", string);
free(string);
break;
default: |
17a3c5e9 |
cli_dbgmsg("TNEF - unsupported message tag 0x%x type 0x%d length %d\n", tag, type, length); |
36196180 |
break; |
a1727087 |
#endif |
36196180 |
}
|
94f38202 |
/*cli_dbgmsg("%lu %lu\n", (long)(offset + length), ftell(fp));*/ |
36196180 |
|
33eb424c |
if(!CLI_ISCONTAINED2(0, fsize, (off_t)offset, (off_t)length)) { |
91b8682a |
cli_dbgmsg("TNEF: Incorrect length field in tnef_message\n"); |
33eb424c |
return -1;
} |
3d8c1fc7 |
(*pos) = offset + length; |
36196180 |
/* Checksum - TODO, verify */ |
3d8c1fc7 |
(*pos) += 2; |
36196180 |
return 0;
}
static int |
3d8c1fc7 |
tnef_attachment(fmap_t *map, off_t *pos, uint16_t type, uint16_t tag, int32_t length, const char *dir, fileblob **fbref, off_t fsize) |
36196180 |
{ |
22bfcbf1 |
uint32_t todo;
uint16_t i16; |
3dbb92ed |
off_t offset; |
a1727087 |
char *string;
|
75173f19 |
cli_dbgmsg("attachment tag 0x%x, type 0x%x, length %d\n", tag, type,
(int)length); |
a1727087 |
|
3d8c1fc7 |
offset = *pos; |
a1727087 |
switch(tag) {
case attATTACHTITLE: |
7c46bfb8 |
if(length <= 0)
return -1; |
a1727087 |
string = cli_malloc(length + 1); |
241e7eb1 |
if(string == NULL) {
cli_errmsg("tnef_attachment: Unable to allocate memory for string\n"); |
7c46bfb8 |
return -1; |
241e7eb1 |
} |
3d8c1fc7 |
if(fmap_readn(map, string, *pos, (uint32_t)length) != (uint32_t)length) { |
675cc5ae |
free(string); |
a1727087 |
return -1; |
675cc5ae |
} |
3d8c1fc7 |
(*pos) += (uint32_t)length; |
a1727087 |
string[length] = '\0';
cli_dbgmsg("TNEF filename %s\n", string); |
cd453277 |
if(*fbref == NULL) {
*fbref = fileblobCreate(); |
675cc5ae |
if(*fbref == NULL) {
free(string); |
cd453277 |
return -1; |
675cc5ae |
} |
cd453277 |
} |
a1727087 |
fileblobSetFilename(*fbref, dir, string);
free(string);
break;
case attATTACHDATA:
if(*fbref == NULL) {
*fbref = fileblobCreate();
if(*fbref == NULL)
return -1;
} |
b8ce70e6 |
todo = length; |
3d8c1fc7 |
while(todo) { |
b8ce70e6 |
unsigned char buf[BUFSIZ]; |
3d8c1fc7 |
int32_t got = fmap_readn(map, buf, *pos, MIN(sizeof(buf), todo));
if (got <= 0)
break;
(*pos) += got; |
b8ce70e6 |
fileblobAddData(*fbref, buf, got);
todo -= got; |
a1727087 |
}
break;
default: |
75173f19 |
cli_dbgmsg("TNEF - unsupported attachment tag 0x%x type 0x%d length %d\n",
tag, type, (int)length); |
a1727087 |
break;
}
|
94f38202 |
/*cli_dbgmsg("%lu %lu\n", (long)(offset + length), ftell(fp));*/ |
a1727087 |
|
33eb424c |
if(!CLI_ISCONTAINED2(0, fsize, (off_t)offset, (off_t)length)) { |
91b8682a |
cli_dbgmsg("TNEF: Incorrect length field in tnef_attachment\n"); |
33eb424c |
return -1;
} |
3d8c1fc7 |
(*pos) = (long)(offset + length); /* shouldn't be needed */ |
a1727087 |
|
3d8c1fc7 |
(*pos) += 2; |
a1727087 |
|
36196180 |
return 0;
} |
22bfcbf1 |
static int |
3d8c1fc7 |
tnef_header(fmap_t *map, off_t *pos, uint8_t *part, uint16_t *type, uint16_t *tag, int32_t *length) |
22bfcbf1 |
{
uint32_t i32; |
3d8c1fc7 |
int rc; |
22bfcbf1 |
|
3d8c1fc7 |
if (fmap_readn(map, part, *pos, 1) != 1) |
22bfcbf1 |
return 0; |
3d8c1fc7 |
(*pos)++; |
22bfcbf1 |
if(*part == (uint8_t)0)
return 0;
|
3d8c1fc7 |
rc = fmap_readn(map, &i32, *pos, sizeof(uint32_t));
if (rc != sizeof(uint32_t)) {
if(((*part == '\n') || (*part == '\r')) && (rc == 0)) { |
e36c045d |
/*
* trailing newline in the file, could be caused by |
a0ba2961 |
* broken quoted-printable encoding in the source
* message missing a final '=' |
e36c045d |
*/
cli_dbgmsg("tnef_header: ignoring trailing newline\n");
return 0;
} |
22bfcbf1 |
return -1; |
e36c045d |
} |
3d8c1fc7 |
(*pos) += sizeof(uint32_t); |
22bfcbf1 |
i32 = host32(i32); |
3dbb92ed |
*tag = (uint16_t)(i32 & 0xFFFF);
*type = (uint16_t)((i32 & 0xFFFF0000) >> 16); |
22bfcbf1 |
|
3d8c1fc7 |
if(fmap_readn(map, &i32, *pos, sizeof(uint32_t)) != sizeof(uint32_t)) |
22bfcbf1 |
return -1; |
3d8c1fc7 |
(*pos) += sizeof(uint32_t); |
7c46bfb8 |
*length = (int32_t)host32(i32); |
22bfcbf1 |
|
75173f19 |
cli_dbgmsg("message tag 0x%x, type 0x%x, length %d\n",
*tag, *type, (int)*length); |
22bfcbf1 |
return 1;
} |