~ubuntu-branches/ubuntu/feisty/clamav/feisty

« back to all changes in this revision

Viewing changes to libclamav/message.c

  • Committer: Bazaar Package Importer
  • Author(s): Kees Cook
  • Date: 2007-02-20 10:33:44 UTC
  • mto: This revision was merged to the branch mainline in revision 16.
  • Revision ID: james.westby@ubuntu.com-20070220103344-zgcu2psnx9d98fpa
Tags: upstream-0.90
ImportĀ upstreamĀ versionĀ 0.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
 *  Copyright (C) 2007-2008 Sourcefire, Inc.
3
 
 *
4
 
 *  Authors: Nigel Horne
 
2
 *  Copyright (C) 2002-2006 Nigel Horne <njh@bandsman.co.uk>
5
3
 *
6
4
 *  This program is free software; you can redistribute it and/or modify
7
 
 *  it under the terms of the GNU General Public License version 2 as
8
 
 *  published by the Free Software Foundation.
 
5
 *  it under the terms of the GNU General Public License as published by
 
6
 *  the Free Software Foundation; either version 2 of the License, or
 
7
 *  (at your option) any later version.
9
8
 *
10
9
 *  This program is distributed in the hope that it will be useful,
11
10
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
15
 *  along with this program; if not, write to the Free Software
17
16
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18
17
 *  MA 02110-1301, USA.
19
 
 *
20
 
 * TODO: Optimise messageExport, decodeLine, messageIsEncoding
21
18
 */
22
19
static  char    const   rcsid[] = "$Id: message.c,v 1.195 2007/02/12 20:46:09 njh Exp $";
23
20
 
25
22
#include "clamav-config.h"
26
23
#endif
27
24
 
 
25
#ifndef CL_DEBUG
 
26
#define NDEBUG  /* map CLAMAV debug onto standard */
 
27
#endif
 
28
 
28
29
#ifdef CL_THREAD_SAFE
29
30
#ifndef _REENTRANT
30
31
#define _REENTRANT      /* for Solaris 2.8 */
69
70
#endif
70
71
#endif
71
72
 
72
 
static  int     messageHasArgument(const message *m, const char *variable);
73
73
static  void    messageIsEncoding(message *m);
74
74
static unsigned char *decode(message *m, const char *in, unsigned char *out, unsigned char (*decoder)(char), bool isFast);
75
75
static  void    sanitiseBase64(char *s);
112
112
        {       NULL,                   NOENCODING      }
113
113
};
114
114
 
115
 
