17
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18
18
* MA 02110-1301, USA.
22
* Revision 1.23 2007/02/12 20:46:08 njh
25
* Revision 1.22 2006/07/31 09:19:52 njh
28
* Revision 1.21 2006/07/01 16:17:35 njh
31
* Revision 1.20 2006/07/01 03:47:50 njh
32
* Don't loop if binhex runs out of memory
34
* Revision 1.19 2006/05/19 11:02:12 njh
37
* Revision 1.18 2006/04/09 19:59:27 kojm
38
* update GPL headers with new address for FSF
40
* Revision 1.17 2005/11/06 14:03:26 nigelhorne
41
* Ensure NAME_MAX isn't redefined on BeOS
43
* Revision 1.16 2005/05/14 16:13:25 nigelhorne
44
* Ensure munmap is the right size
46
* Revision 1.15 2005/05/13 19:30:34 nigelhorne
47
* Clean cli_realloc call
49
* Revision 1.14 2005/03/10 08:51:30 nigelhorne
52
* Revision 1.13 2005/01/19 05:29:41 nigelhorne
55
* Revision 1.12 2004/12/27 14:17:14 nigelhorne
56
* Fix segfault if write to temporary file fails
58
* Revision 1.11 2004/12/17 12:03:38 nigelhorne
59
* Tidy up for machines without MMAP
61
* Revision 1.10 2004/12/16 15:29:51 nigelhorne
64
* Revision 1.9 2004/11/28 22:06:39 nigelhorne
65
* Tidy space only headers code
67
* Revision 1.8 2004/11/28 21:05:50 nigelhorne
68
* Handle headers with only spaces
70
* Revision 1.7 2004/11/23 09:05:26 nigelhorne
71
* Fix crash in base64 encoded binhex files
73
* Revision 1.6 2004/11/22 15:16:53 nigelhorne
74
* Use cli_realloc instead of many cli_mallocs
76
* Revision 1.5 2004/11/18 20:11:34 nigelhorne
79
* Revision 1.4 2004/11/18 19:30:29 kojm
80
* add support for Mac's HQX file format
82
* Revision 1.3 2004/11/18 18:24:45 nigelhorne
85
* Revision 1.2 2004/11/18 18:09:06 nigelhorne
86
* First draft of binhex.c
89
static char const rcsid[] = "$Id: binhex.c,v 1.23 2007/02/12 20:46:08 njh Exp $";
94
23
#include "clamav-config.h"
99
#define _REENTRANT /* for Solaris 2.8 */
105
#include <sys/mman.h>
106
#else /* HAVE_SYS_MMAN_H */
113
#include <sys/stat.h>
114
30
#include "others.h"
120
cli_binhex(const char *dir, int desc)
123
cli_warnmsg("File not decoded - binhex decoding needs mmap() (for now)\n");
127
char *buf, *start, *line;
133
if(fstat(desc, &statb) < 0)
136
size = (size_t)statb.st_size;
145
start = buf = mmap(NULL, size, PROT_READ, MAP_PRIVATE, desc, 0);
146
if(buf == MAP_FAILED) {
151
cli_dbgmsg("mmap'ed binhex file\n");
153
bytesleft = (long)size;
156
while(bytesleft > 0) {
160
/*printf("%d: ", bytesleft);*/
162
for(ptr = buf; bytesleft && (*ptr != '\n') && (*ptr != '\r'); ptr++) {
167
/*printf("%d: ", length);*/
169
newline = cli_realloc(line, (size_t)(length + 1));
175
memcpy(line, buf, length);
180
if(messageAddStr(m, line) < 0)
183
if((bytesleft > 0) && (*ptr == '\r')) {
195
if(binhexBegin(m) == NULL) {
197
cli_dbgmsg("No binhex line found\n");
201
/* similar to binhexMessage */
202
messageSetEncoding(m, "x-binhex");
204
fb = messageToFileblob(m, dir, 1);
206
cli_dbgmsg("Binhex file decoded to %s\n", fileblobGetFilename(fb));
209
cli_errmsg("Couldn't decode binhex file to %s\n", dir);
213
return CL_CLEAN; /* a lie - but it gets things going */
214
/* return CL_EIO; */ /* probably CL_EMEM, but we can't tell at this layer */
35
static const uint8_t hqxtbl[] = {
36
/* 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f */
37
/* 00-0f */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
38
/* 10-1f */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
39
/* 20-2f */ 0xff,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0xff,0xff,
40
/* 30-3f */ 0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0xff,0x14,0x15,0xff,0xff,0xff,0xff,0xff,0xff,
41
/* 40-4f */ 0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0xff,
42
/* 50-5f */ 0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0xff,0x2c,0x2d,0x2e,0x2f,0xff,0xff,0xff,0xff,
43
/* 60-6f */ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0xff,0x37,0x38,0x39,0x3a,0x3b,0x3c,0xff,0xff,
44
/* 70-7f */ 0x3d,0x3e,0x3f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
47
#define BH_FLUSH_SZ (BUFSIZ - 256)
49
int cli_binhex(cli_ctx *ctx) {
50
fmap_t *map = *ctx->fmap;
51
uint8_t *encoded, decoded[BUFSIZ], spare_bits, last_byte=0, this_byte, offset=0;
52
size_t enc_done=0, enc_todo=map->len;
53
unsigned int dec_done=0, chunksz = 0, chunkoff=0;
54
uint32_t datalen, reslen;
55
int in_data = 0, in_run = 0, datafd, resfd, ret = CL_CLEAN;
56
enum binhex_phase { IN_BANNER, IN_HEADER, IN_DATA, IN_LIMBO1, IN_LIMBO2, IN_RES } write_phase = IN_BANNER;
59
cli_dbgmsg("in cli_binhex\n");
60
if(!map->len) return CL_CLEAN;
62
if((ret = cli_gentempfd(ctx->engine->tmpdir, &dname, &datafd)) != CL_SUCCESS)
65
if((ret = cli_gentempfd(ctx->engine->tmpdir, &rname, &resfd)) != CL_SUCCESS) {
67
if(cli_unlink(dname)) ret = CL_EUNLINK;
74
if(!enc_todo || dec_done >= BH_FLUSH_SZ) {
75
if(write_phase == IN_HEADER) {
76
uint32_t namelen = (uint32_t)decoded[0], hdrlen = 1 + namelen + 1 + 4 + 4 + 2;
78
cli_dbgmsg("cli_binhex: file is empty\n");
81
datalen = (decoded[hdrlen]<<24) | (decoded[hdrlen+1]<<16) | (decoded[hdrlen+2]<<8) | decoded[hdrlen+3];
83
reslen = (decoded[hdrlen]<<24) | (decoded[hdrlen+1]<<16) | (decoded[hdrlen+2]<<8) | decoded[hdrlen+3];
85
decoded[namelen+1] = 0;
86
if(dec_done <= hdrlen) {
87
cli_dbgmsg("cli_binhex: file too short for header\n");
90
if((ret = cli_checklimits("cli_binhex(data)", ctx, datalen, 0, 0)) != CL_CLEAN)
92
if(cli_checklimits("cli_binhex(resources)", ctx, reslen, 0, 0) != CL_CLEAN)
94
cli_dbgmsg("cli_binhex: decoding '%s' - %u bytes of data to %s - %u bytes or resources to %s\n", decoded+1, datalen, dname, reslen, rname);
95
memmove(decoded, &decoded[hdrlen], dec_done - hdrlen);
99
if(dec_done && write_phase == IN_DATA) {
100
unsigned int todo = MIN(dec_done, datalen);
103
if(cli_writen(datafd, decoded, todo)!=(int)todo) {
109
lseek(datafd, 0, SEEK_SET);
110
ret = cli_magic_scandesc(datafd, ctx);
111
if(ret == CL_VIRUS) break;
114
memmove(decoded, &decoded[todo], dec_done);
116
if(dec_done && write_phase == IN_LIMBO1) {
119
cli_dbgmsg("cli_binhex: skipping resources (too small)\n");
125
memmove(decoded, &decoded[2], dec_done);
130
memmove(decoded, &decoded[1], dec_done);
133
if(dec_done && write_phase == IN_LIMBO2) {
135
cli_dbgmsg("cli_binhex: skipping resources (too small)\n");
140
memmove(decoded, &decoded[1], dec_done);
142
if(dec_done && write_phase == IN_RES) {
143
unsigned int todo = MIN(dec_done, reslen);
146
if(cli_writen(resfd, decoded, todo)!=(int)todo) {
151
lseek(resfd, 0, SEEK_SET);
152
ret = cli_magic_scandesc(resfd, ctx);
157
if(write_phase == IN_DATA) {
158
cli_dbgmsg("cli_binhex: scanning partially extracted data fork\n");
159
lseek(datafd, 0, SEEK_SET);
160
ret = cli_magic_scandesc(datafd, ctx);
161
} else if(write_phase == IN_RES) {
162
cli_dbgmsg("cli_binhex: scanning partially extracted resource fork\n");
163
lseek(resfd, 0, SEEK_SET);
164
ret = cli_magic_scandesc(resfd, ctx);
171
chunksz = MIN(enc_todo, map->pgsz);
172
encoded = fmap_need_off_once(map, enc_done, chunksz);
181
b = encoded[chunkoff++];
185
if((char)b == '\r' || (char)b == '\n') {
189
if(!in_data) continue;
190
if(write_phase == IN_BANNER) {
192
cli_dbgmsg("cli_binhex: broken file (missing stream start identifier)\n");
199
if(b > 0x7f || (b = hqxtbl[b]) == 0xff) {
200
cli_dbgmsg("cli_binhex: Invalid character (%02x)\n", encoded[chunkoff-1]);
203
switch((offset++) & 3) { /* 6 bits per char */
204
case 0: /* left-6h */
207
case 1: /* left-2l + middle-4h */
208
this_byte = spare_bits | (b>>4);
211
case 2: /* middle-4l + right-2h */
212
this_byte = spare_bits | (b>>2);
215
case 3: /* right-6l */
216
this_byte = spare_bits | b;
225
decoded[dec_done++] = last_byte;
228
} else if(this_byte == 0x90) {
232
decoded[dec_done++] = this_byte;
233
last_byte = this_byte;
238
if(!ctx->engine->keeptmp) {
239
if(cli_unlink(dname) && ret != CL_VIRUS) ret = CL_EUNLINK;
240
if(cli_unlink(rname) && ret != CL_VIRUS) ret = CL_EUNLINK;