static  const   struct  mime_map {
 
115
static  struct  mime_map {
116
116
        const   char    *string;
117
117
        mime_type       type;
118
118
} mime_map[] = {
220
220
 
221
221
        assert(mess != NULL);
222
222
        if(type == NULL) {
223
 
                cli_dbgmsg("Empty content-type field\n");
 
223
                cli_warnmsg("Empty content-type field\n");
224
224
                return 0;
225
225
        }
226
226
 
268
268
                        mess->mimeType = MEXTENSION;
269
269
                else {
270
270
                        /*
 
271
                         * Based on a suggestion by James Stevens
 
272
                         *      <James@kyzo.com>
271
273
                         * Force scanning of strange messages
272
274
                         */
273
275
                        if(strcasecmp(type, "plain") == 0) {
294
296
                                        }
295
297
                                }
296
298
                                if(highestSimil >= 50) {
297
 
                                        cli_dbgmsg("Unknown MIME type \"%s\" - guessing as %s (%d%% certainty)\n",
298
 
                                                type, closest,
299
 
                                                highestSimil);
 
299
                                        cli_dbgmsg("Unknown MIME type \"%s\" - guessing as %s (%u%% certainty)\n",
 
300
                                                type, closest, highestSimil);
300
301
                                        mess->mimeType = (mime_type)t;
301
302
                                } else {
302
303
                                        cli_dbgmsg("Unknown MIME type: `%s', set to Application - if you believe this file contains a virus, submit it to www.clamav.net\n", type);
388
389
messageAddArgument(message *m, const char *arg)
389
390
{
390
391
        int offset;
391
 
        char *p;
392
392
 
393
393
        assert(m != NULL);
394
394
 
414
414
                        return; /* already in there */
415
415
 
416
416
        if(offset == m->numberOfArguments) {
417
 
                char **q;
 
417
                char **ptr;
418
418
 
419
419
                m->numberOfArguments++;
420
 
                q = (char **)cli_realloc(m->mimeArguments, m->numberOfArguments * sizeof(char *));
421
 
                if(q == NULL) {
 
420
                ptr = (char **)cli_realloc(m->mimeArguments, m->numberOfArguments * sizeof(char *));
 
421
                if(ptr == NULL) {
422
422
                        m->numberOfArguments--;
423
423
                        return;
424
424
                }
425
 
                m->mimeArguments = q;
426
 
        }
427
 
 
428
 
        p = m->mimeArguments[offset] = rfc2231(arg);
429
 
 
430
 
        if(strchr(p, '=') == NULL) {
431
 
                if(strncmp(p, "filename", 8) == 0) {
432
 
                        /*
433
 
                         * FIXME: Bounce message handling is corrupting the in
434
 
                         * core copies of headers
435
 
                         */
436
 
                        cli_dbgmsg("Possible data corruption fixed\n");
437
 
                        p[8] = '=';
438
 
                } else {
439
 
                        if(p && *p)
440
 
                                cli_dbgmsg("messageAddArgument, '%s' contains no '='\n", p);
441
 
                        free(m->mimeArguments[offset]);
442
 
                        m->mimeArguments[offset] = NULL;
443
 
                        return;
444
 
                }
445
 
        }
 
425
                m->mimeArguments = ptr;
 
426
        }
 
427
 
 
428
        arg = m->mimeArguments[offset] = rfc2231(arg);
446
429
 
447
430
        /*
448
431
         * This is terribly broken from an RFC point of view but is useful
450
433
         * mime. By pretending defaulting to an application rather than
451
434
         * to nomime we can ensure they're saved and scanned
452
435
         */
453
 
        if(p && ((strncasecmp(p, "filename=", 9) == 0) || (strncasecmp(p, "name=", 5) == 0)))
 
436
        if((strncasecmp(arg, "filename=", 9) == 0) || (strncasecmp(arg, "name=", 5) == 0))
454
437
                if(messageGetMimeType(m) == NOMIME) {
455
438
                        cli_dbgmsg("Force mime encoding to application\n");
456
439
                        messageSetMimeType(m, "application");
476
459
                const char *key, *cptr;
477
460
                char *data, *field;
478
461
 
479
 
                if(isspace(*string & 0xff) || (*string == ';')) {
 
462
                if(isspace(*string) || (*string == ';')) {
480
463
                        string++;
481
464
                        continue;
482
465
                }
657
640
                        while(isspace(*ptr))
658
641
                                ptr++;
659
642
                        if(*ptr != '=') {
660
 
                                cli_dbgmsg("messageFindArgument: no '=' sign found in MIME header '%s' (%s)\n", variable, messageGetArgument(m, i));
 
643
                                cli_warnmsg("messageFindArgument: no '=' sign found in MIME header '%s' (%s)\n", variable, messageGetArgument(m, i));
661
644
                                return NULL;
662
645
                        }
663
646
                        if((*++ptr == '"') && (strchr(&ptr[1], '"') != NULL)) {
669
652
                                        return NULL;
670
653
 
671
654
                                /*
 
655
                                 * Thomas Lamy <Thomas.Lamy@in-online.net>:
672
656
                                 * fix un-quoting of boundary strings from
673
657
                                 * header, occurs if boundary was given as
674
658
                                 *      'boundary="_Test_";'
689
673
        return NULL;
690
674
}
691
675
 
692
 
char *
693
 
messageGetFilename(const message *m)
694
 
{
695
 
        char *filename = (char *)messageFindArgument(m, "filename");
696
 
 
697
 
        if(filename)
698
 
                return filename;
699
 
 
700
 
        return (char *)messageFindArgument(m, "name");
701
 
}
702
 
 
703
 
/* Returns true or false */
704
 
static int
705
 
messageHasArgument(const message *m, const char *variable)
706
 
{
707
 
        int i;
708
 
        size_t len;
709
 
 
710
 
        assert(m != NULL);
711
 
        assert(variable != NULL);
712
 
 
713
 
        len = strlen(variable);
714
 
 
715
 
        for(i = 0; i < m->numberOfArguments; i++) {
716
 
                const char *ptr;
717
 
 
718
 
                ptr = messageGetArgument(m, i);
719
 
                if((ptr == NULL) || (*ptr == '\0'))
720
 
                        continue;
721
 
#ifdef  CL_DEBUG
722
 
                cli_dbgmsg("messageHasArgument: compare %lu bytes of %s with %s\n",
723
 
                        (unsigned long)len, variable, ptr);
724
 
#endif
725
 
                if(strncasecmp(ptr, variable, len) == 0) {
726
 
                        ptr = &ptr[len];
727
 
                        while(isspace(*ptr))
728
 
                                ptr++;
729
 
                        if(*ptr != '=') {
730
 
                                cli_dbgmsg("messageHasArgument: no '=' sign found in MIME header '%s' (%s)\n", variable, messageGetArgument(m, i));
731
 
                                return 0;
732
 
                        }
733
 
                        return 1;
734
 
                }
735
 
        }
736
 
        return 0;
737
 
}
738
 
 
739
 
int
740
 
messageHasFilename(const message *m)
741
 
{
742
 
        return messageHasArgument(m, "filename") || messageHasArgument(m, "file");
743
 
}
744
 
 
745
676
void
746
677
messageSetEncoding(message *m, const char *enctype)
747
678
{
1021
952
}
1022
953
 
1023
954
/*
1024
 
 * Put the contents of the given text at the end of the current object.
1025
 
 * Can be used either to move a text object into a message, or to move a
1026
 
 * message's text into another message only moving from a given offset.
1027
 
 * The given text emptied; it can be used again if needed, though be warned that
1028
 
 * it will have an empty line at the start.
1029
 
 * Returns 0 for failure, 1 for success
1030
 
 */
1031
 
int
1032
 
messageMoveText(message *m, text *t, message *old_message)
1033
 
{
1034
 
        int rc;
1035
 
 
1036
 
        if(m->body_first == NULL) {
1037
 
                if(old_message) {
1038
 
                        text *u;
1039
 
                        /*
1040
 
                         * t is within old_message which is about to be
1041
 
                         * destroyed
1042
 
                         */
1043
 
                        assert(old_message->body_first != NULL);
1044
 
 
1045
 
                        m->body_first = t;
1046
 
                        for(u = old_message->body_first; u != t;) {
1047
 
                                text *next;
1048
 
 
1049
 
                                if(u->t_line)
1050
 
                                        lineUnlink(u->t_line);
1051
 
                                next = u->t_next;
1052
 
 
1053
 
                                free(u);
1054
 
                                u = next;
1055
 
 
1056
 
                                if(u == NULL) {
1057
 
                                        cli_dbgmsg("messageMoveText sanity check: t not within old_message\n");
1058
 
                                        return -1;
1059
 
                                }
1060
 
                        }
1061
 
                        assert(old_message->body_last->t_next == NULL);
1062
 
 
1063
 
                        m->body_last = old_message->body_last;
1064
 
                        old_message->body_first = old_message->body_last = NULL;
1065
 
 
1066
 
                        /* Do any pointers need to be reset? */
1067
 
                        if((old_message->bounce == NULL) &&
1068
 
                           (old_message->encoding == NULL) &&
1069
 
                           (old_message->binhex == NULL) &&
1070
 
                           (old_message->yenc == NULL))
1071
 
                                return 0;
1072
 
 
1073
 
                        m->body_last = m->body_first;
1074
 
                        rc = 0;
1075
 
                } else {
1076
 
                        m->body_last = m->body_first = textMove(NULL, t);
1077
 
                        if(m->body_first == NULL)
1078
 
                                rc = -1;
1079
 
                        else
1080
 
                                rc = 0;
1081
 
                }
1082
 
        } else {
1083
 
                m->body_last = textMove(m->body_last, t);
1084
 
                if(m->body_last == NULL) {
1085
 
                        rc = -1;
1086
 
                        m->body_last = m->body_first;
1087
 
                } else
1088
 
                        rc = 0;
1089
 
        }
1090
 
 
1091
 
        while(m->body_last->t_next) {
1092
 
                m->body_last = m->body_last->t_next;
1093
 
                if(m->body_last->t_line)
1094
 
                        messageIsEncoding(m);
1095
 
        }
1096
 
 
1097
 
        return rc;
1098
 
}
1099
 
 
1100
 
/*
1101
955
 * See if the last line marks the start of a non MIME inclusion that
1102
956
 * will need to be scanned
1103
957
 */
1108
962
        static const char binhex[] = "(This file must be converted with BinHex 4.0)";
1109
963
        const char *line = lineGetData(m->body_last->t_line);
1110
964
 
1111
 
        /*if(m->ctx == NULL)
1112
 
                cli_dbgmsg("messageIsEncoding, ctx == NULL\n");*/
 
965
        /* not enough matches to warrant this test */
 
966
        /*if(lineGetRefCount(m->body_last->t_line) > 1) {
 
967
                return;
 
968
        }*/
1113
969
 
1114
970
        if((m->encoding == NULL) &&
1115
971
           (strncasecmp(line, encoding, sizeof(encoding) - 1) == 0) &&
1116
972
           (strstr(line, "7bit") == NULL))
1117
973
                m->encoding = m->body_last;
1118
 
        else if((m->bounce == NULL) && m->ctx &&
 
974
        else if((m->bounce == NULL) &&
1119
975
                (strncasecmp(line, "Received: ", 10) == 0) &&
1120
 
                (cli_filetype((const unsigned char *)line, strlen(line), m->ctx->engine) == CL_TYPE_MAIL))
 
976
                (cli_filetype((const unsigned char *)line, strlen(line)) == CL_TYPE_MAIL))
1121
977
                        m->bounce = m->body_last;
1122
978
                /* Not needed with fast track visa technology */
1123
979
        /*else if((m->uuencode == NULL) && isuuencodebegin(line))
1147
1003
}
1148
1004
 
1149
1005
/*
 
1006
 * Clean up the message by removing trailing spaces and blank lines
 
1007
 */
 
1008
void
 
1009
messageClean(message *m)
 
1010
{
 
1011
        text *newEnd = textClean(m->body_first);
 
1012
 
 
1013
        if(newEnd)
 
1014
                m->body_last = newEnd;
 
1015
}
 
1016
 
 
1017
/*
1150
1018
 * Export a message using the given export routines
1151
1019
 *
1152
1020
 * TODO: It really should export into an array, one
1173
1041
 
1174
1042
        cli_dbgmsg("messageExport: numberOfEncTypes == %d\n", m->numberOfEncTypes);
1175
1043
 
 
1044
        if((t_line = binhexBegin(m)) != NULL) {
 
1045
                unsigned char byte;
 
1046
                unsigned long newlen = 0L, len, dataforklen, resourceforklen, l;
 
1047
                unsigned char *data;
 
1048
                char *ptr;
 
1049
                int bytenumber;
 
1050
                blob *tmp;
 
1051
 
 
1052
                /*
 
1053
                 * Table look up by Thomas Lamy <Thomas.Lamy@in-online.net>
 
1054
                 * HQX conversion table - illegal chars are 0xff
 
1055
                 */
 
1056
                const unsigned char hqxtbl[] = {
 
1057
                             /*   00   01   02   03   04   05   06   07   08   09   0a   0b   0c   0d   0e   0f */
 
1058
                /* 00-0f */     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
 
1059
                /* 10-1f */     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
 
1060
                /* 20-2f */     0xff,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0xff,0xff,
 
1061
                /* 30-3f */     0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0xff,0x14,0x15,0xff,0xff,0xff,0xff,0xff,0xff,
 
1062
                /* 40-4f */     0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0xff,
 
1063
                /* 50-5f */     0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0xff,0x2c,0x2d,0x2e,0x2f,0xff,0xff,0xff,0xff,
 
1064
                /* 60-6f */     0x30,0x31,0x32,0x33,0x34,0x35,0x36,0xff,0x37,0x38,0x39,0x3a,0x3b,0x3c,0xff,0xff,
 
1065
                /* 70-7f */     0x3d,0x3e,0x3f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
 
1066
                };
 
1067
 
 
1068
                cli_dbgmsg("messageExport: decode binhex\n");
 
1069
                /*
 
1070
                 * Decode BinHex4. First create a temporary blob which contains
 
1071
                 * the encoded message. Then decode that blob to the target
 
1072
                 * blob, free the temporary blob and return the target one
 
1073
                 *
 
1074
                 * FIXME: EICAR isn't detected: should create 3 files in fork
 
1075
                 *      format: .info, .data and .rsrc. This is needed for
 
1076
                 *      position dependant detection such as EICAR
 
1077
                 *
 
1078
                 * See RFC1741
 
1079
                 */
 
1080
                while(((t_line = t_line->t_next) != NULL) &&
 
1081
                      (t_line->t_line == NULL))
 
1082
                        ;
 
1083
 
 
1084
                tmp = textToBlob(t_line, NULL,
 
1085
                        ((m->numberOfEncTypes == 1) && (m->encodingTypes[0] == BINHEX)) ? destroy_text : 0);
 
1086
 
 
1087
                if(tmp == NULL) {
 
1088
                        /*
 
1089
                         * FIXME: We've probably run out of memory during the
 
1090
                         * text to blob.
 
1091
                         * TODO: if m->numberOfEncTypes == 1 we could delete
 
1092
                         * the text object as we decode it
 
1093
                         */
 
1094
                        cli_warnmsg("Couldn't start binhex parser\n");
 
1095
                        (*destroy)(ret);
 
1096
                        return NULL;
 
1097
                }
 
1098
 
 
1099
                data = blobGetData(tmp);
 
1100
 
 
1101
                if(data == NULL) {
 
1102
                        cli_warnmsg("Couldn't locate the binhex message that was claimed to be there\n");
 
1103
                        blobDestroy(tmp);
 
1104
                        (*destroy)(ret);
 
1105
                        return NULL;
 
1106
                }
 
1107
                len = blobGetDataSize(tmp);
 
1108
 
 
1109
                if(data[0] == ':') {
 
1110
                        unsigned char *uptr;
 
1111
                        /* 7 bit (ala RFC1741) */
 
1112
 
 
1113
                        /*
 
1114
                         * FIXME: this is dirty code, modification of the
 
1115
                         * contents of a member of the blob object should be
 
1116
                         * done through blob.c
 
1117
                         *
 
1118
                         * Convert 7 bit data into 8 bit
 
1119
                         */
 
1120
                        cli_dbgmsg("decode HQX7 message (%lu bytes)\n", len);
 
1121
 
 
1122
                        uptr = cli_malloc(len);
 
1123
                        if(uptr == NULL) {
 
1124
                                blobDestroy(tmp);
 
1125
                                (*destroy)(ret);
 
1126
                                return NULL;
 
1127
                        }
 
1128
                        memcpy(uptr, data, len);
 
1129
                        bytenumber = 0;
 
1130
 
 
1131
                        /*
 
1132
                         * uptr now contains the encoded (7bit) data - len bytes long
 
1133
                         * data will contain the unencoded (8bit) data
 
1134
                         */
 
1135
                        for(l = 1; l < len; l++) {
 
1136
                                unsigned char c = uptr[l];
 
1137
 
 
1138
                                if(c == ':')
 
1139
                                        break;
 
1140
 
 
1141
                                if((c == '\n') || (c == '\r'))
 
1142
                                        continue;
 
1143
 
 
1144
                                if((c < 0x20) || (c > 0x7f) || (hqxtbl[c] == 0xff)) {
 
1145
                                        cli_warnmsg("Invalid HQX7 character '%c' (0x%02x)\n", c, c);
 
1146
                                        break;
 
1147
                                }
 
1148
                                c = hqxtbl[c];
 
1149
                                assert(c <= 63);
 
1150
 
 
1151
                                /*
 
1152
                                 * These masks probably aren't needed, but
 
1153
                                 * they're here to verify the code is correct
 
1154
                                 */
 
1155
                                switch(bytenumber) {
 
1156
                                        case 0:
 
1157
                                                data[newlen] = (c << 2) & 0xFC;
 
1158
                                                bytenumber = 1;
 
1159
                                                break;
 
1160
                                        case 1:
 
1161
                                                data[newlen++] |= (c >> 4) & 0x3;
 
1162
                                                data[newlen] = (c << 4) & 0xF0;
 
1163
                                                bytenumber = 2;
 
1164
                                                break;
 
1165
                                        case 2:
 
1166
                                                data[newlen++] |= (c >> 2) & 0xF;
 
1167
                                                data[newlen] = (c << 6) & 0xC0;
 
1168
                                                bytenumber = 3;
 
1169
                                                break;
 
1170
                                        case 3:
 
1171
                                                data[newlen++] |= c & 0x3F;
 
1172
                                                bytenumber = 0;
 
1173
                                                break;
 
1174
                                }
 
1175
                        }
 
1176
 
 
1177
                        cli_dbgmsg("decoded HQX7 message (now %lu bytes)\n", newlen);
 
1178
 
 
1179
                        /*
 
1180
                         * Throw away the old encoded (7bit) data
 
1181
                         * data now points to the encoded (8bit) data - newlen bytes
 
1182
                         *
 
1183
                         * The data array may contain repetitive characters
 
1184
                         */
 
1185
                        free(uptr);
 
1186
                } else {
 
1187
                        cli_warnmsg("HQX8 messages not yet supported - if you believe this file contains a virus, submit it to www.clamav.net\n");
 
1188
                        newlen = len;
 
1189
                }
 
1190
 
 
1191
                /*
 
1192
                 * Uncompress repetitive characters
 
1193
                 */
 
1194
                if(memchr(data, 0x90, newlen)) {
 
1195
                        blob *u = blobCreate(); /* uncompressed data */
 
1196
 
 
1197
                        if(u == NULL) {
 
1198
                                (*destroy)(ret);
 
1199
                                blobDestroy(tmp);
 
1200
                                return NULL;
 
1201
                        }
 
1202
                        /*
 
1203
                         * Includes compression
 
1204
                         */
 
1205
                        for(l = 0L; l < newlen; l++) {
 
1206
                                unsigned char c = data[l];
 
1207
 
 
1208
                                /*
 
1209
                                 * TODO: handle the case where the first byte
 
1210
                                 * is 0x90
 
1211
                                 */
 
1212
                                blobAddData(u, &c, 1);
 
1213
 
 
1214
                                if((l < (newlen - 1L)) && (data[l + 1] == 0x90)) {
 
1215
                                        int count;
 
1216
 
 
1217
                                        l += 2;
 
1218
                                        count = data[l];
 
1219
 
 
1220
                                        if(count == 0) {
 
1221
                                                c = 0x90;
 
1222
                                                blobAddData(u, &c, 1);
 
1223
                                        } else {
 
1224
#ifdef  CL_DEBUG
 
1225
                                                cli_dbgmsg("uncompress HQX7 at 0x%06lu: %d repetitive bytes\n", l, count);
 
1226
#endif
 
1227
                                                blobGrow(u, count);
 
1228
                                                while(--count > 0)
 
1229
                                                        blobAddData(u, &c, 1);
 
1230
                                        }
 
1231
                                }
 
1232
                        }
 
1233
                        blobDestroy(tmp);
 
1234
                        tmp = u;
 
1235
                        data = blobGetData(tmp);
 
1236
                        len = blobGetDataSize(tmp);
 
1237
                        cli_dbgmsg("Uncompressed %lu bytes to %lu\n", newlen, len);
 
1238
                } else {
 
1239
                        len = newlen;
 
1240
                        cli_dbgmsg("HQX7 message (%lu bytes) is not compressed\n",
 
1241
                                len);
 
1242
                }
 
1243
                if(len == 0) {
 
1244
                        cli_warnmsg("Discarding empty binHex attachment\n");
 
1245
                        (*destroy)(ret);
 
1246
                        blobDestroy(tmp);
 
1247
                        return NULL;
 
1248
                }
 
1249
 
 
1250
                /*
 
1251
                 * The blob tmp now contains the uncompressed data
 
1252
                 * of len bytes, i.e. the repetitive bytes have been removed
 
1253
                 */
 
1254
 
 
1255
                /*
 
1256
                 * Parse the header
 
1257
                 *
 
1258
                 * TODO: set filename argument in message as well
 
1259
                 */
 
1260
                byte = data[0];
 
1261
                if(byte >= len) {
 
1262
                        (*destroy)(ret);
 
1263
                        blobDestroy(tmp);
 
1264
                        return NULL;
 
1265
                }
 
1266
                filename = cli_malloc(byte + 1);
 
1267
                if(filename == NULL) {
 
1268
                        (*destroy)(ret);
 
1269
                        blobDestroy(tmp);
 
1270
                        return NULL;
 
1271
                }
 
1272
                memcpy(filename, &data[1], byte);
 
1273
                filename[byte] = '\0';
 
1274
                (*setFilename)(ret, dir, filename);
 
1275
                /*ptr = cli_malloc(strlen(filename) + 6);*/
 
1276
                ptr = cli_malloc(byte + 6);
 
1277
                if(ptr) {
 
1278
                        sprintf(ptr, "name=%s", filename);
 
1279
                        messageAddArgument(m, ptr);
 
1280
                        free(ptr);
 
1281
                }
 
1282
 
 
1283
                /*
 
1284
                 * skip over length, filename, version, type, creator and flags
 
1285
                 */
 
1286
                byte = 1 + byte + 1 + 4 + 4 + 2;
 
1287
 
 
1288
                /*
 
1289
                 * Set len to be the data fork length
 
1290
                 */
 
1291
                dataforklen = ((data[byte] << 24) & 0xFF000000) |
 
1292
                        ((data[byte + 1] << 16) & 0xFF0000) |
 
1293
                        ((data[byte + 2] << 8) & 0xFF00) |
 
1294
                        (data[byte + 3] & 0xFF);
 
1295
 
 
1296
                resourceforklen = ((data[byte + 4] << 24) & 0xFF000000) |
 
1297
                        ((data[byte + 5] << 16) & 0xFF0000) |
 
1298
                        ((data[byte + 6] << 8) & 0xFF00) |
 
1299
                        (data[byte + 7] & 0xFF);
 
1300
 
 
1301
                cli_dbgmsg("Filename = '%s', data fork length = %lu, resource fork length = %lu bytes\n",
 
1302
                        filename, dataforklen, resourceforklen);
 
1303
 
 
1304
                free((char *)filename);
 
1305
 
 
1306
                /*
 
1307
                 * Skip over data fork length, resource fork length and CRC
 
1308
                 */
 
1309
                byte += 10;
 
1310
 
 
1311
                l = blobGetDataSize(tmp) - byte;
 
1312
 
 
1313
                if(l < dataforklen) {
 
1314
                        cli_warnmsg("Corrupt BinHex file, claims it is %lu bytes long in a message of %lu bytes\n",
 
1315
                                dataforklen, l);
 
1316
                        dataforklen = l;
 
1317
                }
 
1318
                if(setCTX && m->ctx)
 
1319
                        (*setCTX)(ret, m->ctx);
 
1320
 
 
1321
                (*addData)(ret, &data[byte], dataforklen);
 
1322
 
 
1323
                blobDestroy(tmp);
 
1324
 
 
1325
                if(destroy_text)
 
1326
                        m->binhex = NULL;
 
1327
 
 
1328
                if((m->numberOfEncTypes == 0) ||
 
1329
                   ((m->numberOfEncTypes == 1) && (m->encodingTypes[0] == BINHEX))) {
 
1330
                        cli_dbgmsg("Finished exporting binhex file\n");
 
1331
                        return ret;
 
1332
                }
 
1333
        }
 
1334
 
1176
1335
        if(m->numberOfEncTypes == 0) {
1177
1336
                /*
1178
1337
                 * Fast copy
1179
1338
                 */
1180
1339
                cli_dbgmsg("messageExport: Entering fast copy mode\n");
1181
1340
 
1182
 
#if     0
1183
 
                filename = messageGetFilename(m);
1184
 
 
1185
 
                if(filename == NULL) {
1186
 
                        cli_dbgmsg("Unencoded attachment sent with no filename\n");
1187
 
                        messageAddArgument(m, "name=attachment");
1188
 
                } else if((strcmp(filename, "textportion") != 0) && (strcmp(filename, "mixedtextportion") != 0))
1189
 
                        /*
1190
 
                         * Some virus attachments don't say how they've
1191
 
                         * been encoded. We assume base64
1192
 
                         */
1193
 
                        messageSetEncoding(m, "base64");
1194
 
#else
1195
1341
                filename = (char *)messageFindArgument(m, "filename");
1196
1342
                if(filename == NULL) {
1197
1343
                        filename = (char *)messageFindArgument(m, "name");
1206
1352
                                 */
1207
1353
                                messageSetEncoding(m, "base64");
1208
1354
                }
1209
 
#endif
1210
1355
 
1211
1356
                (*setFilename)(ret, dir, (filename && *filename) ? filename : "attachment");
1212
1357
 
1229
1374
 
1230
1375
                        newret = (*create)();
1231
1376
                        if(newret == NULL) {
1232
 
                                cli_dbgmsg("Not all decoding algorithms were run\n");
 
1377
                                cli_errmsg("Not all decoding algorithms were run\n");
1233
1378
                                return ret;
1234
1379
                        }
1235
1380
                        (*destroy)(ret);
1236
1381
                        ret = newret;
1237
1382
                }
1238
 
                cli_dbgmsg("messageExport: enctype %d is %d\n", i, (int)enctype);
 
1383
                cli_dbgmsg("messageExport: enctype %d is %d\n", i, enctype);
1239
1384
                /*
1240
1385
                 * Find the filename to decode
1241
1386
                 */
1242
 
                if(((enctype == YENCODE) || (i == 0)) && yEncBegin(m)) {
 
1387
                if(((enctype == YENCODE) && yEncBegin(m)) || ((i == 0) && yEncBegin(m))) {
1243
1388
                        const char *f;
1244
1389
 
1245
1390
                        /*
1268
1413
                } else {
1269
1414
                        if(enctype == UUENCODE) {
1270
1415
                                /*
1271
 
                                 * The body will have been stripped out by the
1272
 
                                 * fast track visa system. Treat as plain/text,
1273
 
                                 * which means we'll still scan for funnies
1274
 
                                 * outside of the uuencoded portion.
 
1416
                                 * The body will have been stripped out by the fast track visa
 
1417
                                 * system. Treat as plain/text, which means we'll still scan
 
1418
                                 * for funnies outside of the uuencoded portion.
1275
1419
                                 */
1276
1420
                                cli_dbgmsg("messageExport: treat uuencode as text/plain\n");
1277
1421
                                enctype = m->encodingTypes[i] = NOENCODING;
1278
1422
                        }
1279
 
                        filename = messageGetFilename(m);
1280
 
 
 
1423
                        filename = (char *)messageFindArgument(m, "filename");
1281
1424
                        if(filename == NULL) {
1282
 
                                cli_dbgmsg("Attachment sent with no filename\n");
1283
 
                                messageAddArgument(m, "name=attachment");
1284
 
                        } else if(enctype == NOENCODING)
1285
 
                                /*
1286
 
                                 * Some virus attachments don't say how
1287
 
                                 * they've been encoded. We assume
1288
 
                                 * base64.
1289
 
                                 *
1290
 
                                 * FIXME: don't do this if it's a fall
1291
 
                                 * through from uuencode
1292
 
                                 */
1293
 
                                messageSetEncoding(m, "base64");
 
1425
                                filename = (char *)messageFindArgument(m, "name");
 
1426
 
 
1427
                                if(filename == NULL) {
 
1428
                                        cli_dbgmsg("Attachment sent with no filename\n");
 
1429
                                        messageAddArgument(m, "name=attachment");
 
1430
                                } else if(enctype == NOENCODING)
 
1431
                                        /*
 
1432
                                         * Some virus attachments don't say how
 
1433
                                         * they've been encoded. We assume
 
1434
                                         * base64.
 
1435
                                         *
 
1436
                                         * FIXME: don't do this if it's a fall
 
1437
                                         * through from uuencode
 
1438
                                         */
 
1439
                                        messageSetEncoding(m, "base64");
 
1440
                        }
1294
1441
 
1295
1442
                        (*setFilename)(ret, dir, (filename && *filename) ? filename : "attachment");
1296
1443
 
1305
1452
                 * message
1306
1453
                 */
1307
1454
                if(t_line == NULL) {
1308
 
                        cli_dbgmsg("Empty attachment not saved\n");
 
1455
                        cli_warnmsg("Empty attachment not saved\n");
1309
1456
                        (*destroy)(ret);
1310
1457
                        return NULL;
1311
1458
                }
1384
1531
                } while((t_line = t_line->t_next) != NULL);
1385
1532
 
1386
1533
                cli_dbgmsg("Exported %lu bytes using enctype %d\n",
1387
 
                        (unsigned long)size, (int)enctype);
 
1534
                        (unsigned long)size, enctype);
1388
1535
 
1389
1536
                /* Verify we have nothing left to flush out */
1390
1537
                if(m->base64chars) {
1403
1550
unsigned char *
1404
1551
base64Flush(message *m, unsigned char *buf)
1405
1552
{
1406
 
        cli_dbgmsg("%d trailing bytes to export\n", m->base64chars);
 
1553
        cli_dbgmsg("%u trailing bytes to export\n", m->base64chars);
1407
1554
 
1408
1555
        if(m->base64chars) {
1409
1556
                unsigned char *ret = decode(m, NULL, buf, base64, FALSE);
1415
1562
        return NULL;
1416
1563
}
1417
1564
 
1418
 
int messageSavePartial(message *m, const char *dir, const char *md5id, unsigned part)
1419
 
{
1420
 
        char fullname[1024];
1421
 
        fileblob *fb;
1422
 
        unsigned long time_val;
1423
 
 
1424
 
        cli_dbgmsg("messageSavePartial\n");
1425
 
        time_val  = time(NULL);
1426
 
        snprintf(fullname, 1024, "%s"PATHSEP"clamav-partial-%lu_%s-%u", dir, time_val, md5id, part);
1427
 
 
1428
 
        fb = messageExport(m, fullname,
1429
 
                (void *(*)(void))fileblobCreate,
1430
 
                (void(*)(void *))fileblobDestroy,
1431
 
                (void(*)(void *, const char *, const char *))fileblobPartialSet,
1432
 
                (void(*)(void *, const unsigned char *, size_t))fileblobAddData,
1433
 
                (void *(*)(text *, void *, int))textToFileblob,
1434
 
                (void(*)(void *, cli_ctx *))fileblobSetCTX,
1435
 
                0);
1436
 
        if(!fb)
1437
 
                return CL_EFORMAT;
1438
 
        fileblobDestroy(fb);
1439
 
        return CL_SUCCESS;
1440
 
}
1441
 
 
1442
1565
/*
1443
1566
 * Decode and transfer the contents of the message into a fileblob
1444
1567
 * The caller must free the returned fileblob
1539
1662
                const encoding_type enctype = m->encodingTypes[i];
1540
1663
 
1541
1664
                cli_dbgmsg("messageToText: export transfer method %d = %d\n",
1542
 
                        i, (int)enctype);
 
1665
                        i, enctype);
1543
1666
 
1544
1667
                switch(enctype) {
1545
1668
                        case NOENCODING:
1551
1674
                                for(t_line = messageGetBody(m); t_line; t_line = t_line->t_next) {
1552
1675
                                        if(first == NULL)
1553
1676
                                                first = last = cli_malloc(sizeof(text));
1554
 
                                        else if (last) {
 
1677
                                        else {
1555
1678
                                                last->t_next = cli_malloc(sizeof(text));
1556
1679
                                                last = last->t_next;
1557
1680
                                        }
1558
1681
 
1559
1682
                                        if(last == NULL) {
1560
1683
                                                if(first) {
 
1684
                                                        last->t_next = NULL;
1561
1685
                                                        textDestroy(first);
1562
1686
                                                }
1563
1687
                                                return NULL;
1569
1693
                                }
1570
1694
                                continue;
1571
1695
                        case UUENCODE:
1572
 
                                cli_warnmsg("messageToText: Unexpected attempt to handle uuencoded file\n");
 
1696
                                cli_errmsg("messageToText: Unexpected attempt to handle uuencoded file - report to http://bugs.clamav.net\n");
1573
1697
                                if(first) {
1574
 
                                        if(last)
1575
 
                                                last->t_next = NULL;
 
1698
                                        last->t_next = NULL;
1576
1699
                                        textDestroy(first);
1577
1700
                                }
1578
1701
                                return NULL;
1582
1705
                                if(t_line == NULL) {
1583
1706
                                        /*cli_warnmsg("YENCODED attachment is missing begin statement\n");*/
1584
1707
                                        if(first) {
1585
 
                                                if(last)
1586
 
                                                        last->t_next = NULL;
 
1708
                                                last->t_next = NULL;
1587
1709
                                                textDestroy(first);
1588
1710
                                        }
1589
1711
                                        return NULL;
1619
1741
 
1620
1742
                        if(first == NULL)
1621
1743
                                first = last = cli_malloc(sizeof(text));
1622
 
                        else if (last) {
 
1744
                        else {
1623
1745
                                last->t_next = cli_malloc(sizeof(text));
1624
1746
                                last = last->t_next;
1625
1747
                        }
1657
1779
                        if(decode(m, NULL, data, base64, FALSE) && data[0]) {
1658
1780
                                if(first == NULL)
1659
1781
                                        first = last = cli_malloc(sizeof(text));
1660
 
                                else if (last) {
 
1782
                                else {
1661
1783
                                        last->t_next = cli_malloc(sizeof(text));
1662
1784
                                        last = last->t_next;
1663
1785
                                }
1760
1882
}
1761
1883
#endif
1762
1884
 
 
1885
void
 
1886
messageClearMarkers(message *m)
 
1887
{
 
1888
        m->encoding = m->bounce = m->binhex = NULL;
 
1889
}
 
1890
 
1763
1891
/*
1764
1892
 * Decode a line and add it to a buffer, return the end of the buffer
1765
1893
 * to help appending callers. There is no new line at the end of "line"
1822
1950
                                        }
1823
1951
 
1824
1952
                                        /*
 
1953
                                         * Fix by Torok Edvin
 
1954
                                         * <edwintorok@gmail.com>
1825
1955
                                         * Handle messages that use a broken
1826
1956
                                         * quoted-printable encoding of
1827
1957
                                         * href=\"http://, instead of =3D
1828
1958
                                         */
1829
 
                                        if(byte != '=')
1830
 
                                                byte = (byte << 4) | hex(*line);
1831
 
                                        else
 
1959
                                        if(byte != '=') {
 
1960
                                                byte <<= 4;
 
1961
                                                byte += hex(*line);
 
1962
                                        } else
1832
1963
                                                line -= 2;
1833
1964
 
1834
1965
                                        *buf++ = byte;
1903
2034
                                 * the maximum length of a uuencoded line is
1904
2035
                                 * 62 characters
1905
2036
                                 */
1906
 
                                cli_dbgmsg("uudecode: buffer overflow stopped, attempting to ignore but decoding may fail\n");
 
2037
                                cli_warnmsg("uudecode: buffer overflow stopped, attempting to ignore but decoding may fail\n");
1907
2038
                        else {
1908
2039
                                (void)decode(m, line, buf, uudecode, (len & 3) == 0);
1909
2040
                                buf = &buf[reallen];
1939
2070
static void
1940
2071
sanitiseBase64(char *s)
1941
2072
{
1942
 
        cli_dbgmsg("sanitiseBase64 '%s'\n", s);
1943
 
        while(*s)
 
2073
        /*cli_dbgmsg("sanitiseBase64 '%s'\n", s);*/
 
2074
        for(; *s; s++)
1944
2075
                if(base64Table[(unsigned int)(*s & 0xFF)] == 255) {
1945
2076
                        char *p1;
1946
2077
 
1947
2078
                        for(p1 = s; p1[0] != '\0'; p1++)
1948
2079
                                p1[0] = p1[1];
1949
 
                } else
1950
 
                        s++;
 
2080
                        --s;
 
2081
                }
1951
2082
}
1952
2083
 
1953
2084
/*
2038
2169
                        case 4:
2039
2170
                                *out++ = (b1 << 2) | ((b2 >> 4) & 0x3);
2040
2171
                                *out++ = (b2 << 4) | ((b3 >> 2) & 0xF);
2041
 
                                if((nbytes == 4) || (b3&0x3))
 
2172
                                if((nbytes == 4) || b3)
2042
2173
                                        *out++ = (b3 << 6) | (b4 & 0x3F);
2043
2174
                                break;
2044
2175
                        case 2:
2092
2223
                }
2093
2224
 
2094
2225
                switch(nbytes) {
2095
 
                        case 4:
2096
 
                                *out++ = (b1 << 2) | ((b2 >> 4) & 0x3);
2097
 
                                *out++ = (b2 << 4) | ((b3 >> 2) & 0xF);
2098
 
                                *out++ = (b3 << 6) | (b4 & 0x3F);
2099
 
                                continue;
2100
2226
                        case 3:
2101
2227
                                m->base64_3 = b3;
2102
2228
                        case 2:
2103
2229
                                m->base64_2 = b2;
2104
2230
                        case 1:
2105
2231
                                m->base64_1 = b1;
2106
 
                                m->base64chars = nbytes;
 
2232
                                break;
 
2233
                        case 4:
 
2234
                                *out++ = (b1 << 2) | ((b2 >> 4) & 0x3);
 
2235
                                *out++ = (b2 << 4) | ((b3 >> 2) & 0xF);
 
2236
                                *out++ = (b3 << 6) | (b4 & 0x3F);
2107
2237
                                break;
2108
2238
                        default:
2109
2239
                                assert(0);
2110
2240
                }
2111
 
                break;  /* nbytes != 4 => EOL */
 
2241
                if(nbytes != 4) {
 
2242
                        m->base64chars = nbytes;
 
2243
                        break;
 
2244
                }
2112
2245
        }
2113
2246
        return out;
2114
2247
}
2276
2409
        enum { LANGUAGE, CHARSET, CONTENTS } field;
2277
2410
 
2278
2411
        if(strstr(in, "*0*=") != NULL) {
2279
 
                char *p;
2280
 
 
2281
 
                /* Don't handle continuations, decode what we can */
2282
 
                p = ret = cli_malloc(strlen(in) + 16);
2283
 
                if(ret == NULL)
2284
 
                        return NULL;
2285
 
 
2286
 
                do {
2287
 
                        switch(*in) {
2288
 
                                default:
2289
 
                                        *p++ = *in++;
2290
 
                                        continue;
2291
 
                                case '*':
2292
 
                                        do
2293
 
                                                in++;
2294
 
                                        while((*in != '*') && *in);
2295
 
                                        if(*in) {
2296
 
                                                in++;
2297
 
                                                continue;
2298
 
                                        }
2299
 
                                        *p = '\0';
2300
 
                                        break;
2301
 
                                case '=':
2302
 
                                        /*strcpy(p, in);*/
2303
 
                                        strcpy(p, "=rfc2231failure");
2304
 
                                        break;
2305
 
                        }
2306
 
                        break;
2307
 
                } while(*in);
2308
 
 
2309
 
                cli_dbgmsg("RFC2231 parameter continuations are not yet handled, returning \"%s\"\n",
2310
 
                        ret);
2311
 
                return ret;
 
2412
                cli_warnmsg("RFC2231 parameter continuations are not yet handled\n");
 
2413
                return cli_strdup(in);
2312
2414
        }
2313
2415
 
2314
2416
        ptr = strstr(in, "*0=");
2323
2425
                field = LANGUAGE;
2324
2426
        }
2325
2427
 
2326
 
        if(ptr == NULL) {       /* quick return */
2327
 
                out = ret = cli_strdup(in);
2328
 
                while(*out)
2329
 
                        *out++ &= 0x7F;
2330
 
                return ret;
2331
 
        }
 
2428
        if(ptr == NULL) /* quick return */
 
2429
                return cli_strdup(in);
2332
2430
 
2333
2431
        cli_dbgmsg("rfc2231 '%s'\n", in);
2334
2432
 
2397
2495
 
2398
2496
        if(field != CONTENTS) {
2399
2497
                free(ret);
2400
 
                cli_dbgmsg("Invalid RFC2231 header: '%s'\n", in);
 
2498
                cli_warnmsg("Invalid RFC2231 header: '%s'\n", in);
2401
2499
                return cli_strdup("");
2402
2500
        }
2403
2501