~ubuntu-branches/ubuntu/vivid/exim4/vivid

« back to all changes in this revision

Viewing changes to src/malware.c

  • Committer: Package Import Robot
  • Author(s): Corey Bryant
  • Date: 2014-08-04 11:48:39 UTC
  • mfrom: (56.1.2 utopic)
  • Revision ID: package-import@ubuntu.com-20140804114839-xoulpcx9nxi5m72u
Tags: 4.84~RC1-3ubuntu1
* Merge from Debian unstable (LP: #1351470). Remaining changes:
  - Show Ubuntu distribution on smtp:
    + debian/patches/fix_smtp_banner.patch: updated SMTP banner
      with Ubuntu distribution
    + debian/control: added lsb-release build dependency
  - Don't provide default-mta; in Ubuntu, we want postfix to be the
    default.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
*     Exim - an Internet mail transport agent    *
3
3
*************************************************/
4
4
 
5
 
/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
 
5
/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-2014 */
6
6
/* License: GPL */
7
7
 
8
8
/* Code for calling virus (malware) scanners. Called from acl.c. */
10
10
#include "exim.h"
11
11
#ifdef WITH_CONTENT_SCAN
12
12
 
 
13
typedef enum {M_FPROTD, M_DRWEB, M_AVES, M_FSEC, M_KAVD, M_CMDL,
 
14
                M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD} scanner_t;
 
15
typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t;
 
16
static struct scan
 
17
{
 
18
  scanner_t     scancode;
 
19
  const uschar * name;
 
20
  const uschar * options_default;
 
21
  contype_t     conn;
 
22
} m_scans[] =
 
23
{
 
24
  { M_FPROTD,   US"f-protd",    US"localhost 10200-10204",            MC_TCP },
 
25
  { M_DRWEB,    US"drweb",      US"/usr/local/drweb/run/drwebd.sock", MC_STRM },
 
26
  { M_AVES,     US"aveserver",  US"/var/run/aveserver",               MC_UNIX },
 
27
  { M_FSEC,     US"fsecure",    US"/var/run/.fsav",                   MC_UNIX },
 
28
  { M_KAVD,     US"kavdaemon",  US"/var/run/AvpCtl",                  MC_UNIX },
 
29
  { M_CMDL,     US"cmdline",    NULL,                                 MC_NONE },
 
30
  { M_SOPHIE,   US"sophie",     US"/var/run/sophie",                  MC_UNIX },
 
31
  { M_CLAMD,    US"clamd",      US"/tmp/clamd",                       MC_NONE },
 
32
  { M_SOCK,     US"sock",       US"/tmp/malware.sock",                MC_STRM },
 
33
  { M_MKSD,     US"mksd",       NULL,                                 MC_NONE },
 
34
  { -1,         NULL,           NULL, MC_NONE }         /* end-marker */
 
35
};
 
36
 
13
37
/* The maximum number of clamd servers that are supported in the configuration */
14
38
#define MAX_CLAMD_SERVERS 32
15
39
#define MAX_CLAMD_SERVERS_S "32"
18
42
#define MAX_CLAMD_ADDRESS_LENGTH_S "64"
19
43
 
20
44
typedef struct clamd_address_container {
21
 
  uschar tcp_addr[MAX_CLAMD_ADDRESS_LENGTH];
 
45
  uschar tcp_addr[MAX_CLAMD_ADDRESS_LENGTH+1];
22
46
  unsigned int tcp_port;
23
47
} clamd_address_container;
24
48
 
25
49
/* declaration of private routines */
26
 
static int mksd_scan_packed(int sock, uschar *scan_filename);
 
50
static int mksd_scan_packed(struct scan * scanent, int sock, uschar *scan_filename);
27
51
static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking);
28
52
 
29
 
/* SHUT_WR seems to be undefined on Unixware? */
30
 
#ifndef SHUT_WR
31
 
#define SHUT_WR 1
 
53
#ifndef nelements
 
54
# define nelements(arr) (sizeof(arr) / sizeof(arr[0]))
32
55
#endif
33
56
 
34
57
 
44
67
#define DERR_TIMEOUT                (1<<9)   /* scan timeout has run out */
45
68
#define DERR_BAD_CALL               (1<<15)  /* wrong command */
46
69
 
47
 
/* Routine to check whether a system is big- or litte-endian.
 
70
/* Routine to check whether a system is big- or little-endian.
48
71
   Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
49
72
   Needed for proper kavdaemon implementation. Sigh. */
50
73
#define BIG_MY_ENDIAN      0
51
74
#define LITTLE_MY_ENDIAN   1
52
 
int test_byte_order(void);
53
 
int test_byte_order() {
54
 
      short int word = 0x0001;
55
 
      char *byte = (char *) &word;
56
 
      return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
 
75
static int test_byte_order(void);
 
76
static inline int
 
77
test_byte_order()
 
78
{
 
79
  short int word = 0x0001;
 
80
  char *byte = (char *) &word;
 
81
  return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
57
82
}
58
83
 
59
 
uschar malware_name_buffer[256];
60
 
int malware_ok = 0;
 
84
BOOL malware_ok = FALSE;
61
85
 
62
86
/* Gross hacks for the -bmalware option; perhaps we should just create
63
87
the scan directory normally for that case, but look into rigging up the
78
102
Returns:      Exim message processing code (OK, FAIL, DEFER, ...)
79
103
              where true means malware was found (condition applies)
80
104
*/
81
 
int malware(uschar **listptr) {
82
 
  uschar scan_filename[1024];
83
 
  BOOL fits;
 
105
int
 
106
malware(uschar **listptr)
 
107
{
 
108
  uschar * scan_filename;
84
109
  int ret;
85
110
 
86
 
  fits = string_format(scan_filename, sizeof(scan_filename),
87
 
      CS"%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
88
 
  if (!fits)
89
 
    {
90
 
    av_failed = TRUE;
91
 
    log_write(0, LOG_MAIN|LOG_PANIC,
92
 
        "malware filename does not fit in buffer [malware()]");
93
 
    return DEFER;
94
 
  }
95
 
 
 
111
  scan_filename = string_sprintf("%s/scan/%s/%s.eml",
 
112
                    spool_directory, message_id, message_id);
96
113
  ret = malware_internal(listptr, scan_filename, FALSE);
97
114
  if (ret == DEFER) av_failed = TRUE;
98
115
 
116
133
                where true means malware was found (condition applies)
117
134
*/
118
135
int
119
 
malware_in_file(uschar *eml_filename) {
 
136
malware_in_file(uschar *eml_filename)
 
137
{
120
138
  uschar *scan_options[2];
121
139
  uschar message_id_buf[64];
122
140
  int ret;
150
168
}
151
169
 
152
170
 
 
171
static inline int
 
172
malware_errlog_defer(const uschar * str)
 
173
{
 
174
  log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str);
 
175
  return DEFER;
 
176
}
 
177
 
 
178
static int
 
179
m_errlog_defer(struct scan * scanent, const uschar * str)
 
180
{
 
181
  return malware_errlog_defer(string_sprintf("%s: %s", scanent->name, str));
 
182
}
 
183
static int
 
184
m_errlog_defer_3(struct scan * scanent, const uschar * str,
 
185
        int fd_to_close)
 
186
{
 
187
  (void) close(fd_to_close);
 
188
  return m_errlog_defer(scanent, str);
 
189
}
 
190
 
 
191
/*************************************************/
 
192
 
 
193
/* Only used by the Clamav code, which is working from a list of servers and
 
194
uses the returned in_addr to get a second connection to the same system.
 
195
*/
 
196
static inline int
 
197
m_tcpsocket(const uschar * hostname, unsigned int port,
 
198
        host_item * host, uschar ** errstr)
 
199
{
 
200
  return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5, host, errstr);
 
201
}
 
202
 
 
203
static int
 
204
m_tcpsocket_fromdef(const uschar * hostport, uschar ** errstr)
 
205
{
 
206
  int scan;
 
207
  uschar hostname[256];
 
208
  unsigned int portlow, porthigh;
 
209
 
 
210
  /* extract host and port part */
 
211
  scan = sscanf(CS hostport, "%255s %u-%u", hostname, &portlow, &porthigh);
 
212
  if ( scan != 3 ) {
 
213
    if ( scan != 2 ) {
 
214
      *errstr = string_sprintf("invalid socket '%s'", hostport);
 
215
      return -1;
 
216
    }
 
217
    porthigh = portlow;
 
218
  }
 
219
 
 
220
  return ip_connectedsocket(SOCK_STREAM, hostname, portlow, porthigh,
 
221
                            5, NULL, errstr);
 
222
}
 
223
 
 
224
static int
 
225
m_unixsocket(const uschar * path, uschar ** errstr)
 
226
{
 
227
  int sock;
 
228
  struct sockaddr_un server;
 
229
 
 
230
  if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
 
231
    *errstr = US"can't open UNIX socket.";
 
232
    return -1;
 
233
  }
 
234
 
 
235
  server.sun_family = AF_UNIX;
 
236
  Ustrncpy(server.sun_path, path, sizeof(server.sun_path)-1);
 
237
  server.sun_path[sizeof(server.sun_path)-1] = '\0';
 
238
  if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
 
239
    int err = errno;
 
240
    (void)close(sock);
 
241
    *errstr =  string_sprintf("unable to connect to UNIX socket (%s): %s",
 
242
                  path, strerror(err));
 
243
    return -1;
 
244
    }
 
245
  return sock;
 
246
}
 
247
 
 
248
static inline int
 
249
m_streamsocket(const uschar * spec, uschar ** errstr)
 
250
{
 
251
  return *spec == '/'
 
252
    ? m_unixsocket(spec, errstr) : m_tcpsocket_fromdef(spec, errstr);
 
253
}
 
254
 
 
255
static int
 
256
m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
 
257
{
 
258
  if (send(sock, buf, cnt, 0) < 0) {
 
259
    int err = errno;
 
260
    (void)close(sock);
 
261
    *errstr = string_sprintf("unable to send to socket (%s): %s",
 
262
           buf, strerror(err));
 
263
    return -1;
 
264
    }
 
265
  return sock;
 
266
}
 
267
 
 
268
static const pcre *
 
269
m_pcre_compile(const uschar * re, uschar ** errstr)
 
270
{
 
271
  const uschar * rerror;
 
272
  int roffset;
 
273
  const pcre * cre;
 
274
 
 
275
  cre = pcre_compile(CS re, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
 
276
  if (!cre)
 
277
    *errstr= string_sprintf("regular expression error in '%s': %s at offset %d",
 
278
        re, rerror, roffset);
 
279
  return cre;
 
280
}
 
281
 
 
282
uschar *
 
283
m_pcre_exec(const pcre * cre, uschar * text)
 
284
{
 
285
  int ovector[10*3];
 
286
  int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0,
 
287
                ovector, nelements(ovector));
 
288
  uschar * substr = NULL;
 
289
  if (i >= 2)                           /* Got it */
 
290
    pcre_get_substring(CS text, ovector, i, 1, (const char **) &substr);
 
291
  return substr;
 
292
}
 
293
 
 
294
static const pcre *
 
295
m_pcre_nextinlist(uschar ** list, int * sep, char * listerr, uschar ** errstr)
 
296
{
 
297
  const uschar * list_ele;
 
298
  const pcre * cre = NULL;
 
299
 
 
300
  if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
 
301
    *errstr = US listerr;
 
302
  else
 
303
    cre = m_pcre_compile(CUS list_ele, errstr);
 
304
  return cre;
 
305
}
 
306
 
153
307
/*************************************************
154
308
*          Scan content for malware              *
155
309
*************************************************/
165
319
Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
166
320
                where true means malware was found (condition applies)
167
321
*/
168
 
static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) {
 
322
static int
 
323
malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking)
 
324
{
169
325
  int sep = 0;
170
326
  uschar *list = *listptr;
171
327
  uschar *av_scanner_work = av_scanner;
172
328
  uschar *scanner_name;
173
 
  uschar scanner_name_buffer[16];
174
329
  uschar *malware_regex;
175
 
  uschar malware_regex_buffer[64];
176
330
  uschar malware_regex_default[] = ".+";
177
331
  unsigned long mbox_size;
178
332
  FILE *mbox_file;
179
 
  int roffset;
180
333
  const pcre *re;
181
 
  const uschar *rerror;
 
334
  uschar * errstr;
 
335
  struct scan * scanent;
 
336
  const uschar * scanner_options;
 
337
  int sock = -1;
182
338
 
183
339
  /* make sure the eml mbox file is spooled up */
184
 
  mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL);
185
 
  if (mbox_file == NULL) {
186
 
    /* error while spooling */
187
 
    log_write(0, LOG_MAIN|LOG_PANIC,
188
 
           "malware acl condition: error while creating mbox spool file");
189
 
    return DEFER;
190
 
  };
 
340
  if (!(mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL)))
 
341
    return malware_errlog_defer(US"error while creating mbox spool file");
 
342
 
191
343
  /* none of our current scanners need the mbox
192
344
     file as a stream, so we can close it right away */
193
345
  (void)fclose(mbox_file);
194
346
 
195
347
  /* extract the malware regex to match against from the option list */
196
 
  if ((malware_regex = string_nextinlist(&list, &sep,
197
 
                                         malware_regex_buffer,
198
 
                                         sizeof(malware_regex_buffer))) != NULL) {
 
348
  if (!(malware_regex = string_nextinlist(&list, &sep, NULL, 0)))
 
349
    return FAIL;                /* empty means "don't match anything" */
199
350
 
200
 
    /* parse 1st option */
 
351
  /* parse 1st option */
201
352
    if ( (strcmpic(malware_regex,US"false") == 0) ||
202
 
         (Ustrcmp(malware_regex,"0") == 0) ) {
203
 
      /* explicitly no matching */
204
 
      return FAIL;
205
 
    };
 
353
       (Ustrcmp(malware_regex,"0") == 0) )
 
354
    return FAIL;                /* explicitly no matching */
206
355
 
207
 
    /* special cases (match anything except empty) */
208
 
    if ( (strcmpic(malware_regex,US"true") == 0) ||
209
 
         (Ustrcmp(malware_regex,"*") == 0) ||
210
 
         (Ustrcmp(malware_regex,"1") == 0) ) {
211
 
      malware_regex = malware_regex_default;
212
 
    };
213
 
  }
214
 
  else {
215
 
    /* empty means "don't match anything" */
216
 
    return FAIL;
217
 
  };
 
356
  /* special cases (match anything except empty) */
 
357
  if ( (strcmpic(malware_regex,US"true") == 0) ||
 
358
       (Ustrcmp(malware_regex,"*") == 0) ||
 
359
       (Ustrcmp(malware_regex,"1") == 0) )
 
360
    malware_regex = malware_regex_default;
218
361
 
219
362
  /* Reset sep that is set by previous string_nextinlist() call */
220
363
  sep = 0;
221
364
 
222
365
  /* compile the regex, see if it works */
223
 
  re = pcre_compile(CS malware_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
224
 
  if (re == NULL) {
225
 
    log_write(0, LOG_MAIN|LOG_PANIC,
226
 
             "malware acl condition: regular expression error in '%s': %s at offset %d", malware_regex, rerror, roffset);
227
 
    return DEFER;
228
 
  };
 
366
  if (!(re = m_pcre_compile(malware_regex, &errstr)))
 
367
    return malware_errlog_defer(errstr);
229
368
 
230
369
  /* if av_scanner starts with a dollar, expand it first */
231
370
  if (*av_scanner == '$') {
232
 
    av_scanner_work = expand_string(av_scanner);
233
 
    if (av_scanner_work == NULL) {
234
 
      log_write(0, LOG_MAIN|LOG_PANIC,
235
 
           "malware acl condition: av_scanner starts with $, but expansion failed: %s", expand_string_message);
236
 
      return DEFER;
237
 
    }
238
 
    else {
239
 
      debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
240
 
      /* disable result caching in this case */
241
 
      malware_name = NULL;
242
 
      malware_ok = 0;
243
 
    };
 
371
    if (!(av_scanner_work = expand_string(av_scanner)))
 
372
      return malware_errlog_defer(
 
373
           string_sprintf("av_scanner starts with $, but expansion failed: %s",
 
374
           expand_string_message));
 
375
 
 
376
    debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
 
377
    /* disable result caching in this case */
 
378
    malware_name = NULL;
 
379
    malware_ok = FALSE;
244
380
  }
245
381
 
246
 
  /* Do not scan twice. */
247
 
  if (malware_ok == 0) {
 
382
  /* Do not scan twice (unless av_scanner is dynamic). */
 
383
  if (!malware_ok) {
248
384
 
249
385
    /* find the scanner type from the av_scanner option */
250
 
    if ((scanner_name = string_nextinlist(&av_scanner_work, &sep,
251
 
                                          scanner_name_buffer,
252
 
                                          sizeof(scanner_name_buffer))) == NULL) {
253
 
      /* no scanner given */
254
 
      log_write(0, LOG_MAIN|LOG_PANIC,
255
 
             "malware acl condition: av_scanner configuration variable is empty");
256
 
      return DEFER;
257
 
    };
258
 
 
259
 
  /* "f-protd" scanner type ----------------------------------------------- */
260
 
  if (strcmpic(scanner_name, US"f-protd") == 0) {
261
 
    uschar *fp_options, *fp_scan_option;
262
 
    uschar fp_scan_option_buffer[1024];
263
 
    uschar fp_options_buffer[1024];
264
 
    uschar fp_options_default[] = "localhost 10200-10204";
265
 
    uschar hostname[256];
266
 
    unsigned int port, portlow, porthigh, connect_ok=0, detected=0, par_count = 0;
267
 
    struct hostent *he;
268
 
    struct in_addr in;
269
 
    int sock;
270
 
    uschar scanrequest[2048], buf[32768], *strhelper, *strhelper2;
271
 
 
272
 
    if ((fp_options = string_nextinlist(&av_scanner_work, &sep,
273
 
      fp_options_buffer, sizeof(fp_options_buffer))) == NULL) {
274
 
      /* no options supplied, use default options */
275
 
      fp_options = fp_options_default;
276
 
    };
277
 
 
278
 
    /* extract host and port part */
279
 
    if ( sscanf(CS fp_options, "%s %u-%u", hostname, &portlow, &porthigh) != 3 ) {
280
 
      if ( sscanf(CS fp_options, "%s %u", hostname, &portlow) != 2 ) {
281
 
        log_write(0, LOG_MAIN|LOG_PANIC,
282
 
          "malware acl condition: f-protd: invalid socket '%s'", fp_options);
283
 
        return DEFER;
284
 
      }
285
 
      porthigh = portlow;
286
 
    }
287
 
 
288
 
    /* Lookup the host */
289
 
    if((he = gethostbyname(CS hostname)) == 0) {
290
 
      log_write(0, LOG_MAIN|LOG_PANIC,
291
 
        "malware acl condition: f-protd: failed to lookup host '%s'", hostname);
292
 
      return DEFER;
293
 
    }
294
 
 
295
 
    in = *(struct in_addr *) he->h_addr_list[0];
296
 
    port = portlow;
297
 
 
298
 
 
299
 
    /* Open the f-protd TCP socket */
300
 
    if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
301
 
      log_write(0, LOG_MAIN|LOG_PANIC,
302
 
        "malware acl condition: f-protd: unable to acquire socket (%s)",
303
 
        strerror(errno));
304
 
      return DEFER;
305
 
    }
306
 
 
307
 
    /* Try to connect to all portslow-high until connection is established */
308
 
    for (port = portlow; !connect_ok && port < porthigh; port++) {
309
 
      if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) >= 0) {
310
 
        connect_ok = 1;
311
 
      }
312
 
    }
313
 
 
314
 
    if ( !connect_ok ) {
315
 
      log_write(0, LOG_MAIN|LOG_PANIC,
316
 
        "malware acl condition: f-protd: connection to %s, port %u-%u failed (%s)",
317
 
        inet_ntoa(in), portlow, porthigh, strerror(errno));
318
 
      (void)close(sock);
319
 
      return DEFER;
320
 
    }
321
 
 
322
 
    DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
323
 
    (void)string_format(scanrequest, 1024, CS"GET %s", eml_filename);
324
 
 
325
 
    while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
326
 
      fp_scan_option_buffer, sizeof(fp_scan_option_buffer))) != NULL) {
327
 
      if ( par_count ) {
328
 
        Ustrcat(scanrequest, "%20");
329
 
      } else {
330
 
        Ustrcat(scanrequest, "?");
331
 
      }
332
 
      Ustrcat(scanrequest, fp_scan_option);
333
 
      par_count++;
334
 
    }
335
 
    Ustrcat(scanrequest, " HTTP/1.0\r\n\r\n");
336
 
 
337
 
    /* send scan request */
338
 
    if (send(sock, &scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
339
 
      (void)close(sock);
340
 
      log_write(0, LOG_MAIN|LOG_PANIC,
341
 
        "malware acl condition: f-protd: unable to send command to socket (%s)", scanrequest);
342
 
      return DEFER;
343
 
    }
344
 
 
345
 
    /* We get a lot of empty lines, so we need this hack to check for any data at all */
346
 
    while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
347
 
      if ( recv_line(sock, buf, 32768) > 0) {
348
 
        if ( Ustrstr(buf, US"<detected type=\"") != NULL ) {
349
 
          detected = 1;
350
 
        } else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
351
 
          if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
352
 
            *strhelper2 = '\0';
353
 
            Ustrcpy(malware_name_buffer, strhelper + 6);
354
 
          }
355
 
        } else if ( Ustrstr(buf, US"<summary code=\"") ) {
356
 
          if ( Ustrstr(buf, US"<summary code=\"11\">") ) {
357
 
            malware_name = malware_name_buffer;
358
 
          } else {
359
 
            malware_name = NULL;
360
 
          }
361
 
        }
362
 
      }
363
 
    }
364
 
    (void)close(sock);
365
 
  }
366
 
  /* "drweb" scanner type ----------------------------------------------- */
367
 
  /* v0.1 - added support for tcp sockets          */
368
 
  /* v0.0 - initial release -- support for unix sockets      */
369
 
  else if (strcmpic(scanner_name,US"drweb") == 0) {
370
 
    uschar *drweb_options;
371
 
    uschar drweb_options_buffer[1024];
372
 
    uschar drweb_options_default[] = "/usr/local/drweb/run/drwebd.sock";
373
 
    struct sockaddr_un server;
374
 
    int sock, result, ovector[30];
375
 
    unsigned int port, fsize;
376
 
    uschar tmpbuf[1024], *drweb_fbuf;
377
 
    uschar drweb_match_string[128];
378
 
    int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
379
 
        drweb_vnum, drweb_slen, drweb_fin = 0x0000;
380
 
    unsigned long bread;
381
 
    uschar hostname[256];
382
 
    struct hostent *he;
383
 
    struct in_addr in;
384
 
    pcre *drweb_re;
385
 
 
386
 
    if ((drweb_options = string_nextinlist(&av_scanner_work, &sep,
387
 
      drweb_options_buffer, sizeof(drweb_options_buffer))) == NULL) {
388
 
      /* no options supplied, use default options */
389
 
      drweb_options = drweb_options_default;
390
 
    };
391
 
 
392
 
    if (*drweb_options != '/') {
393
 
 
394
 
      /* extract host and port part */
395
 
      if( sscanf(CS drweb_options, "%s %u", hostname, &port) != 2 ) {
396
 
        log_write(0, LOG_MAIN|LOG_PANIC,
397
 
          "malware acl condition: drweb: invalid socket '%s'", drweb_options);
398
 
        return DEFER;
399
 
      }
400
 
 
401
 
      /* Lookup the host */
402
 
      if((he = gethostbyname(CS hostname)) == 0) {
403
 
        log_write(0, LOG_MAIN|LOG_PANIC,
404
 
          "malware acl condition: drweb: failed to lookup host '%s'", hostname);
405
 
        return DEFER;
406
 
      }
407
 
 
408
 
      in = *(struct in_addr *) he->h_addr_list[0];
409
 
 
410
 
      /* Open the drwebd TCP socket */
411
 
      if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
412
 
        log_write(0, LOG_MAIN|LOG_PANIC,
413
 
          "malware acl condition: drweb: unable to acquire socket (%s)",
414
 
          strerror(errno));
415
 
        return DEFER;
416
 
      }
417
 
 
418
 
      if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
419
 
        (void)close(sock);
420
 
        log_write(0, LOG_MAIN|LOG_PANIC,
421
 
          "malware acl condition: drweb: connection to %s, port %u failed (%s)",
422
 
          inet_ntoa(in), port, strerror(errno));
423
 
        return DEFER;
424
 
      }
425
 
 
426
 
      /* prepare variables */
427
 
      drweb_cmd = htonl(DRWEBD_SCAN_CMD);
428
 
      drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
429
 
 
430
 
      /* calc file size */
431
 
      drweb_fd = open(CS eml_filename, O_RDONLY);
432
 
      if (drweb_fd == -1) {
433
 
        (void)close(sock);
434
 
        log_write(0, LOG_MAIN|LOG_PANIC,
435
 
          "malware acl condition: drweb: can't open spool file %s: %s",
436
 
          eml_filename, strerror(errno));
437
 
        return DEFER;
438
 
      }
439
 
      fsize = lseek(drweb_fd, 0, SEEK_END);
440
 
      if (fsize == -1) {
441
 
        (void)close(sock);
442
 
        (void)close(drweb_fd);
443
 
        log_write(0, LOG_MAIN|LOG_PANIC,
444
 
          "malware acl condition: drweb: can't seek spool file %s: %s",
445
 
          eml_filename, strerror(errno));
446
 
        return DEFER;
447
 
      }
448
 
      drweb_slen = htonl(fsize);
449
 
      lseek(drweb_fd, 0, SEEK_SET);
450
 
 
451
 
      DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s %u]\n",
452
 
          scanner_name, hostname, port);
453
 
 
454
 
      /* send scan request */
455
 
      if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
456
 
          (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
457
 
          (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
458
 
          (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
459
 
        (void)close(sock);
460
 
        (void)close(drweb_fd);
461
 
        log_write(0, LOG_MAIN|LOG_PANIC,
462
 
          "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
463
 
        return DEFER;
464
 
      }
465
 
 
466
 
      drweb_fbuf = (uschar *) malloc (fsize);
467
 
      if (!drweb_fbuf) {
468
 
        (void)close(sock);
469
 
        (void)close(drweb_fd);
470
 
        log_write(0, LOG_MAIN|LOG_PANIC,
471
 
          "malware acl condition: drweb: unable to allocate memory %u for file (%s)",
472
 
          fsize, eml_filename);
473
 
        return DEFER;
474
 
      }
475
 
 
476
 
      result = read (drweb_fd, drweb_fbuf, fsize);
477
 
      if (result == -1) {
478
 
        (void)close(sock);
479
 
        (void)close(drweb_fd);
480
 
        free(drweb_fbuf);
481
 
        log_write(0, LOG_MAIN|LOG_PANIC,
482
 
          "malware acl condition: drweb: can't read spool file %s: %s",
483
 
          eml_filename, strerror(errno));
484
 
        return DEFER;
485
 
      }
486
 
      (void)close(drweb_fd);
487
 
 
488
 
      /* send file body to socket */
489
 
      if (send(sock, drweb_fbuf, fsize, 0) < 0) {
490
 
        (void)close(sock);
491
 
        free(drweb_fbuf);
492
 
        log_write(0, LOG_MAIN|LOG_PANIC,
493
 
          "malware acl condition: drweb: unable to send file body to socket (%s)", drweb_options);
494
 
        return DEFER;
495
 
      }
496
 
      (void)close(drweb_fd);
497
 
    }
498
 
    else {
499
 
      /* open the drwebd UNIX socket */
500
 
      sock = socket(AF_UNIX, SOCK_STREAM, 0);
501
 
      if (sock < 0) {
502
 
        log_write(0, LOG_MAIN|LOG_PANIC,
503
 
          "malware acl condition: drweb: can't open UNIX socket");
504
 
        return DEFER;
505
 
      }
506
 
      server.sun_family = AF_UNIX;
507
 
      Ustrcpy(server.sun_path, drweb_options);
508
 
      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
509
 
        (void)close(sock);
510
 
        log_write(0, LOG_MAIN|LOG_PANIC,
511
 
          "malware acl condition: drweb: unable to connect to socket (%s). errno=%d", drweb_options, errno);
512
 
        return DEFER;
513
 
      }
514
 
 
515
 
      /* prepare variables */
516
 
      drweb_cmd = htonl(DRWEBD_SCAN_CMD);
517
 
      drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
518
 
      drweb_slen = htonl(Ustrlen(eml_filename));
519
 
 
520
 
      DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
521
 
          scanner_name, drweb_options);
522
 
 
523
 
      /* send scan request */
524
 
      if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
525
 
          (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
526
 
          (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
527
 
          (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
528
 
          (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
529
 
        (void)close(sock);
530
 
        log_write(0, LOG_MAIN|LOG_PANIC,
531
 
          "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
532
 
        return DEFER;
533
 
      }
534
 
    }
535
 
 
536
 
    /* wait for result */
537
 
    if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) {
538
 
      (void)close(sock);
539
 
      log_write(0, LOG_MAIN|LOG_PANIC,
540
 
        "malware acl condition: drweb: unable to read return code");
541
 
      return DEFER;
542
 
    }
543
 
    drweb_rc = ntohl(drweb_rc);
544
 
 
545
 
    if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) {
546
 
      (void)close(sock);
547
 
      log_write(0, LOG_MAIN|LOG_PANIC,
548
 
        "malware acl condition: drweb: unable to read the number of viruses");
549
 
      return DEFER;
550
 
    }
551
 
    drweb_vnum = ntohl(drweb_vnum);
552
 
 
553
 
    /* "virus(es) found" if virus number is > 0 */
554
 
    if (drweb_vnum)
555
 
    {
556
 
      int i;
557
 
      uschar pre_malware_nb[256];
558
 
 
559
 
      malware_name = malware_name_buffer;
560
 
 
561
 
      /* setup default virus name */
562
 
      Ustrcpy(malware_name_buffer,"unknown");
563
 
 
564
 
      /* read and concatenate virus names into one string */
565
 
      for (i=0;i<drweb_vnum;i++)
566
 
      {
567
 
        /* read the size of report */
568
 
        if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen))) {
569
 
          (void)close(sock);
570
 
          log_write(0, LOG_MAIN|LOG_PANIC,
571
 
            "malware acl condition: drweb: cannot read report size");
572
 
          return DEFER;
573
 
        };
574
 
        drweb_slen = ntohl(drweb_slen);
575
 
 
576
 
        /* read report body */
577
 
        if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen) {
578
 
          (void)close(sock);
579
 
          log_write(0, LOG_MAIN|LOG_PANIC,
580
 
            "malware acl condition: drweb: cannot read report string");
581
 
          return DEFER;
582
 
        };
583
 
        tmpbuf[drweb_slen] = '\0';
584
 
 
585
 
        /* set up match regex, depends on retcode */
586
 
        Ustrcpy(drweb_match_string, "infected\\swith\\s*(.+?)$");
587
 
 
588
 
        drweb_re = pcre_compile( CS drweb_match_string,
589
 
          PCRE_COPT,
590
 
          (const char **)&rerror,
591
 
          &roffset,
592
 
          NULL );
593
 
 
594
 
        /* try matcher on the line, grab substring */
595
 
        result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
596
 
        if (result >= 2) {
597
 
          pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS pre_malware_nb, 255);
598
 
        }
599
 
        /* the first name we just copy to malware_name */
600
 
        if (i==0)
601
 
          Ustrcpy(CS malware_name_buffer, CS pre_malware_nb);
602
 
        else {
603
 
          /* concatenate each new virus name to previous */
604
 
          int slen = Ustrlen(malware_name_buffer);
605
 
          if (slen < (slen+Ustrlen(pre_malware_nb))) {
606
 
            Ustrcat(malware_name_buffer, "/");
607
 
            Ustrcat(malware_name_buffer, pre_malware_nb);
608
 
          }
609
 
        }
610
 
      }
611
 
    }
612
 
    else {
613
 
      const char *drweb_s = NULL;
614
 
 
615
 
      if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
616
 
      if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
617
 
      if (drweb_rc & DERR_TIMEOUT)  drweb_s = "timeout";
618
 
      if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
619
 
      /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
620
 
       * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
621
 
       * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
622
 
       * and others are ignored */
623
 
      if (drweb_s) {
624
 
        log_write(0, LOG_MAIN|LOG_PANIC,
625
 
          "malware acl condition: drweb: drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s);
626
 
        (void)close(sock);
627
 
        return DEFER;
628
 
      }
629
 
      /* no virus found */
630
 
      malware_name = NULL;
631
 
    };
632
 
    (void)close(sock);
633
 
  }
634
 
  /* ----------------------------------------------------------------------- */
635
 
    else if (strcmpic(scanner_name,US"aveserver") == 0) {
636
 
      uschar *kav_options;
637
 
      uschar kav_options_buffer[1024];
638
 
      uschar kav_options_default[] = "/var/run/aveserver";
639
 
      uschar buf[32768];
640
 
      struct sockaddr_un server;
641
 
      int sock;
642
 
      int result;
643
 
 
644
 
      if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
645
 
                                           kav_options_buffer,
646
 
                                           sizeof(kav_options_buffer))) == NULL) {
647
 
        /* no options supplied, use default options */
648
 
        kav_options = kav_options_default;
649
 
      };
650
 
 
651
 
      /* open the aveserver socket */
652
 
      sock = socket(AF_UNIX, SOCK_STREAM, 0);
653
 
      if (sock < 0) {
654
 
        log_write(0, LOG_MAIN|LOG_PANIC,
655
 
             "malware acl condition: can't open UNIX socket.");
656
 
        return DEFER;
657
 
      }
658
 
      server.sun_family = AF_UNIX;
659
 
      Ustrcpy(server.sun_path, kav_options);
660
 
      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
661
 
        (void)close(sock);
662
 
        log_write(0, LOG_MAIN|LOG_PANIC,
663
 
             "malware acl condition: unable to connect to aveserver UNIX socket (%s). errno=%d", kav_options, errno);
664
 
        return DEFER;
665
 
      }
666
 
 
667
 
      /* read aveserver's greeting and see if it is ready (2xx greeting) */
668
 
      recv_line(sock, buf, 32768);
669
 
 
670
 
      if (buf[0] != '2') {
671
 
        /* aveserver is having problems */
672
 
        (void)close(sock);
673
 
        log_write(0, LOG_MAIN|LOG_PANIC,
674
 
             "malware acl condition: aveserver is unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
675
 
        return DEFER;
676
 
      };
677
 
 
678
 
      /* prepare our command */
679
 
      (void)string_format(buf, 32768, "SCAN bPQRSTUW %s\r\n", eml_filename);
680
 
 
681
 
      DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name);
682
 
 
683
 
      /* and send it */
684
 
      if (send(sock, buf, Ustrlen(buf), 0) < 0) {
685
 
        (void)close(sock);
686
 
        log_write(0, LOG_MAIN|LOG_PANIC,
687
 
             "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
688
 
        return DEFER;
689
 
      }
690
 
 
691
 
      malware_name = NULL;
692
 
      result = 0;
693
 
      /* read response lines, find malware name and final response */
694
 
      while (recv_line(sock, buf, 32768) > 0) {
695
 
        debug_printf("aveserver: %s\n", buf);
696
 
        if (buf[0] == '2') {
697
 
    break;
698
 
  } else if (buf[0] == '5') {
699
 
          /* aveserver is having problems */
700
 
          log_write(0, LOG_MAIN|LOG_PANIC,
701
 
             "malware acl condition: unable to scan file %s (Responded: %s).",
702
 
       eml_filename, buf);
703
 
          result = DEFER;
704
 
    break;
705
 
  } else if (Ustrncmp(buf,"322",3) == 0) {
706
 
          uschar *p = Ustrchr(&buf[4],' ');
707
 
          *p = '\0';
708
 
          Ustrcpy(malware_name_buffer,&buf[4]);
709
 
          malware_name = malware_name_buffer;
710
 
  };
711
 
      }
712
 
 
713
 
      /* prepare our command */
714
 
      (void)string_format(buf, 32768, "quit\r\n");
715
 
 
716
 
      /* and send it */
717
 
      if (send(sock, buf, Ustrlen(buf), 0) < 0) {
718
 
        (void)close(sock);
719
 
        log_write(0, LOG_MAIN|LOG_PANIC,
720
 
             "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
721
 
        return DEFER;
722
 
      }
723
 
 
724
 
      /* read aveserver's greeting and see if it is ready (2xx greeting) */
725
 
      recv_line(sock, buf, 32768);
726
 
 
727
 
      if (buf[0] != '2') {
728
 
        /* aveserver is having problems */
729
 
        (void)close(sock);
730
 
        log_write(0, LOG_MAIN|LOG_PANIC,
731
 
             "malware acl condition: unable to quit aveserver dialogue (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
732
 
        return DEFER;
733
 
      };
734
 
 
735
 
      (void)close(sock);
736
 
 
737
 
      if (result == DEFER) return DEFER;
738
 
    }
739
 
    /* "fsecure" scanner type ------------------------------------------------- */
740
 
    else if (strcmpic(scanner_name,US"fsecure") == 0) {
741
 
      uschar *fsecure_options;
742
 
      uschar fsecure_options_buffer[1024];
743
 
      uschar fsecure_options_default[] = "/var/run/.fsav";
744
 
      struct sockaddr_un server;
745
 
      int sock, i, j, bread = 0;
746
 
      uschar file_name[1024];
747
 
      uschar av_buffer[1024];
748
 
      pcre *fs_inf;
749
 
      static uschar *cmdoptions[] = { US"CONFIGURE\tARCHIVE\t1\n",
750
 
                                      US"CONFIGURE\tTIMEOUT\t0\n",
751
 
                                      US"CONFIGURE\tMAXARCH\t5\n",
752
 
                                      US"CONFIGURE\tMIME\t1\n" };
753
 
 
754
 
      malware_name = NULL;
755
 
      if ((fsecure_options = string_nextinlist(&av_scanner_work, &sep,
756
 
                                               fsecure_options_buffer,
757
 
                                               sizeof(fsecure_options_buffer))) == NULL) {
758
 
         /* no options supplied, use default options */
759
 
         fsecure_options = fsecure_options_default;
760
 
      };
761
 
 
762
 
      /* open the fsecure socket */
763
 
      sock = socket(AF_UNIX, SOCK_STREAM, 0);
764
 
      if (sock < 0) {
765
 
        log_write(0, LOG_MAIN|LOG_PANIC,
766
 
                  "malware acl condition: unable to open fsecure socket %s (%s)",
767
 
                  fsecure_options, strerror(errno));
768
 
        return DEFER;
769
 
      }
770
 
      server.sun_family = AF_UNIX;
771
 
      Ustrcpy(server.sun_path, fsecure_options);
772
 
      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
773
 
        (void)close(sock);
774
 
        log_write(0, LOG_MAIN|LOG_PANIC,
775
 
                  "malware acl condition: unable to connect to fsecure socket %s (%s)",
776
 
                  fsecure_options, strerror(errno));
777
 
        return DEFER;
778
 
      }
779
 
 
780
 
      DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
781
 
          scanner_name, fsecure_options);
782
 
 
783
 
      /* pass options */
784
 
      memset(av_buffer, 0, sizeof(av_buffer));
785
 
      for (i=0; i != 4; i++) {
786
 
        /* debug_printf("send option \"%s\"",cmdoptions[i]); */
787
 
        if (write(sock, cmdoptions[i], Ustrlen(cmdoptions[i])) < 0) {
788
 
          (void)close(sock);
789
 
          log_write(0, LOG_MAIN|LOG_PANIC,
790
 
                    "malware acl condition: unable to write fsecure option %d to %s (%s)",
791
 
                    i, fsecure_options, strerror(errno));
792
 
          return DEFER;
793
 
        };
794
 
 
795
 
        bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
796
 
        if (bread >0) av_buffer[bread]='\0';
797
 
        if (bread < 0) {
798
 
          (void)close(sock);
799
 
          log_write(0, LOG_MAIN|LOG_PANIC,
800
 
                    "malware acl condition: unable to read fsecure answer %d (%s)", i, strerror(errno));
801
 
          return DEFER;
802
 
        };
803
 
        for (j=0;j<bread;j++) if((av_buffer[j]=='\r')||(av_buffer[j]=='\n')) av_buffer[j] ='@';
804
 
        /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
805
 
        /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
806
 
      };
807
 
 
808
 
      /* pass the mailfile to fsecure */
809
 
      (void)string_format(file_name,1024,"SCAN\t%s\n", eml_filename);
810
 
      /* debug_printf("send scan %s",file_name); */
811
 
      if (write(sock, file_name, Ustrlen(file_name)) < 0) {
812
 
        (void)close(sock);
813
 
        log_write(0, LOG_MAIN|LOG_PANIC,
814
 
                  "malware acl condition: unable to write fsecure scan to %s (%s)",
815
 
                  fsecure_options, strerror(errno));
816
 
        return DEFER;
817
 
      };
818
 
 
819
 
      /* set up match */
820
 
      /* todo also SUSPICION\t */
821
 
      fs_inf = pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", PCRE_COPT, (const char **)&rerror, &roffset, NULL);
822
 
 
823
 
      /* read report, linewise */
824
 
      do {
825
 
        int ovector[30];
826
 
        i = 0;
827
 
        memset(av_buffer, 0, sizeof(av_buffer));
828
 
        do {
829
 
          bread=ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT);
830
 
          if (bread < 0) {
831
 
            (void)close(sock);
832
 
            log_write(0, LOG_MAIN|LOG_PANIC,
833
 
                      "malware acl condition: unable to read fsecure result (%s)", strerror(errno));
834
 
            return DEFER;
835
 
          };
836
 
          i++;
837
 
        }
838
 
        while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
839
 
        av_buffer[i-1] = '\0';
840
 
        /* debug_printf("got line \"%s\"\n",av_buffer); */
841
 
 
842
 
        /* Really search for virus again? */
843
 
        if (malware_name == NULL) {
844
 
          /* try matcher on the line, grab substring */
845
 
          i = pcre_exec(fs_inf, NULL, CS av_buffer, Ustrlen(av_buffer), 0, 0, ovector, 30);
846
 
          if (i >= 2) {
847
 
            /* Got it */
848
 
            pcre_copy_substring(CS av_buffer, ovector, i, 1, CS malware_name_buffer, 255);
849
 
            malware_name = malware_name_buffer;
850
 
          };
851
 
        };
852
 
      }
853
 
      while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
854
 
      (void)close(sock);
855
 
    }
856
 
    /* ----------------------------------------------------------------------- */
857
 
 
858
 
    /* "kavdaemon" scanner type ------------------------------------------------ */
859
 
    else if (strcmpic(scanner_name,US"kavdaemon") == 0) {
860
 
      uschar *kav_options;
861
 
      uschar kav_options_buffer[1024];
862
 
      uschar kav_options_default[] = "/var/run/AvpCtl";
863
 
      struct sockaddr_un server;
864
 
      int sock;
865
 
      time_t t;
866
 
      uschar tmpbuf[1024];
867
 
      uschar scanrequest[1024];
868
 
      uschar kav_match_string[128];
869
 
      int kav_rc;
870
 
      unsigned long kav_reportlen, bread;
871
 
      pcre *kav_re;
872
 
      uschar *p;
873
 
      int fits;
874
 
 
875
 
      if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
876
 
                                           kav_options_buffer,
877
 
                                           sizeof(kav_options_buffer))) == NULL) {
878
 
        /* no options supplied, use default options */
879
 
        kav_options = kav_options_default;
880
 
      };
881
 
 
882
 
      /* open the kavdaemon socket */
883
 
      sock = socket(AF_UNIX, SOCK_STREAM, 0);
884
 
      if (sock < 0) {
885
 
        log_write(0, LOG_MAIN|LOG_PANIC,
886
 
             "malware acl condition: can't open UNIX socket.");
887
 
        return DEFER;
888
 
      }
889
 
      server.sun_family = AF_UNIX;
890
 
      Ustrcpy(server.sun_path, kav_options);
891
 
      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
892
 
        (void)close(sock);
893
 
        log_write(0, LOG_MAIN|LOG_PANIC,
894
 
             "malware acl condition: unable to connect to kavdaemon UNIX socket (%s). errno=%d", kav_options, errno);
895
 
        return DEFER;
896
 
      }
897
 
 
898
 
      /* get current date and time, build scan request */
899
 
      time(&t);
900
 
      /* pdp note: before the eml_filename parameter, this scanned the
901
 
      directory; not finding documentation, so we'll strip off the directory.
902
 
      The side-effect is that the test framework scanning may end up in
903
 
      scanning more than was requested, but for the normal interface, this is
904
 
      fine. */
905
 
      strftime(CS tmpbuf, sizeof(tmpbuf), "<0>%d %b %H:%M:%S:%%s", localtime(&t));
906
 
      fits = string_format(scanrequest, 1024,CS tmpbuf, eml_filename);
907
 
      if (!fits) {
908
 
        (void)close(sock);
909
 
        log_write(0, LOG_MAIN|LOG_PANIC,
910
 
            "malware filename does not fit in buffer [malware_internal() kavdaemon]");
911
 
      }
912
 
      p = Ustrrchr(scanrequest, '/');
913
 
      if (p)
914
 
        *p = '\0';
915
 
 
916
 
      DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
917
 
          scanner_name, kav_options);
918
 
 
919
 
      /* send scan request */
920
 
      if (send(sock, scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
921
 
        (void)close(sock);
922
 
        log_write(0, LOG_MAIN|LOG_PANIC,
923
 
             "malware acl condition: unable to write to kavdaemon UNIX socket (%s)", kav_options);
924
 
        return DEFER;
925
 
      }
926
 
 
927
 
      /* wait for result */
928
 
      if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
929
 
        (void)close(sock);
930
 
        log_write(0, LOG_MAIN|LOG_PANIC,
931
 
             "malware acl condition: unable to read 2 bytes from kavdaemon socket.");
932
 
        return DEFER;
933
 
      }
934
 
 
935
 
      /* get errorcode from one nibble */
936
 
      if (test_byte_order() == LITTLE_MY_ENDIAN) {
937
 
        kav_rc = tmpbuf[0] & 0x0F;
938
 
      }
939
 
      else {
940
 
        kav_rc = tmpbuf[1] & 0x0F;
941
 
      };
942
 
 
943
 
      /* improper kavdaemon configuration */
944
 
      if ( (kav_rc == 5) || (kav_rc == 6) ) {
945
 
        (void)close(sock);
946
 
        log_write(0, LOG_MAIN|LOG_PANIC,
947
 
             "malware acl condition: please reconfigure kavdaemon to NOT disinfect or remove infected files.");
948
 
        return DEFER;
949
 
      };
950
 
 
951
 
      if (kav_rc == 1) {
952
 
        (void)close(sock);
953
 
        log_write(0, LOG_MAIN|LOG_PANIC,
954
 
             "malware acl condition: kavdaemon reported 'scanning not completed' (code 1).");
955
 
        return DEFER;
956
 
      };
957
 
 
958
 
      if (kav_rc == 7) {
959
 
        (void)close(sock);
960
 
        log_write(0, LOG_MAIN|LOG_PANIC,
961
 
             "malware acl condition: kavdaemon reported 'kavdaemon damaged' (code 7).");
962
 
        return DEFER;
963
 
      };
964
 
 
965
 
      /* code 8 is not handled, since it is ambigous. It appears mostly on
966
 
      bounces where part of a file has been cut off */
967
 
 
968
 
      /* "virus found" return codes (2-4) */
969
 
      if ((kav_rc > 1) && (kav_rc < 5)) {
970
 
        int report_flag = 0;
971
 
 
972
 
        /* setup default virus name */
973
 
        Ustrcpy(malware_name_buffer,"unknown");
974
 
        malware_name = malware_name_buffer;
975
 
 
976
 
        if (test_byte_order() == LITTLE_MY_ENDIAN) {
977
 
          report_flag = tmpbuf[1];
978
 
        }
979
 
        else {
980
 
          report_flag = tmpbuf[0];
981
 
        };
982
 
 
983
 
        /* read the report, if available */
984
 
        if( report_flag == 1 ) {
985
 
          /* read report size */
986
 
          if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
987
 
            (void)close(sock);
988
 
            log_write(0, LOG_MAIN|LOG_PANIC,
989
 
                  "malware acl condition: cannot read report size from kavdaemon");
990
 
            return DEFER;
991
 
          };
992
 
 
993
 
          /* it's possible that avp returns av_buffer[1] == 1 but the
994
 
          reportsize is 0 (!?) */
995
 
          if (kav_reportlen > 0) {
996
 
            /* set up match regex, depends on retcode */
997
 
            if( kav_rc == 3 )
998
 
              Ustrcpy(kav_match_string, "suspicion:\\s*(.+?)\\s*$");
999
 
            else
1000
 
              Ustrcpy(kav_match_string, "infected:\\s*(.+?)\\s*$");
1001
 
 
1002
 
            kav_re = pcre_compile( CS kav_match_string,
1003
 
                                   PCRE_COPT,
1004
 
                                   (const char **)&rerror,
1005
 
                                   &roffset,
1006
 
                                   NULL );
1007
 
 
1008
 
            /* read report, linewise */
1009
 
            while (kav_reportlen > 0) {
1010
 
              int result = 0;
1011
 
              int ovector[30];
1012
 
 
1013
 
              bread = 0;
1014
 
              while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
1015
 
                kav_reportlen--;
1016
 
                if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
1017
 
                bread++;
1018
 
              };
1019
 
              bread++;
1020
 
              tmpbuf[bread] = '\0';
1021
 
 
1022
 
              /* try matcher on the line, grab substring */
1023
 
              result = pcre_exec(kav_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
1024
 
              if (result >= 2) {
1025
 
                pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS malware_name_buffer, 255);
1026
 
                break;
1027
 
              };
1028
 
            };
1029
 
          };
1030
 
        };
1031
 
      }
1032
 
      else {
1033
 
        /* no virus found */
1034
 
        malware_name = NULL;
1035
 
      };
1036
 
 
1037
 
      (void)close(sock);
1038
 
    }
1039
 
    /* ----------------------------------------------------------------------- */
1040
 
 
1041
 
 
1042
 
    /* "cmdline" scanner type ------------------------------------------------ */
1043
 
    else if (strcmpic(scanner_name,US"cmdline") == 0) {
1044
 
      uschar *cmdline_scanner;
1045
 
      uschar cmdline_scanner_buffer[1024];
1046
 
      uschar *cmdline_trigger;
1047
 
      uschar cmdline_trigger_buffer[1024];
1048
 
      const pcre *cmdline_trigger_re;
1049
 
      uschar *cmdline_regex;
1050
 
      uschar cmdline_regex_buffer[1024];
1051
 
      const pcre *cmdline_regex_re;
1052
 
      uschar file_name[1024];
1053
 
      uschar commandline[1024];
1054
 
      void (*eximsigchld)(int);
1055
 
      void (*eximsigpipe)(int);
1056
 
      FILE *scanner_out = NULL;
1057
 
      FILE *scanner_record = NULL;
1058
 
      uschar linebuffer[32767];
1059
 
      int trigger = 0;
1060
 
      int result;
1061
 
      int ovector[30];
1062
 
      uschar *p;
1063
 
      BOOL fits;
1064
 
 
1065
 
      /* find scanner command line */
1066
 
      if ((cmdline_scanner = string_nextinlist(&av_scanner_work, &sep,
1067
 
                                          cmdline_scanner_buffer,
1068
 
                                          sizeof(cmdline_scanner_buffer))) == NULL) {
1069
 
        /* no command line supplied */
1070
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1071
 
             "malware acl condition: missing commandline specification for cmdline scanner type.");
1072
 
        return DEFER;
1073
 
      };
1074
 
 
1075
 
      /* find scanner output trigger */
1076
 
      if ((cmdline_trigger = string_nextinlist(&av_scanner_work, &sep,
1077
 
                                          cmdline_trigger_buffer,
1078
 
                                          sizeof(cmdline_trigger_buffer))) == NULL) {
1079
 
        /* no trigger regex supplied */
1080
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1081
 
             "malware acl condition: missing trigger specification for cmdline scanner type.");
1082
 
        return DEFER;
1083
 
      };
1084
 
 
1085
 
      /* precompile trigger regex */
1086
 
      cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1087
 
      if (cmdline_trigger_re == NULL) {
1088
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1089
 
                 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_trigger, rerror, roffset);
1090
 
        return DEFER;
1091
 
      };
1092
 
 
1093
 
      /* find scanner name regex */
1094
 
      if ((cmdline_regex = string_nextinlist(&av_scanner_work, &sep,
1095
 
                                             cmdline_regex_buffer,
1096
 
                                             sizeof(cmdline_regex_buffer))) == NULL) {
1097
 
        /* no name regex supplied */
1098
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1099
 
             "malware acl condition: missing virus name regex specification for cmdline scanner type.");
1100
 
        return DEFER;
1101
 
      };
1102
 
 
1103
 
      /* precompile name regex */
1104
 
      cmdline_regex_re = pcre_compile(CS cmdline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1105
 
      if (cmdline_regex_re == NULL) {
1106
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1107
 
                 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_regex, rerror, roffset);
1108
 
        return DEFER;
1109
 
      };
1110
 
 
1111
 
      /* prepare scanner call; despite the naming, file_name holds a directory
1112
 
      name which is documented as the value given to %s. */
1113
 
      if (Ustrlen(eml_filename) > sizeof(file_name) - 1)
1114
 
        {
1115
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1116
 
            "malware filename does not fit in buffer [malware_internal() cmdline]");
1117
 
        return DEFER;
1118
 
        }
1119
 
      Ustrcpy(file_name, eml_filename);
1120
 
      p = Ustrrchr(file_name, '/');
1121
 
      if (p)
1122
 
        *p = '\0';
1123
 
      fits = string_format(commandline, sizeof(commandline), CS cmdline_scanner, file_name);
1124
 
      if (!fits)
1125
 
        {
1126
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1127
 
            "cmdline scanner command-line does not fit in buffer");
1128
 
        return DEFER;
1129
 
        }
1130
 
 
1131
 
      /* redirect STDERR too */
1132
 
      if (Ustrlen(commandline) + 5 > sizeof(commandline))
1133
 
        {
1134
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1135
 
            "cmdline scanner command-line does not fit in buffer (STDERR redirect)");
1136
 
        return DEFER;
1137
 
        }
1138
 
      Ustrcat(commandline," 2>&1");
1139
 
 
1140
 
      DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
1141
 
 
1142
 
      /* store exims signal handlers */
1143
 
      eximsigchld = signal(SIGCHLD,SIG_DFL);
1144
 
      eximsigpipe = signal(SIGPIPE,SIG_DFL);
1145
 
 
1146
 
      scanner_out = popen(CS commandline,"r");
1147
 
      if (scanner_out == NULL) {
1148
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1149
 
                 "malware acl condition: calling cmdline scanner (%s) failed: %s.", commandline, strerror(errno));
1150
 
        signal(SIGCHLD,eximsigchld);
1151
 
        signal(SIGPIPE,eximsigpipe);
1152
 
        return DEFER;
1153
 
      };
1154
 
 
1155
 
      (void)string_format(file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
1156
 
      scanner_record = modefopen(file_name,"wb",SPOOL_MODE);
1157
 
 
1158
 
      if (scanner_record == NULL) {
1159
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1160
 
                 "malware acl condition: opening scanner output file (%s) failed: %s.", file_name, strerror(errno));
1161
 
        pclose(scanner_out);
1162
 
        signal(SIGCHLD,eximsigchld);
1163
 
        signal(SIGPIPE,eximsigpipe);
1164
 
        return DEFER;
1165
 
      };
1166
 
 
1167
 
      /* look for trigger while recording output */
1168
 
      while(fgets(CS linebuffer,32767,scanner_out) != NULL) {
1169
 
        if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
1170
 
          /* short write */
1171
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1172
 
                 "malware acl condition: short write on scanner output file (%s).", file_name);
1173
 
          pclose(scanner_out);
1174
 
          signal(SIGCHLD,eximsigchld);
1175
 
          signal(SIGPIPE,eximsigpipe);
1176
 
          return DEFER;
1177
 
        };
1178
 
        /* try trigger match */
1179
 
        if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
1180
 
          trigger = 1;
1181
 
      };
1182
 
 
1183
 
      (void)fclose(scanner_record);
1184
 
      pclose(scanner_out);
1185
 
      signal(SIGCHLD,eximsigchld);
1186
 
      signal(SIGPIPE,eximsigpipe);
1187
 
 
1188
 
      if (trigger) {
1189
 
        /* setup default virus name */
1190
 
        Ustrcpy(malware_name_buffer,"unknown");
1191
 
        malware_name = malware_name_buffer;
1192
 
 
1193
 
        /* re-open the scanner output file, look for name match */
1194
 
        scanner_record = fopen(CS file_name,"rb");
1195
 
        while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
1196
 
          /* try match */
1197
 
          result = pcre_exec(cmdline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
1198
 
          if (result >= 2) {
1199
 
            pcre_copy_substring(CS linebuffer, ovector, result, 1, CS malware_name_buffer, 255);
1200
 
          };
1201
 
        };
1202
 
        (void)fclose(scanner_record);
1203
 
      }
1204
 
      else {
1205
 
        /* no virus found */
1206
 
        malware_name = NULL;
1207
 
      };
1208
 
    }
1209
 
    /* ----------------------------------------------------------------------- */
1210
 
 
1211
 
 
1212
 
    /* "sophie" scanner type ------------------------------------------------- */
1213
 
    else if (strcmpic(scanner_name,US"sophie") == 0) {
1214
 
      uschar *sophie_options;
1215
 
      uschar sophie_options_buffer[1024];
1216
 
      uschar sophie_options_default[] = "/var/run/sophie";
1217
 
      int bread = 0;
1218
 
      struct sockaddr_un server;
1219
 
      int sock, len;
1220
 
      uschar *p;
1221
 
      uschar file_name[1024];
1222
 
      uschar av_buffer[1024];
1223
 
 
1224
 
      if ((sophie_options = string_nextinlist(&av_scanner_work, &sep,
1225
 
                                          sophie_options_buffer,
1226
 
                                          sizeof(sophie_options_buffer))) == NULL) {
1227
 
        /* no options supplied, use default options */
1228
 
        sophie_options = sophie_options_default;
1229
 
      }
1230
 
 
1231
 
      /* open the sophie socket */
1232
 
      sock = socket(AF_UNIX, SOCK_STREAM, 0);
1233
 
      if (sock < 0) {
1234
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1235
 
             "malware acl condition: can't open UNIX socket.");
1236
 
        return DEFER;
1237
 
      }
1238
 
      server.sun_family = AF_UNIX;
1239
 
      Ustrcpy(server.sun_path, sophie_options);
1240
 
      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1241
 
        (void)close(sock);
1242
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1243
 
             "malware acl condition: unable to connect to sophie UNIX socket (%s). errno=%d", sophie_options, errno);
1244
 
        return DEFER;
1245
 
      }
1246
 
 
1247
 
      /* pass the scan directory to sophie */
1248
 
      len = Ustrlen(eml_filename) + 1;
1249
 
      if (len > sizeof(file_name))
1250
 
        {
1251
 
        (void)close(sock);
1252
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1253
 
            "malware filename does not fit in buffer [malware_internal() sophie]");
1254
 
        return DEFER;
1255
 
        }
1256
 
      memcpy(file_name, eml_filename, len);
1257
 
      p = Ustrrchr(file_name, '/');
1258
 
      if (p)
1259
 
        *p = '\0';
1260
 
 
1261
 
      DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
1262
 
          scanner_name, sophie_options);
1263
 
 
1264
 
      if (  write(sock, file_name, Ustrlen(file_name)) < 0
1265
 
         || write(sock, "\n", 1) != 1
1266
 
         ) {
1267
 
        (void)close(sock);
1268
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1269
 
             "malware acl condition: unable to write to sophie UNIX socket (%s)", sophie_options);
1270
 
        return DEFER;
1271
 
      }
1272
 
 
1273
 
      /* wait for result */
1274
 
      memset(av_buffer, 0, sizeof(av_buffer));
1275
 
      if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0)) {
1276
 
        (void)close(sock);
1277
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1278
 
             "malware acl condition: unable to read from sophie UNIX socket (%s)", sophie_options);
1279
 
        return DEFER;
1280
 
      }
1281
 
 
1282
 
      (void)close(sock);
1283
 
 
1284
 
      /* infected ? */
1285
 
      if (av_buffer[0] == '1') {
1286
 
        if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
1287
 
        Ustrcpy(malware_name_buffer,&av_buffer[2]);
1288
 
        malware_name = malware_name_buffer;
1289
 
      }
1290
 
      else if (!strncmp(CS av_buffer, "-1", 2)) {
1291
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1292
 
             "malware acl condition: malware acl condition: sophie reported error");
1293
 
        return DEFER;
1294
 
      }
1295
 
      else {
1296
 
        /* all ok, no virus */
1297
 
        malware_name = NULL;
1298
 
      }
1299
 
    }
1300
 
    /* ----------------------------------------------------------------------- */
1301
 
 
1302
 
 
1303
 
    /* "clamd" scanner type ------------------------------------------------- */
1304
 
    /* This code was originally contributed by David Saez */
1305
 
    /* There are three scanning methods available to us:
1306
 
     *  (1) Use the SCAN command, pointing to a file in the filesystem
1307
 
     *  (2) Use the STREAM command, send the data on a separate port
1308
 
     *  (3) Use the zINSTREAM command, send the data inline
1309
 
     * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1310
 
     * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1311
 
     * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1312
 
     * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1313
 
     * WITH_OLD_CLAMAV_STREAM is defined.
1314
 
     * See Exim bug 926 for details.  */
1315
 
    else if (strcmpic(scanner_name,US"clamd") == 0) {
1316
 
      uschar *clamd_options = NULL;
1317
 
      uschar clamd_options_buffer[1024];
1318
 
      uschar clamd_options_default[] = "/tmp/clamd";
1319
 
      uschar *p, *vname, *result_tag, *response_end;
1320
 
      struct sockaddr_un server;
1321
 
      int sock,bread=0;
1322
 
      unsigned int port;
1323
 
      uschar file_name[1024];
1324
 
      uschar av_buffer[1024];
1325
 
      uschar *hostname = "";
1326
 
      struct hostent *he;
1327
 
      struct in_addr in;
1328
 
      uschar *clamav_fbuf;
1329
 
      int clam_fd, result;
1330
 
      unsigned int fsize;
1331
 
      BOOL use_scan_command = FALSE, fits;
1332
 
      clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS];
1333
 
      int current_server;
1334
 
      int num_servers = 0;
1335
 
#ifdef WITH_OLD_CLAMAV_STREAM
1336
 
      uschar av_buffer2[1024];
1337
 
      int sockData;
1338
 
#else
1339
 
      uint32_t send_size, send_final_zeroblock;
1340
 
#endif
1341
 
 
1342
 
      if ((clamd_options = string_nextinlist(&av_scanner_work, &sep,
1343
 
                                             clamd_options_buffer,
1344
 
                                             sizeof(clamd_options_buffer))) == NULL) {
1345
 
        /* no options supplied, use default options */
1346
 
        clamd_options = clamd_options_default;
1347
 
      }
1348
 
 
1349
 
      if (*clamd_options == '/')
1350
 
        /* Local file; so we def want to use_scan_command and don't want to try
1351
 
         * passing IP/port combinations */
1352
 
        use_scan_command = TRUE;
1353
 
      else {
1354
 
        uschar *address = clamd_options;
1355
 
        uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20];
1356
 
 
1357
 
        /* Go through the rest of the list of host/port and construct an array
1358
 
         * of servers to try. The first one is the bit we just passed from
1359
 
         * clamd_options so process that first and then scan the remainder of
1360
 
         * the address buffer */
1361
 
        do {
1362
 
          clamd_address_container *this_clamd;
1363
 
 
1364
 
          /* The 'local' option means use the SCAN command over the network
1365
 
           * socket (ie common file storage in use) */
1366
 
          if (strcmpic(address,US"local") == 0) {
1367
 
            use_scan_command = TRUE;
1368
 
            continue;
1369
 
          }
1370
 
 
1371
 
          /* XXX: If unsuccessful we should free this memory */
1372
 
          this_clamd =
1373
 
              (clamd_address_container *)store_get(sizeof(clamd_address_container));
1374
 
 
1375
 
          /* extract host and port part */
1376
 
          if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u", this_clamd->tcp_addr,
1377
 
                                            &(this_clamd->tcp_port)) != 2 ) {
1378
 
            log_write(0, LOG_MAIN|LOG_PANIC,
1379
 
                      "malware acl condition: clamd: invalid address '%s'", address);
1380
 
            continue;
1381
 
          }
1382
 
 
1383
 
          clamd_address_vector[num_servers] = this_clamd;
1384
 
          num_servers++;
1385
 
          if (num_servers >= MAX_CLAMD_SERVERS) {
1386
 
            log_write(0, LOG_MAIN|LOG_PANIC,
1387
 
                  "More than " MAX_CLAMD_SERVERS_S " clamd servers specified; "
1388
 
                  "only using the first " MAX_CLAMD_SERVERS_S );
1389
 
            break;
1390
 
          }
1391
 
        } while ((address = string_nextinlist(&av_scanner_work, &sep,
1392
 
                                        address_buffer,
1393
 
                                        sizeof(address_buffer))) != NULL);
1394
 
 
1395
 
        /* check if we have at least one server */
1396
 
        if (!num_servers) {
1397
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1398
 
             "malware acl condition: clamd: no useable clamd server addresses in malware configuration option.");
1399
 
          return DEFER;
1400
 
        }
1401
 
      }
1402
 
 
1403
 
      /* See the discussion of response formats below to see why we really don't
1404
 
      like colons in filenames when passing filenames to ClamAV. */
1405
 
      if (use_scan_command && Ustrchr(eml_filename, ':')) {
1406
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1407
 
            "malware acl condition: clamd: local/SCAN mode incompatible with" \
1408
 
            " : in path to email filename [%s]", eml_filename);
1409
 
        return DEFER;
1410
 
      }
1411
 
 
1412
 
      /* We have some network servers specified */
1413
 
      if (num_servers) {
1414
 
 
1415
 
        /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1416
 
         * only supports AF_INET, but we should probably be looking to the
1417
 
         * future and rewriting this to be protocol-independent anyway. */
1418
 
 
1419
 
        while ( num_servers > 0 ) {
1420
 
          /* Randomly pick a server to start with */
1421
 
          current_server = random_number( num_servers );
1422
 
 
1423
 
          debug_printf("trying server name %s, port %u\n",
1424
 
                       clamd_address_vector[current_server]->tcp_addr,
1425
 
                       clamd_address_vector[current_server]->tcp_port);
1426
 
 
1427
 
          /* Lookup the host. This is to ensure that we connect to the same IP
1428
 
           * on both connections (as one host could resolve to multiple ips) */
1429
 
          if((he = gethostbyname(CS clamd_address_vector[current_server]->tcp_addr))
1430
 
                          == 0) {
1431
 
            log_write(0, LOG_MAIN|LOG_PANIC,
1432
 
                    "malware acl condition: clamd: failed to lookup host '%s'",
1433
 
                    clamd_address_vector[current_server]->tcp_addr
1434
 
                    );
1435
 
            goto try_next_server;
1436
 
          }
1437
 
 
1438
 
          in = *(struct in_addr *) he->h_addr_list[0];
1439
 
 
1440
 
          /* Open the ClamAV Socket */
1441
 
          if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1442
 
            log_write(0, LOG_MAIN|LOG_PANIC,
1443
 
                      "malware acl condition: clamd: unable to acquire socket (%s)",
1444
 
                      strerror(errno));
1445
 
            goto try_next_server;
1446
 
          }
1447
 
 
1448
 
          if (ip_connect( sock,
1449
 
                          AF_INET,
1450
 
                          (uschar*)inet_ntoa(in),
1451
 
                          clamd_address_vector[current_server]->tcp_port,
1452
 
                          5 ) > -1) {
1453
 
            /* Connection successfully established with a server */
1454
 
            hostname = clamd_address_vector[current_server]->tcp_addr;
1455
 
            break;
1456
 
          } else {
1457
 
            log_write(0, LOG_MAIN|LOG_PANIC,
1458
 
               "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1459
 
               clamd_address_vector[current_server]->tcp_addr,
1460
 
               clamd_address_vector[current_server]->tcp_port,
1461
 
               strerror(errno));
1462
 
 
1463
 
            (void)close(sock);
1464
 
          }
1465
 
 
1466
 
try_next_server:
1467
 
          /* Remove the server from the list. XXX We should free the memory */
1468
 
          num_servers--;
1469
 
          int i;
1470
 
          for( i = current_server; i < num_servers; i++ )
1471
 
            clamd_address_vector[i] = clamd_address_vector[i+1];
1472
 
        }
1473
 
 
1474
 
        if ( num_servers == 0 ) {
1475
 
          log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: all clamd servers failed");
1476
 
            return DEFER;
1477
 
        }
1478
 
      } else {
1479
 
        /* open the local socket */
1480
 
        if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
1481
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1482
 
                    "malware acl condition: clamd: unable to acquire socket (%s)",
1483
 
                    strerror(errno));
1484
 
          return DEFER;
1485
 
        }
1486
 
 
1487
 
        server.sun_family = AF_UNIX;
1488
 
        Ustrcpy(server.sun_path, clamd_options);
1489
 
 
1490
 
        if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1491
 
          (void)close(sock);
1492
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1493
 
                    "malware acl condition: clamd: unable to connect to UNIX socket %s (%s)",
1494
 
                    clamd_options, strerror(errno) );
1495
 
          return DEFER;
1496
 
        }
1497
 
      }
1498
 
 
1499
 
      /* have socket in variable "sock"; command to use is semi-independent of
1500
 
       * the socket protocol.  We use SCAN if is local (either Unix/local
1501
 
       * domain socket, or explicitly told local) else we stream the data.
1502
 
       * How we stream the data depends upon how we were built.  */
1503
 
 
1504
 
      if (!use_scan_command) {
1505
 
 
1506
 
#ifdef WITH_OLD_CLAMAV_STREAM
1507
 
        /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1508
 
         * that port on a second connection; then in the scan-method-neutral
1509
 
         * part, read the response back on the original connection. */
1510
 
 
1511
 
        DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1512
 
            scanner_name);
1513
 
 
1514
 
        /* Pass the string to ClamAV (7 = "STREAM\n") */
1515
 
        if (send(sock, "STREAM\n", 7, 0) < 0) {
1516
 
          log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1517
 
                strerror(errno));
1518
 
          (void)close(sock);
1519
 
          return DEFER;
1520
 
        }
1521
 
        memset(av_buffer2, 0, sizeof(av_buffer2));
1522
 
        bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
1523
 
 
1524
 
        if (bread < 0) {
1525
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1526
 
                "malware acl condition: clamd: unable to read PORT from socket (%s)",
1527
 
                strerror(errno));
1528
 
          (void)close(sock);
1529
 
          return DEFER;
1530
 
        }
1531
 
 
1532
 
        if (bread == sizeof(av_buffer)) {
1533
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1534
 
                "malware acl condition: clamd: buffer too small");
1535
 
          (void)close(sock);
1536
 
          return DEFER;
1537
 
        }
1538
 
 
1539
 
        if (!(*av_buffer2)) {
1540
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1541
 
                "malware acl condition: clamd: ClamAV returned null");
1542
 
          (void)close(sock);
1543
 
          return DEFER;
1544
 
        }
1545
 
 
1546
 
        av_buffer2[bread] = '\0';
1547
 
        if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) {
1548
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1549
 
                  "malware acl condition: clamd: Expected port information from clamd, got '%s'", av_buffer2);
1550
 
          (void)close(sock);
1551
 
          return DEFER;
1552
 
        };
1553
 
 
1554
 
        if ( (sockData = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1555
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1556
 
                  "malware acl condition: clamd: unable to acquire socket (%s)",
1557
 
                  strerror(errno));
1558
 
          (void)close(sock);
1559
 
          return DEFER;
1560
 
        }
1561
 
 
1562
 
        if (ip_connect(sockData, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1563
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1564
 
                  "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1565
 
                  inet_ntoa(in), port, strerror(errno));
1566
 
          (void)close(sockData); (void)close(sock);
1567
 
          return DEFER;
1568
 
        }
1569
 
 
1570
 
#define CLOSE_SOCKDATA (void)close(sockData)
1571
 
#else /* WITH_OLD_CLAMAV_STREAM not defined */
1572
 
        /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1573
 
        chunks, <n> a 4-byte number (network order), terminated by a zero-length
1574
 
        chunk. */
1575
 
 
1576
 
        DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1577
 
            scanner_name);
1578
 
 
1579
 
        /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1580
 
        if (send(sock, "zINSTREAM", 10, 0) < 0) {
1581
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1582
 
              "malware acl condition: clamd: unable to send zINSTREAM to socket (%s)",
1583
 
              strerror(errno));
1584
 
          (void)close(sock);
1585
 
          return DEFER;
1586
 
        }
1587
 
 
1588
 
#define CLOSE_SOCKDATA /**/
1589
 
#endif
1590
 
 
1591
 
        /* calc file size */
1592
 
        clam_fd = open(CS eml_filename, O_RDONLY);
1593
 
        if (clam_fd == -1) {
1594
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1595
 
            "malware acl condition: clamd: can't open spool file %s: %s",
1596
 
            eml_filename, strerror(errno));
1597
 
          CLOSE_SOCKDATA; (void)close(sock);
1598
 
          return DEFER;
1599
 
        }
1600
 
        fsize = lseek(clam_fd, 0, SEEK_END);
1601
 
        if (fsize == -1) {
1602
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1603
 
            "malware acl condition: clamd: can't seek spool file %s: %s",
1604
 
            eml_filename, strerror(errno));
1605
 
          CLOSE_SOCKDATA; (void)close(sock);
1606
 
          return DEFER;
1607
 
        }
1608
 
        lseek(clam_fd, 0, SEEK_SET);
1609
 
 
1610
 
        clamav_fbuf = (uschar *) malloc (fsize);
1611
 
        if (!clamav_fbuf) {
1612
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1613
 
            "malware acl condition: clamd: unable to allocate memory %u for file (%s)",
1614
 
            fsize, eml_filename);
1615
 
          CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1616
 
          return DEFER;
1617
 
        }
1618
 
 
1619
 
        result = read (clam_fd, clamav_fbuf, fsize);
1620
 
        if (result == -1) {
1621
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1622
 
            "malware acl condition: clamd: can't read spool file %s: %s",
1623
 
            eml_filename, strerror(errno));
1624
 
          CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1625
 
          free(clamav_fbuf);
1626
 
          return DEFER;
1627
 
        }
1628
 
        (void)close(clam_fd);
1629
 
 
1630
 
        /* send file body to socket */
1631
 
#ifdef WITH_OLD_CLAMAV_STREAM
1632
 
        if (send(sockData, clamav_fbuf, fsize, 0) < 0) {
1633
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1634
 
            "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
1635
 
          CLOSE_SOCKDATA; (void)close(sock);
1636
 
          free(clamav_fbuf);
1637
 
          return DEFER;
1638
 
        }
1639
 
#else
1640
 
        send_size = htonl(fsize);
1641
 
        send_final_zeroblock = 0;
1642
 
        if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1643
 
            (send(sock, clamav_fbuf, fsize, 0) < 0) ||
1644
 
            (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1645
 
          {
1646
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1647
 
            "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
1648
 
          (void)close(sock);
1649
 
          free(clamav_fbuf);
1650
 
          return DEFER;
1651
 
          }
1652
 
#endif
1653
 
 
1654
 
        free(clamav_fbuf);
1655
 
 
1656
 
        CLOSE_SOCKDATA;
1657
 
#undef CLOSE_SOCKDATA
1658
 
 
1659
 
      } else { /* use scan command */
1660
 
        /* Send a SCAN command pointing to a filename; then in the then in the
1661
 
         * scan-method-neutral part, read the response back */
1662
 
 
1663
 
/* ================================================================= */
1664
 
 
1665
 
        /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1666
 
        which dates to when ClamAV needed us to break apart the email into the
1667
 
        MIME parts (eg, with the now deprecated demime condition coming first).
1668
 
        Some time back, ClamAV gained the ability to deconstruct the emails, so
1669
 
        doing this would actually have resulted in the mail attachments being
1670
 
        scanned twice, in the broken out files and from the original .eml.
1671
 
        Since ClamAV now handles emails (and has for quite some time) we can
1672
 
        just use the email file itself. */
1673
 
        /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1674
 
        fits = string_format(file_name, sizeof(file_name), "SCAN %s\n",
1675
 
            eml_filename);
1676
 
        if (!fits) {
1677
 
          (void)close(sock);
1678
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1679
 
              "malware filename does not fit in buffer [malware_internal() clamd]");
1680
 
        }
1681
 
 
1682
 
        DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1683
 
            scanner_name, clamd_options);
1684
 
 
1685
 
        if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
1686
 
          (void)close(sock);
1687
 
          log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1688
 
                    strerror(errno));
1689
 
          return DEFER;
1690
 
        }
1691
 
 
1692
 
        /* Do not shut down the socket for writing; a user report noted that
1693
 
         * clamd 0.70 does not react well to this. */
1694
 
      }
1695
 
      /* Commands have been sent, no matter which scan method or connection
1696
 
       * type we're using; now just read the result, independent of method. */
1697
 
 
1698
 
      /* Read the result */
1699
 
      memset(av_buffer, 0, sizeof(av_buffer));
1700
 
      bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
1701
 
      (void)close(sock);
1702
 
 
1703
 
      if (!(bread  > 0)) {
1704
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1705
 
                  "malware acl condition: clamd: unable to read from socket (%s)",
1706
 
                  strerror(errno));
1707
 
        return DEFER;
1708
 
      }
1709
 
 
1710
 
      if (bread == sizeof(av_buffer)) {
1711
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1712
 
                  "malware acl condition: clamd: buffer too small");
1713
 
        return DEFER;
1714
 
      }
1715
 
 
1716
 
      /* Check the result. ClamAV returns one of two result formats.
1717
 
      In the basic mode, the response is of the form:
1718
 
        infected: -> "<filename>: <virusname> FOUND"
1719
 
        not-infected: -> "<filename>: OK"
1720
 
        error: -> "<filename>: <errcode> ERROR
1721
 
      If the ExtendedDetectionInfo option has been turned on, then we get:
1722
 
        "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1723
 
      for the infected case.  Compare:
1724
 
/tmp/eicar.com: Eicar-Test-Signature FOUND
1725
 
/tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1726
 
 
1727
 
      In the streaming case, clamd uses the filename "stream" which you should
1728
 
      be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }.  (The
1729
 
      client app will replace "stream" with the original filename before returning
1730
 
      results to stdout, but the trace shows the data).
1731
 
 
1732
 
      We will assume that the pathname passed to clamd from Exim does not contain
1733
 
      a colon.  We will have whined loudly above if the eml_filename does (and we're
1734
 
      passing a filename to clamd). */
1735
 
 
1736
 
      if (!(*av_buffer)) {
1737
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1738
 
                  "malware acl condition: clamd: ClamAV returned null");
1739
 
        return DEFER;
1740
 
      }
1741
 
 
1742
 
      /* strip newline at the end (won't be present for zINSTREAM)
1743
 
      (also any trailing whitespace, which shouldn't exist, but we depend upon
1744
 
      this below, so double-check) */
1745
 
      p = av_buffer + Ustrlen(av_buffer) - 1;
1746
 
      if (*p == '\n') *p = '\0';
1747
 
 
1748
 
      DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1749
 
 
1750
 
      while (isspace(*--p) && (p > av_buffer))
1751
 
        *p = '\0';
1752
 
      if (*p) ++p;
1753
 
      response_end = p;
1754
 
 
1755
 
      /* colon in returned output? */
1756
 
      if((p = Ustrchr(av_buffer,':')) == NULL) {
1757
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1758
 
                  "malware acl condition: clamd: ClamAV returned malformed result (missing colon): %s",
1759
 
                  av_buffer);
1760
 
        return DEFER;
1761
 
      }
1762
 
 
1763
 
      /* strip filename */
1764
 
      while (*p && isspace(*++p)) /**/;
1765
 
      vname = p;
1766
 
 
1767
 
      /* It would be bad to encounter a virus with "FOUND" in part of the name,
1768
 
      but we should at least be resistant to it. */
1769
 
      p = Ustrrchr(vname, ' ');
1770
 
      if (p)
1771
 
        result_tag = p + 1;
1772
 
      else
1773
 
        result_tag = vname;
1774
 
 
1775
 
      if (Ustrcmp(result_tag, "FOUND") == 0) {
1776
 
        /* p should still be the whitespace before the result_tag */
1777
 
        while (isspace(*p)) --p;
1778
 
        *++p = '\0';
1779
 
        /* Strip off the extended information too, which will be in parens
1780
 
        after the virus name, with no intervening whitespace. */
1781
 
        if (*--p == ')') {
1782
 
          /* "(hash:size)", so previous '(' will do; if not found, we have
1783
 
          a curious virus name, but not an error. */
1784
 
          p = Ustrrchr(vname, '(');
1785
 
          if (p)
 
386
    if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
 
387
      return malware_errlog_defer(US"av_scanner configuration variable is empty");
 
388
 
 
389
    for (scanent = m_scans; ; scanent++) {
 
390
      if (!scanent->name)
 
391
        return malware_errlog_defer(string_sprintf("unknown scanner type '%s'",
 
392
          scanner_name));
 
393
      if (strcmpic(scanner_name, US scanent->name) != 0)
 
394
        continue;
 
395
      if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
 
396
        scanner_options = scanent->options_default;
 
397
      if (scanent->conn == MC_NONE)
 
398
        break;
 
399
      switch(scanent->conn)
 
400
      {
 
401
      case MC_TCP:  sock = m_tcpsocket_fromdef(scanner_options, &errstr); break;
 
402
      case MC_UNIX: sock = m_unixsocket(scanner_options, &errstr);        break;
 
403
      case MC_STRM: sock = m_streamsocket(scanner_options, &errstr);      break;
 
404
      default: /* compiler quietening */ break;
 
405
      }
 
406
      if (sock < 0)
 
407
        return m_errlog_defer(scanent, errstr);
 
408
      break;
 
409
    }
 
410
    DEBUG(D_lookup) debug_printf("Malware scan: %s\n", scanner_name);
 
411
 
 
412
    switch (scanent->scancode) {
 
413
    case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
 
414
      {
 
415
        uschar *fp_scan_option;
 
416
        unsigned int detected=0, par_count=0;
 
417
        uschar * scanrequest;
 
418
        uschar buf[32768], *strhelper, *strhelper2;
 
419
        uschar * malware_name_internal = NULL;
 
420
 
 
421
        DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
 
422
        scanrequest = string_sprintf("GET %s", eml_filename);
 
423
 
 
424
        while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
 
425
                              NULL, 0))) {
 
426
          scanrequest = string_sprintf("%s%s%s", scanrequest,
 
427
                                    par_count ? "%20" : "?", fp_scan_option);
 
428
          par_count++;
 
429
        }
 
430
        scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
 
431
 
 
432
        /* send scan request */
 
433
        if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
 
434
          return m_errlog_defer(scanent, errstr);
 
435
 
 
436
        /* We get a lot of empty lines, so we need this hack to check for any data at all */
 
437
        while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
 
438
          if ( recv_line(sock, buf, sizeof(buf)) > 0) {
 
439
            if ( Ustrstr(buf, US"<detected type=\"") != NULL )
 
440
              detected = 1;
 
441
            else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
 
442
              if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
 
443
                *strhelper2 = '\0';
 
444
                malware_name_internal = string_copy(strhelper+6);
 
445
              }
 
446
            } else if ( Ustrstr(buf, US"<summary code=\"") )
 
447
                malware_name = Ustrstr(buf, US"<summary code=\"11\">")
 
448
                  ? malware_name_internal : NULL;
 
449
          }
 
450
        }
 
451
        break;
 
452
      } /* f-protd */
 
453
 
 
454
    case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
 
455
    /* v0.1 - added support for tcp sockets          */
 
456
    /* v0.0 - initial release -- support for unix sockets      */
 
457
      {
 
458
        int result;
 
459
        off_t fsize;
 
460
        unsigned int fsize_uint;
 
461
        uschar * tmpbuf, *drweb_fbuf;
 
462
        int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
 
463
            drweb_vnum, drweb_slen, drweb_fin = 0x0000;
 
464
        unsigned long bread;
 
465
        const pcre *drweb_re;
 
466
 
 
467
        /* prepare variables */
 
468
        drweb_cmd = htonl(DRWEBD_SCAN_CMD);
 
469
        drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
 
470
 
 
471
        if (*scanner_options != '/') {
 
472
 
 
473
          /* calc file size */
 
474
          if ((drweb_fd = open(CS eml_filename, O_RDONLY)) == -1)
 
475
            return m_errlog_defer_3(scanent,
 
476
              string_sprintf("can't open spool file %s: %s",
 
477
                eml_filename, strerror(errno)),
 
478
              sock);
 
479
 
 
480
          if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1) {
 
481
            int err = errno;
 
482
            (void)close(drweb_fd);
 
483
            return m_errlog_defer_3(scanent,
 
484
              string_sprintf("can't seek spool file %s: %s",
 
485
                eml_filename, strerror(err)),
 
486
              sock);
 
487
          }
 
488
          fsize_uint = (unsigned int) fsize;
 
489
          if ((off_t)fsize_uint != fsize) {
 
490
            (void)close(drweb_fd);
 
491
            return m_errlog_defer_3(scanent,
 
492
              string_sprintf("seeking spool file %s, size overflow",
 
493
                eml_filename),
 
494
              sock);
 
495
          }
 
496
          drweb_slen = htonl(fsize);
 
497
          lseek(drweb_fd, 0, SEEK_SET);
 
498
 
 
499
          DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s]\n",
 
500
              scanner_name, scanner_options);
 
501
 
 
502
          /* send scan request */
 
503
          if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
 
504
              (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
 
505
              (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
 
506
              (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
 
507
            (void)close(drweb_fd);
 
508
            return m_errlog_defer_3(scanent,
 
509
              string_sprintf("unable to send commands to socket (%s)", scanner_options),
 
510
              sock);
 
511
          }
 
512
 
 
513
          if (!(drweb_fbuf = (uschar *) malloc (fsize_uint))) {
 
514
            (void)close(drweb_fd);
 
515
            return m_errlog_defer_3(scanent,
 
516
              string_sprintf("unable to allocate memory %u for file (%s)",
 
517
                fsize_uint, eml_filename),
 
518
              sock);
 
519
          }
 
520
 
 
521
          if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1) {
 
522
            int err = errno;
 
523
            (void)close(drweb_fd);
 
524
            free(drweb_fbuf);
 
525
            return m_errlog_defer_3(scanent,
 
526
              string_sprintf("can't read spool file %s: %s",
 
527
                eml_filename, strerror(err)),
 
528
              sock);
 
529
          }
 
530
          (void)close(drweb_fd);
 
531
 
 
532
          /* send file body to socket */
 
533
          if (send(sock, drweb_fbuf, fsize, 0) < 0) {
 
534
            free(drweb_fbuf);
 
535
            return m_errlog_defer_3(scanent,
 
536
              string_sprintf("unable to send file body to socket (%s)", scanner_options),
 
537
            sock);
 
538
          }
 
539
          (void)close(drweb_fd);
 
540
 
 
541
        } else {
 
542
 
 
543
          drweb_slen = htonl(Ustrlen(eml_filename));
 
544
 
 
545
          DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
 
546
              scanner_name, scanner_options);
 
547
 
 
548
          /* send scan request */
 
549
          if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
 
550
              (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
 
551
              (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
 
552
              (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
 
553
              (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0))
 
554
            return m_errlog_defer_3(scanent,
 
555
              string_sprintf("unable to send commands to socket (%s)", scanner_options),
 
556
              sock);
 
557
        }
 
558
 
 
559
        /* wait for result */
 
560
        if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc)))
 
561
          return m_errlog_defer_3(scanent,
 
562
                      US"unable to read return code", sock);
 
563
        drweb_rc = ntohl(drweb_rc);
 
564
 
 
565
        if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum)))
 
566
          return m_errlog_defer_3(scanent,
 
567
                              US"unable to read the number of viruses", sock);
 
568
        drweb_vnum = ntohl(drweb_vnum);
 
569
 
 
570
        /* "virus(es) found" if virus number is > 0 */
 
571
        if (drweb_vnum) {
 
572
          int i;
 
573
 
 
574
          /* setup default virus name */
 
575
          malware_name = US"unknown";
 
576
 
 
577
          /* set up match regex */
 
578
          drweb_re = m_pcre_compile(US"infected\\swith\\s*(.+?)$", &errstr);
 
579
 
 
580
          /* read and concatenate virus names into one string */
 
581
          for (i=0;i<drweb_vnum;i++)
 
582
          {
 
583
            int size = 0, off = 0, ovector[10*3];
 
584
            /* read the size of report */
 
585
            if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen)))
 
586
              return m_errlog_defer_3(scanent,
 
587
                                US"cannot read report size", sock);
 
588
            drweb_slen = ntohl(drweb_slen);
 
589
            tmpbuf = store_get(drweb_slen);
 
590
 
 
591
            /* read report body */
 
592
            if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen)
 
593
              return m_errlog_defer_3(scanent,
 
594
                                US"cannot read report string", sock);
 
595
            tmpbuf[drweb_slen] = '\0';
 
596
 
 
597
            /* try matcher on the line, grab substring */
 
598
            result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
 
599
                                    ovector, nelements(ovector));
 
600
            if (result >= 2) {
 
601
              const char * pre_malware_nb;
 
602
 
 
603
              pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
 
604
 
 
605
              if (i==0) /* the first name we just copy to malware_name */
 
606
                malware_name = string_append(NULL, &size, &off,
 
607
                                            1, pre_malware_nb);
 
608
 
 
609
              else      /* concatenate each new virus name to previous */
 
610
                malware_name = string_append(malware_name, &size, &off,
 
611
                                            2, "/", pre_malware_nb);
 
612
 
 
613
              pcre_free_substring(pre_malware_nb);
 
614
            }
 
615
          }
 
616
        }
 
617
        else {
 
618
          const char *drweb_s = NULL;
 
619
 
 
620
          if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
 
621
          if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
 
622
          if (drweb_rc & DERR_TIMEOUT)  drweb_s = "timeout";
 
623
          if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
 
624
          /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
 
625
           * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
 
626
           * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
 
627
           * and others are ignored */
 
628
          if (drweb_s)
 
629
            return m_errlog_defer_3(scanent,
 
630
              string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
 
631
              sock);
 
632
 
 
633
          /* no virus found */
 
634
          malware_name = NULL;
 
635
        }
 
636
        break;
 
637
      } /* drweb */
 
638
 
 
639
    case M_AVES: /* "aveserver" scanner type -------------------------------- */
 
640
      {
 
641
        uschar buf[32768];
 
642
        int result;
 
643
 
 
644
        /* read aveserver's greeting and see if it is ready (2xx greeting) */
 
645
        recv_line(sock, buf, sizeof(buf));
 
646
 
 
647
        if (buf[0] != '2')              /* aveserver is having problems */
 
648
          return m_errlog_defer_3(scanent,
 
649
            string_sprintf("unavailable (Responded: %s).",
 
650
                            ((buf[0] != 0) ? buf : (uschar *)"nothing") ),
 
651
            sock);
 
652
 
 
653
        /* prepare our command */
 
654
        (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
 
655
                                                  eml_filename);
 
656
 
 
657
        DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name);
 
658
 
 
659
        /* and send it */
 
660
        if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0)
 
661
          return m_errlog_defer(scanent, errstr);
 
662
 
 
663
        malware_name = NULL;
 
664
        result = 0;
 
665
        /* read response lines, find malware name and final response */
 
666
        while (recv_line(sock, buf, sizeof(buf)) > 0) {
 
667
          debug_printf("aveserver: %s\n", buf);
 
668
          if (buf[0] == '2')
 
669
            break;
 
670
          if (buf[0] == '5') {          /* aveserver is having problems */
 
671
            result = m_errlog_defer(scanent,
 
672
               string_sprintf("unable to scan file %s (Responded: %s).",
 
673
                               eml_filename, buf));
 
674
            break;
 
675
          } else if (Ustrncmp(buf,"322",3) == 0) {
 
676
            uschar *p = Ustrchr(&buf[4],' ');
1786
677
            *p = '\0';
1787
 
        }
1788
 
        Ustrncpy(malware_name_buffer, vname, sizeof(malware_name_buffer)-1);
1789
 
        malware_name = malware_name_buffer;
1790
 
        DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
1791
 
 
1792
 
      } else if (Ustrcmp(result_tag, "ERROR") == 0) {
1793
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1794
 
                  "malware acl condition: clamd: ClamAV returned: %s",
1795
 
                  av_buffer);
1796
 
        return DEFER;
1797
 
 
1798
 
      } else if (Ustrcmp(result_tag, "OK") == 0) {
1799
 
        /* Everything should be OK */
1800
 
        malware_name = NULL;
1801
 
        DEBUG(D_acl) debug_printf("Malware not found\n");
1802
 
 
1803
 
      } else {
1804
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1805
 
                  "malware acl condition: clamd: unparseable response from ClamAV: {%s}",
1806
 
                  av_buffer);
1807
 
        return DEFER;
1808
 
      }
1809
 
 
1810
 
    } /* clamd */
1811
 
 
1812
 
    /* ----------------------------------------------------------------------- */
1813
 
 
1814
 
 
1815
 
    /* "mksd" scanner type --------------------------------------------------- */
1816
 
    else if (strcmpic(scanner_name,US"mksd") == 0) {
1817
 
      uschar *mksd_options;
1818
 
      char *mksd_options_end;
1819
 
      uschar mksd_options_buffer[32];
1820
 
      int mksd_maxproc = 1;  /* default, if no option supplied */
1821
 
      struct sockaddr_un server;
1822
 
      int sock;
1823
 
      int retval;
1824
 
 
1825
 
      if ((mksd_options = string_nextinlist(&av_scanner_work, &sep,
1826
 
                                            mksd_options_buffer,
1827
 
                                            sizeof(mksd_options_buffer))) != NULL) {
1828
 
        mksd_maxproc = (int) strtol(CS mksd_options, &mksd_options_end, 10);
1829
 
        if ((*mksd_options == '\0') || (*mksd_options_end != '\0') ||
1830
 
      (mksd_maxproc < 1) || (mksd_maxproc > 32)) {
1831
 
          log_write(0, LOG_MAIN|LOG_PANIC,
1832
 
                    "malware acl condition: mksd: invalid option '%s'", mksd_options);
1833
 
          return DEFER;
1834
 
        }
1835
 
      }
1836
 
 
1837
 
      /* open the mksd socket */
1838
 
      sock = socket(AF_UNIX, SOCK_STREAM, 0);
1839
 
      if (sock < 0) {
1840
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1841
 
             "malware acl condition: can't open UNIX socket.");
1842
 
        return DEFER;
1843
 
      }
1844
 
      server.sun_family = AF_UNIX;
1845
 
      Ustrcpy(server.sun_path, "/var/run/mksd/socket");
1846
 
      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1847
 
        (void)close(sock);
1848
 
        log_write(0, LOG_MAIN|LOG_PANIC,
1849
 
             "malware acl condition: unable to connect to mksd UNIX socket (/var/run/mksd/socket). errno=%d", errno);
1850
 
        return DEFER;
1851
 
      }
1852
 
 
1853
 
      malware_name = NULL;
1854
 
 
1855
 
      DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
1856
 
 
1857
 
      retval = mksd_scan_packed(sock, eml_filename);
1858
 
 
1859
 
      if (retval != OK)
1860
 
        return retval;
 
678
            malware_name = string_copy(&buf[4]);
 
679
          }
 
680
        }
 
681
 
 
682
        /* and send it */
 
683
        if (m_sock_send(sock, US"quit\r\n", 6, &errstr) < 0)
 
684
          return m_errlog_defer(scanent, errstr);
 
685
 
 
686
        /* read aveserver's greeting and see if it is ready (2xx greeting) */
 
687
        recv_line(sock, buf, sizeof(buf));
 
688
 
 
689
        if (buf[0] != '2')              /* aveserver is having problems */
 
690
          return m_errlog_defer_3(scanent,
 
691
            string_sprintf("unable to quit dialogue (Responded: %s).",
 
692
                          ((buf[0] != 0) ? buf : (uschar *)"nothing") ),
 
693
            sock);
 
694
 
 
695
        if (result == DEFER) {
 
696
          (void)close(sock);
 
697
          return DEFER;
 
698
        }
 
699
        break;
 
700
      } /* aveserver */
 
701
 
 
702
    case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
 
703
      {
 
704
        int i, j, bread = 0;
 
705
        uschar * file_name;
 
706
        uschar av_buffer[1024];
 
707
        const pcre * fs_inf;
 
708
        static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n",
 
709
                                        US"CONFIGURE\tTIMEOUT\t0\n",
 
710
                                        US"CONFIGURE\tMAXARCH\t5\n",
 
711
                                        US"CONFIGURE\tMIME\t1\n" };
 
712
 
 
713
        malware_name = NULL;
 
714
 
 
715
        DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
 
716
            scanner_name, scanner_options);
 
717
 
 
718
        /* pass options */
 
719
        memset(av_buffer, 0, sizeof(av_buffer));
 
720
        for (i=0; i != nelements(cmdopt); i++) {
 
721
 
 
722
          if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
 
723
            return m_errlog_defer(scanent, errstr);
 
724
 
 
725
          bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
 
726
          if (bread >0) av_buffer[bread]='\0';
 
727
          if (bread < 0)
 
728
            return m_errlog_defer_3(scanent,
 
729
              string_sprintf("unable to read answer %d (%s)", i, strerror(errno)),
 
730
              sock);
 
731
          for (j=0;j<bread;j++)
 
732
            if((av_buffer[j]=='\r')||(av_buffer[j]=='\n'))
 
733
              av_buffer[j] ='@';
 
734
        }
 
735
 
 
736
        /* pass the mailfile to fsecure */
 
737
        file_name = string_sprintf("SCAN\t%s\n", eml_filename);
 
738
 
 
739
        if (m_sock_send(sock, file_name, Ustrlen(file_name), &errstr) < 0)
 
740
          return m_errlog_defer(scanent, errstr);
 
741
 
 
742
        /* set up match */
 
743
        /* todo also SUSPICION\t */
 
744
        fs_inf = m_pcre_compile(US"\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", &errstr);
 
745
 
 
746
        /* read report, linewise */
 
747
        do {
 
748
          i = 0;
 
749
          memset(av_buffer, 0, sizeof(av_buffer));
 
750
          do {
 
751
            if ((bread= ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT)) < 0)
 
752
              return m_errlog_defer_3(scanent,
 
753
                string_sprintf("unable to read result (%s)", strerror(errno)),
 
754
                sock);
 
755
          } while (++i < sizeof(av_buffer)-1  &&  av_buffer[i-1] != '\n');
 
756
          av_buffer[i-1] = '\0';
 
757
 
 
758
          /* Really search for virus again? */
 
759
          if (malware_name == NULL)
 
760
            /* try matcher on the line, grab substring */
 
761
            malware_name = m_pcre_exec(fs_inf, av_buffer);
 
762
        }
 
763
        while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
 
764
        break;
 
765
      } /* fsecure */
 
766
 
 
767
    case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
 
768
      {
 
769
        time_t t;
 
770
        uschar tmpbuf[1024];
 
771
        uschar * scanrequest;
 
772
        int kav_rc;
 
773
        unsigned long kav_reportlen, bread;
 
774
        const pcre *kav_re;
 
775
        uschar *p;
 
776
 
 
777
        /* get current date and time, build scan request */
 
778
        time(&t);
 
779
        /* pdp note: before the eml_filename parameter, this scanned the
 
780
        directory; not finding documentation, so we'll strip off the directory.
 
781
        The side-effect is that the test framework scanning may end up in
 
782
        scanning more than was requested, but for the normal interface, this is
 
783
        fine. */
 
784
 
 
785
        strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t));
 
786
        scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename);
 
787
        p = Ustrrchr(scanrequest, '/');
 
788
        if (p)
 
789
          *p = '\0';
 
790
 
 
791
        DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
 
792
            scanner_name, scanner_options);
 
793
 
 
794
        /* send scan request */
 
795
        if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
 
796
          return m_errlog_defer(scanent, errstr);
 
797
 
 
798
        /* wait for result */
 
799
        if ((bread = recv(sock, tmpbuf, 2, 0) != 2))
 
800
          return m_errlog_defer_3(scanent,
 
801
                              US"unable to read 2 bytes from socket.", sock);
 
802
 
 
803
        /* get errorcode from one nibble */
 
804
        kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
 
805
        switch(kav_rc)
 
806
        {
 
807
        case 5: case 6: /* improper kavdaemon configuration */
 
808
          return m_errlog_defer_3(scanent,
 
809
                  US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
 
810
                  sock);
 
811
        case 1:
 
812
          return m_errlog_defer_3(scanent,
 
813
                  US"reported 'scanning not completed' (code 1).", sock);
 
814
        case 7:
 
815
          return m_errlog_defer_3(scanent,
 
816
                  US"reported 'kavdaemon damaged' (code 7).", sock);
 
817
        }
 
818
 
 
819
        /* code 8 is not handled, since it is ambigous. It appears mostly on
 
820
        bounces where part of a file has been cut off */
 
821
 
 
822
        /* "virus found" return codes (2-4) */
 
823
        if ((kav_rc > 1) && (kav_rc < 5)) {
 
824
          int report_flag = 0;
 
825
 
 
826
          /* setup default virus name */
 
827
          malware_name = US"unknown";
 
828
 
 
829
          report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
 
830
 
 
831
          /* read the report, if available */
 
832
          if( report_flag == 1 ) {
 
833
            /* read report size */
 
834
            if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4)
 
835
              return m_errlog_defer_3(scanent,
 
836
                    US"cannot read report size", sock);
 
837
 
 
838
            /* it's possible that avp returns av_buffer[1] == 1 but the
 
839
            reportsize is 0 (!?) */
 
840
            if (kav_reportlen > 0) {
 
841
              /* set up match regex, depends on retcode */
 
842
              kav_re = m_pcre_compile( kav_rc == 3
 
843
                                       ? US"suspicion:\\s*(.+?)\\s*$"
 
844
                                       : US"infected:\\s*(.+?)\\s*$",
 
845
                                       &errstr );
 
846
 
 
847
              /* read report, linewise */
 
848
              while (kav_reportlen > 0) {
 
849
                bread = 0;
 
850
                while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
 
851
                  kav_reportlen--;
 
852
                  if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
 
853
                  bread++;
 
854
                }
 
855
                bread++;
 
856
                tmpbuf[bread] = '\0';
 
857
 
 
858
                /* try matcher on the line, grab substring */
 
859
                if ((malware_name = m_pcre_exec(kav_re, tmpbuf)))
 
860
                  break;
 
861
              }
 
862
            }
 
863
          }
 
864
        }
 
865
        else /* no virus found */
 
866
          malware_name = NULL;
 
867
 
 
868
        break;
 
869
      }
 
870
 
 
871
    case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
 
872
      {
 
873
        const uschar *cmdline_scanner = scanner_options;
 
874
        const pcre *cmdline_trigger_re;
 
875
        const pcre *cmdline_regex_re;
 
876
        uschar * file_name;
 
877
        uschar * commandline;
 
878
        void (*eximsigchld)(int);
 
879
        void (*eximsigpipe)(int);
 
880
        FILE *scanner_out = NULL;
 
881
        FILE *scanner_record = NULL;
 
882
        uschar linebuffer[32767];
 
883
        int trigger = 0;
 
884
        uschar *p;
 
885
 
 
886
        if (!cmdline_scanner)
 
887
          return m_errlog_defer(scanent, errstr);
 
888
 
 
889
        /* find scanner output trigger */
 
890
        cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep,
 
891
                                  "missing trigger specification", &errstr);
 
892
        if (!cmdline_trigger_re)
 
893
          return m_errlog_defer(scanent, errstr);
 
894
 
 
895
        /* find scanner name regex */
 
896
        cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep,
 
897
                            "missing virus name regex specification", &errstr);
 
898
        if (!cmdline_regex_re)
 
899
          return m_errlog_defer(scanent, errstr);
 
900
 
 
901
        /* prepare scanner call; despite the naming, file_name holds a directory
 
902
        name which is documented as the value given to %s. */
 
903
 
 
904
        file_name = string_copy(eml_filename);
 
905
        p = Ustrrchr(file_name, '/');
 
906
        if (p)
 
907
          *p = '\0';
 
908
        commandline = string_sprintf(CS cmdline_scanner, file_name);
 
909
 
 
910
        /* redirect STDERR too */
 
911
        commandline = string_sprintf("%s 2>&1", commandline);
 
912
 
 
913
        DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
 
914
 
 
915
        /* store exims signal handlers */
 
916
        eximsigchld = signal(SIGCHLD,SIG_DFL);
 
917
        eximsigpipe = signal(SIGPIPE,SIG_DFL);
 
918
 
 
919
        if (!(scanner_out = popen(CS commandline,"r"))) {
 
920
          int err = errno;
 
921
          signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
 
922
          return m_errlog_defer(scanent,
 
923
            string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
 
924
        }
 
925
 
 
926
        file_name = string_sprintf("%s/scan/%s/%s_scanner_output",
 
927
                                  spool_directory, message_id, message_id);
 
928
        scanner_record = modefopen(file_name, "wb", SPOOL_MODE);
 
929
 
 
930
        if (scanner_record == NULL) {
 
931
          int err = errno;
 
932
          (void) pclose(scanner_out);
 
933
          signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
 
934
          return m_errlog_defer(scanent,
 
935
            string_sprintf("opening scanner output file (%s) failed: %s.",
 
936
              file_name, strerror(err)));
 
937
        }
 
938
 
 
939
        /* look for trigger while recording output */
 
940
        while(fgets(CS linebuffer, sizeof(linebuffer), scanner_out)) {
 
941
          if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
 
942
            /* short write */
 
943
            (void) pclose(scanner_out);
 
944
            signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
 
945
            return m_errlog_defer(scanent,
 
946
              string_sprintf("short write on scanner output file (%s).", file_name));
 
947
          }
 
948
          /* try trigger match */
 
949
          if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
 
950
            trigger = 1;
 
951
        }
 
952
 
 
953
        (void)fclose(scanner_record);
 
954
        sep = pclose(scanner_out);
 
955
        signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
 
956
        if (sep != 0)
 
957
            return m_errlog_defer(scanent,
 
958
                sep == -1
 
959
                ? string_sprintf("running scanner failed: %s", strerror(sep))
 
960
                : string_sprintf("scanner returned error code: %d", sep));
 
961
 
 
962
        if (trigger) {
 
963
          uschar * s;
 
964
          /* setup default virus name */
 
965
          malware_name = US"unknown";
 
966
 
 
967
          /* re-open the scanner output file, look for name match */
 
968
          scanner_record = fopen(CS file_name, "rb");
 
969
          while(fgets(CS linebuffer, sizeof(linebuffer), scanner_record)) {
 
970
            /* try match */
 
971
            if ((s = m_pcre_exec(cmdline_regex_re, linebuffer)))
 
972
              malware_name = s;
 
973
          }
 
974
          (void)fclose(scanner_record);
 
975
        }
 
976
        else /* no virus found */
 
977
          malware_name = NULL;
 
978
        break;
 
979
      } /* cmdline */
 
980
 
 
981
    case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
 
982
      {
 
983
        int bread = 0;
 
984
        uschar *p;
 
985
        uschar * file_name;
 
986
        uschar av_buffer[1024];
 
987
 
 
988
        /* pass the scan directory to sophie */
 
989
        file_name = string_copy(eml_filename);
 
990
        if ((p = Ustrrchr(file_name, '/')))
 
991
          *p = '\0';
 
992
 
 
993
        DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
 
994
            scanner_name, scanner_options);
 
995
 
 
996
        if (  write(sock, file_name, Ustrlen(file_name)) < 0
 
997
           || write(sock, "\n", 1) != 1
 
998
           )
 
999
          return m_errlog_defer_3(scanent,
 
1000
            string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
 
1001
            sock);
 
1002
 
 
1003
        /* wait for result */
 
1004
        memset(av_buffer, 0, sizeof(av_buffer));
 
1005
        if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0))
 
1006
          return m_errlog_defer_3(scanent,
 
1007
            string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
 
1008
            sock);
 
1009
 
 
1010
        /* infected ? */
 
1011
        if (av_buffer[0] == '1') {
 
1012
          uschar * s = Ustrchr(av_buffer, '\n');
 
1013
          if (s)
 
1014
            *s = '\0';
 
1015
          malware_name = string_copy(&av_buffer[2]);
 
1016
        }
 
1017
        else if (!strncmp(CS av_buffer, "-1", 2))
 
1018
          return m_errlog_defer_3(scanent, US"scanner reported error", sock);
 
1019
        else /* all ok, no virus */
 
1020
          malware_name = NULL;
 
1021
 
 
1022
        break;
 
1023
      }
 
1024
 
 
1025
    case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
 
1026
      {
 
1027
      /* This code was originally contributed by David Saez */
 
1028
      /* There are three scanning methods available to us:
 
1029
       *  (1) Use the SCAN command, pointing to a file in the filesystem
 
1030
       *  (2) Use the STREAM command, send the data on a separate port
 
1031
       *  (3) Use the zINSTREAM command, send the data inline
 
1032
       * The zINSTREAM command was introduced with ClamAV 0.95, which marked
 
1033
       * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
 
1034
       * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
 
1035
       * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
 
1036
       * WITH_OLD_CLAMAV_STREAM is defined.
 
1037
       * See Exim bug 926 for details.  */
 
1038
 
 
1039
        uschar *p, *vname, *result_tag, *response_end;
 
1040
        int bread=0;
 
1041
        uschar * file_name;
 
1042
        uschar av_buffer[1024];
 
1043
        uschar *hostname = US"";
 
1044
        host_item connhost;
 
1045
        uschar *clamav_fbuf;
 
1046
        int clam_fd, result;
 
1047
        off_t fsize;
 
1048
        unsigned int fsize_uint;
 
1049
        BOOL use_scan_command = FALSE;
 
1050
        clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS];
 
1051
        int current_server;
 
1052
        int num_servers = 0;
 
1053
  #ifdef WITH_OLD_CLAMAV_STREAM
 
1054
        unsigned int port;
 
1055
        uschar av_buffer2[1024];
 
1056
        int sockData;
 
1057
  #else
 
1058
        uint32_t send_size, send_final_zeroblock;
 
1059
  #endif
 
1060
 
 
1061
        if (*scanner_options == '/')
 
1062
          /* Local file; so we def want to use_scan_command and don't want to try
 
1063
           * passing IP/port combinations */
 
1064
          use_scan_command = TRUE;
 
1065
        else {
 
1066
          const uschar *address = scanner_options;
 
1067
          uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20];
 
1068
 
 
1069
          /* Go through the rest of the list of host/port and construct an array
 
1070
           * of servers to try. The first one is the bit we just passed from
 
1071
           * scanner_options so process that first and then scan the remainder of
 
1072
           * the address buffer */
 
1073
          do {
 
1074
            clamd_address_container *this_clamd;
 
1075
 
 
1076
            /* The 'local' option means use the SCAN command over the network
 
1077
             * socket (ie common file storage in use) */
 
1078
            if (strcmpic(address,US"local") == 0) {
 
1079
              use_scan_command = TRUE;
 
1080
              continue;
 
1081
            }
 
1082
 
 
1083
            /* XXX: If unsuccessful we should free this memory */
 
1084
            this_clamd =
 
1085
                (clamd_address_container *)store_get(sizeof(clamd_address_container));
 
1086
 
 
1087
            /* extract host and port part */
 
1088
            if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u",
 
1089
                   this_clamd->tcp_addr, &(this_clamd->tcp_port)) != 2 ) {
 
1090
              (void) m_errlog_defer(scanent,
 
1091
                          string_sprintf("invalid address '%s'", address));
 
1092
              continue;
 
1093
            }
 
1094
 
 
1095
            clamd_address_vector[num_servers] = this_clamd;
 
1096
            num_servers++;
 
1097
            if (num_servers >= MAX_CLAMD_SERVERS) {
 
1098
              (void) m_errlog_defer(scanent,
 
1099
                    US"More than " MAX_CLAMD_SERVERS_S " clamd servers "
 
1100
                    "specified; only using the first " MAX_CLAMD_SERVERS_S );
 
1101
              break;
 
1102
            }
 
1103
          } while ((address = string_nextinlist(&av_scanner_work, &sep,
 
1104
                                          address_buffer,
 
1105
                                          sizeof(address_buffer))) != NULL);
 
1106
 
 
1107
          /* check if we have at least one server */
 
1108
          if (!num_servers)
 
1109
            return m_errlog_defer(scanent,
 
1110
              US"no useable server addresses in malware configuration option.");
 
1111
        }
 
1112
 
 
1113
        /* See the discussion of response formats below to see why we really don't
 
1114
        like colons in filenames when passing filenames to ClamAV. */
 
1115
        if (use_scan_command && Ustrchr(eml_filename, ':'))
 
1116
          return m_errlog_defer(scanent,
 
1117
            string_sprintf("local/SCAN mode incompatible with" \
 
1118
              " : in path to email filename [%s]", eml_filename));
 
1119
 
 
1120
        /* We have some network servers specified */
 
1121
        if (num_servers) {
 
1122
 
 
1123
          /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
 
1124
           * only supports AF_INET, but we should probably be looking to the
 
1125
           * future and rewriting this to be protocol-independent anyway. */
 
1126
 
 
1127
          while ( num_servers > 0 ) {
 
1128
            /* Randomly pick a server to start with */
 
1129
            current_server = random_number( num_servers );
 
1130
 
 
1131
            debug_printf("trying server name %s, port %u\n",
 
1132
                         clamd_address_vector[current_server]->tcp_addr,
 
1133
                         clamd_address_vector[current_server]->tcp_port);
 
1134
 
 
1135
            /* Lookup the host. This is to ensure that we connect to the same IP
 
1136
             * on both connections (as one host could resolve to multiple ips) */
 
1137
            sock= m_tcpsocket(clamd_address_vector[current_server]->tcp_addr,
 
1138
                                clamd_address_vector[current_server]->tcp_port,
 
1139
                                &connhost, &errstr);
 
1140
            if (sock >= 0) {
 
1141
              /* Connection successfully established with a server */
 
1142
              hostname = clamd_address_vector[current_server]->tcp_addr;
 
1143
              break;
 
1144
            }
 
1145
 
 
1146
            (void) m_errlog_defer(scanent, errstr);
 
1147
 
 
1148
            /* Remove the server from the list. XXX We should free the memory */
 
1149
            num_servers--;
 
1150
            int i;
 
1151
            for( i = current_server; i < num_servers; i++ )
 
1152
              clamd_address_vector[i] = clamd_address_vector[i+1];
 
1153
          }
 
1154
 
 
1155
          if ( num_servers == 0 )
 
1156
            return m_errlog_defer(scanent, US"all servers failed");
 
1157
 
 
1158
        } else {
 
1159
          if ((sock = m_unixsocket(scanner_options, &errstr)) < 0)
 
1160
            return m_errlog_defer(scanent, errstr);
 
1161
        }
 
1162
 
 
1163
        /* have socket in variable "sock"; command to use is semi-independent of
 
1164
         * the socket protocol.  We use SCAN if is local (either Unix/local
 
1165
         * domain socket, or explicitly told local) else we stream the data.
 
1166
         * How we stream the data depends upon how we were built.  */
 
1167
 
 
1168
        if (!use_scan_command) {
 
1169
 
 
1170
  #ifdef WITH_OLD_CLAMAV_STREAM
 
1171
          /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
 
1172
           * that port on a second connection; then in the scan-method-neutral
 
1173
           * part, read the response back on the original connection. */
 
1174
 
 
1175
          DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
 
1176
              scanner_name);
 
1177
 
 
1178
          /* Pass the string to ClamAV (7 = "STREAM\n") */
 
1179
          if (m_sock_send(sock, US"STREAM\n", 7, &errstr) < 0)
 
1180
            return m_errlog_defer(scanent, errstr);
 
1181
 
 
1182
          memset(av_buffer2, 0, sizeof(av_buffer2));
 
1183
          bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
 
1184
 
 
1185
          if (bread < 0)
 
1186
            return m_errlog_defer_3(scanent,
 
1187
              string_sprintf("unable to read PORT from socket (%s)",
 
1188
                  strerror(errno)),
 
1189
              sock);
 
1190
 
 
1191
          if (bread == sizeof(av_buffer2))
 
1192
            return m_errlog_defer_3(scanent, "buffer too small", sock);
 
1193
 
 
1194
          if (!(*av_buffer2))
 
1195
            return m_errlog_defer_3(scanent, "ClamAV returned null", sock);
 
1196
 
 
1197
          av_buffer2[bread] = '\0';
 
1198
          if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 )
 
1199
            return m_errlog_defer_3(scanent,
 
1200
              string_sprintf("Expected port information from clamd, got '%s'",
 
1201
                av_buffer2),
 
1202
              sock);
 
1203
 
 
1204
          sockData = m_tcpsocket(connhost.address, port, NULL, &errstr);
 
1205
          if (sockData < 0)
 
1206
            return m_errlog_defer_3(scanent, errstr, sock);
 
1207
 
 
1208
  #define CLOSE_SOCKDATA (void)close(sockData)
 
1209
  #else /* WITH_OLD_CLAMAV_STREAM not defined */
 
1210
          /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
 
1211
          chunks, <n> a 4-byte number (network order), terminated by a zero-length
 
1212
          chunk. */
 
1213
 
 
1214
          DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
 
1215
              scanner_name);
 
1216
 
 
1217
          /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
 
1218
          if (send(sock, "zINSTREAM", 10, 0) < 0)
 
1219
            return m_errlog_defer_3(scanent,
 
1220
              string_sprintf("unable to send zINSTREAM to socket (%s)",
 
1221
                strerror(errno)),
 
1222
              sock);
 
1223
 
 
1224
  #define CLOSE_SOCKDATA /**/
 
1225
  #endif
 
1226
 
 
1227
          /* calc file size */
 
1228
          if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0) {
 
1229
            int err = errno;
 
1230
            CLOSE_SOCKDATA;
 
1231
            return m_errlog_defer_3(scanent,
 
1232
              string_sprintf("can't open spool file %s: %s",
 
1233
                eml_filename, strerror(err)),
 
1234
              sock);
 
1235
          }
 
1236
          if ((fsize = lseek(clam_fd, 0, SEEK_END)) < 0) {
 
1237
            int err = errno;
 
1238
            CLOSE_SOCKDATA; (void)close(clam_fd);
 
1239
            return m_errlog_defer_3(scanent,
 
1240
              string_sprintf("can't seek spool file %s: %s",
 
1241
                eml_filename, strerror(err)),
 
1242
              sock);
 
1243
          }
 
1244
          fsize_uint = (unsigned int) fsize;
 
1245
          if ((off_t)fsize_uint != fsize) {
 
1246
            CLOSE_SOCKDATA; (void)close(clam_fd);
 
1247
            return m_errlog_defer_3(scanent,
 
1248
              string_sprintf("seeking spool file %s, size overflow",
 
1249
                eml_filename),
 
1250
              sock);
 
1251
          }
 
1252
          lseek(clam_fd, 0, SEEK_SET);
 
1253
 
 
1254
          if (!(clamav_fbuf = (uschar *) malloc (fsize_uint))) {
 
1255
            CLOSE_SOCKDATA; (void)close(clam_fd);
 
1256
            return m_errlog_defer_3(scanent,
 
1257
              string_sprintf("unable to allocate memory %u for file (%s)",
 
1258
                fsize_uint, eml_filename),
 
1259
              sock);
 
1260
          }
 
1261
 
 
1262
          if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0) {
 
1263
            int err = errno;
 
1264
            free(clamav_fbuf); CLOSE_SOCKDATA; (void)close(clam_fd);
 
1265
            return m_errlog_defer_3(scanent,
 
1266
              string_sprintf("can't read spool file %s: %s",
 
1267
                eml_filename, strerror(err)),
 
1268
              sock);
 
1269
          }
 
1270
          (void)close(clam_fd);
 
1271
 
 
1272
          /* send file body to socket */
 
1273
  #ifdef WITH_OLD_CLAMAV_STREAM
 
1274
          if (send(sockData, clamav_fbuf, fsize_uint, 0) < 0) {
 
1275
            free(clamav_fbuf); CLOSE_SOCKDATA;
 
1276
            return m_errlog_defer_3(scanent,
 
1277
              string_sprintf("unable to send file body to socket (%s:%u)",
 
1278
                hostname, port),
 
1279
              sock);
 
1280
          }
 
1281
  #else
 
1282
          send_size = htonl(fsize_uint);
 
1283
          send_final_zeroblock = 0;
 
1284
          if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
 
1285
              (send(sock, clamav_fbuf, fsize_uint, 0) < 0) ||
 
1286
              (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
 
1287
            {
 
1288
            free(clamav_fbuf);
 
1289
            return m_errlog_defer_3(scanent,
 
1290
              string_sprintf("unable to send file body to socket (%s)", hostname),
 
1291
              sock);
 
1292
            }
 
1293
  #endif
 
1294
 
 
1295
          free(clamav_fbuf);
 
1296
 
 
1297
          CLOSE_SOCKDATA;
 
1298
  #undef CLOSE_SOCKDATA
 
1299
 
 
1300
        } else { /* use scan command */
 
1301
          /* Send a SCAN command pointing to a filename; then in the then in the
 
1302
           * scan-method-neutral part, read the response back */
 
1303
 
 
1304
  /* ================================================================= */
 
1305
 
 
1306
          /* Prior to the reworking post-Exim-4.72, this scanned a directory,
 
1307
          which dates to when ClamAV needed us to break apart the email into the
 
1308
          MIME parts (eg, with the now deprecated demime condition coming first).
 
1309
          Some time back, ClamAV gained the ability to deconstruct the emails, so
 
1310
          doing this would actually have resulted in the mail attachments being
 
1311
          scanned twice, in the broken out files and from the original .eml.
 
1312
          Since ClamAV now handles emails (and has for quite some time) we can
 
1313
          just use the email file itself. */
 
1314
          /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
 
1315
          file_name = string_sprintf("SCAN %s\n", eml_filename);
 
1316
 
 
1317
          DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
 
1318
              scanner_name, scanner_options);
 
1319
 
 
1320
          if (send(sock, file_name, Ustrlen(file_name), 0) < 0)
 
1321
            return m_errlog_defer_3(scanent,
 
1322
              string_sprintf("unable to write to socket (%s)", strerror(errno)),
 
1323
              sock);
 
1324
 
 
1325
          /* Do not shut down the socket for writing; a user report noted that
 
1326
           * clamd 0.70 does not react well to this. */
 
1327
        }
 
1328
        /* Commands have been sent, no matter which scan method or connection
 
1329
         * type we're using; now just read the result, independent of method. */
 
1330
 
 
1331
        /* Read the result */
 
1332
        memset(av_buffer, 0, sizeof(av_buffer));
 
1333
        bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
 
1334
        (void)close(sock);
 
1335
        sock = -1;
 
1336
 
 
1337
        if (!(bread > 0))
 
1338
          return m_errlog_defer(scanent,
 
1339
            string_sprintf("unable to read from socket (%s)", strerror(errno)));
 
1340
 
 
1341
        if (bread == sizeof(av_buffer))
 
1342
          return m_errlog_defer(scanent, US"buffer too small");
 
1343
        /* We're now assured of a NULL at the end of av_buffer */
 
1344
 
 
1345
        /* Check the result. ClamAV returns one of two result formats.
 
1346
        In the basic mode, the response is of the form:
 
1347
          infected: -> "<filename>: <virusname> FOUND"
 
1348
          not-infected: -> "<filename>: OK"
 
1349
          error: -> "<filename>: <errcode> ERROR
 
1350
        If the ExtendedDetectionInfo option has been turned on, then we get:
 
1351
          "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
 
1352
        for the infected case.  Compare:
 
1353
  /tmp/eicar.com: Eicar-Test-Signature FOUND
 
1354
  /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
 
1355
 
 
1356
        In the streaming case, clamd uses the filename "stream" which you should
 
1357
        be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }.  (The
 
1358
        client app will replace "stream" with the original filename before returning
 
1359
        results to stdout, but the trace shows the data).
 
1360
 
 
1361
        We will assume that the pathname passed to clamd from Exim does not contain
 
1362
        a colon.  We will have whined loudly above if the eml_filename does (and we're
 
1363
        passing a filename to clamd). */
 
1364
 
 
1365
        if (!(*av_buffer))
 
1366
          return m_errlog_defer(scanent, US"ClamAV returned null");
 
1367
 
 
1368
        /* strip newline at the end (won't be present for zINSTREAM)
 
1369
        (also any trailing whitespace, which shouldn't exist, but we depend upon
 
1370
        this below, so double-check) */
 
1371
        p = av_buffer + Ustrlen(av_buffer) - 1;
 
1372
        if (*p == '\n') *p = '\0';
 
1373
 
 
1374
        DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
 
1375
 
 
1376
        while (isspace(*--p) && (p > av_buffer))
 
1377
          *p = '\0';
 
1378
        if (*p) ++p;
 
1379
        response_end = p;
 
1380
 
 
1381
        /* colon in returned output? */
 
1382
        if((p = Ustrchr(av_buffer,':')) == NULL)
 
1383
          return m_errlog_defer(scanent,
 
1384
            string_sprintf("ClamAV returned malformed result (missing colon): %s",
 
1385
                    av_buffer));
 
1386
 
 
1387
        /* strip filename */
 
1388
        while (*p && isspace(*++p)) /**/;
 
1389
        vname = p;
 
1390
 
 
1391
        /* It would be bad to encounter a virus with "FOUND" in part of the name,
 
1392
        but we should at least be resistant to it. */
 
1393
        p = Ustrrchr(vname, ' ');
 
1394
        result_tag = p ? p+1 : vname;
 
1395
 
 
1396
        if (Ustrcmp(result_tag, "FOUND") == 0) {
 
1397
          /* p should still be the whitespace before the result_tag */
 
1398
          while (isspace(*p)) --p;
 
1399
          *++p = '\0';
 
1400
          /* Strip off the extended information too, which will be in parens
 
1401
          after the virus name, with no intervening whitespace. */
 
1402
          if (*--p == ')') {
 
1403
            /* "(hash:size)", so previous '(' will do; if not found, we have
 
1404
            a curious virus name, but not an error. */
 
1405
            p = Ustrrchr(vname, '(');
 
1406
            if (p)
 
1407
              *p = '\0';
 
1408
          }
 
1409
          malware_name = string_copy(vname);
 
1410
          DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
 
1411
 
 
1412
        } else if (Ustrcmp(result_tag, "ERROR") == 0)
 
1413
          return m_errlog_defer(scanent,
 
1414
            string_sprintf("ClamAV returned: %s", av_buffer));
 
1415
 
 
1416
        else if (Ustrcmp(result_tag, "OK") == 0) {
 
1417
          /* Everything should be OK */
 
1418
          malware_name = NULL;
 
1419
          DEBUG(D_acl) debug_printf("Malware not found\n");
 
1420
 
 
1421
        } else
 
1422
          return m_errlog_defer(scanent,
 
1423
            string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
 
1424
 
 
1425
        break;
 
1426
      } /* clamd */
 
1427
 
 
1428
    case M_SOCK: /* "sock" scanner type ------------------------------------- */
 
1429
      /* This code was derived by Martin Poole from the clamd code contributed
 
1430
         by David Saez and the cmdline code
 
1431
      */
 
1432
      {
 
1433
        int bread;
 
1434
        uschar * commandline;
 
1435
        uschar av_buffer[1024];
 
1436
        uschar * linebuffer;
 
1437
        uschar * sockline_scanner;
 
1438
        uschar sockline_scanner_default[] = "%s\n";
 
1439
        const pcre *sockline_trig_re;
 
1440
        const pcre *sockline_name_re;
 
1441
 
 
1442
        /* find scanner command line */
 
1443
        if ((sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
 
1444
                                            NULL, 0)))
 
1445
        {       /* check for no expansions apart from one %s */
 
1446
          char * s = index(CS sockline_scanner, '%');
 
1447
          if (s++)
 
1448
            if ((*s != 's' && *s != '%') || index(s+1, '%'))
 
1449
              return m_errlog_defer_3(scanent,
 
1450
                                    US"unsafe sock scanner call spec", sock);
 
1451
        }
 
1452
        else
 
1453
          sockline_scanner = sockline_scanner_default;
 
1454
 
 
1455
        /* find scanner output trigger */
 
1456
        sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
 
1457
                                  "missing trigger specification", &errstr);
 
1458
        if (!sockline_trig_re)
 
1459
          return m_errlog_defer_3(scanent, errstr, sock);
 
1460
 
 
1461
        /* find virus name regex */
 
1462
        sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
 
1463
                            "missing virus name regex specification", &errstr);
 
1464
        if (!sockline_name_re)
 
1465
          return m_errlog_defer_3(scanent, errstr, sock);
 
1466
 
 
1467
        /* prepare scanner call - security depends on expansions check above */
 
1468
        commandline = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
 
1469
        commandline = string_sprintf( CS sockline_scanner, CS commandline);
 
1470
 
 
1471
 
 
1472
        /* Pass the command string to the socket */
 
1473
        if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0)
 
1474
          return m_errlog_defer(scanent, errstr);
 
1475
 
 
1476
        /* Read the result */
 
1477
        memset(av_buffer, 0, sizeof(av_buffer));
 
1478
        bread = read(sock, av_buffer, sizeof(av_buffer));
 
1479
 
 
1480
        if (!(bread > 0))
 
1481
          return m_errlog_defer_3(scanent,
 
1482
            string_sprintf("unable to read from socket (%s)", strerror(errno)),
 
1483
            sock);
 
1484
 
 
1485
        if (bread == sizeof(av_buffer))
 
1486
          return m_errlog_defer_3(scanent, US"buffer too small", sock);
 
1487
        linebuffer = string_copy(av_buffer);
 
1488
 
 
1489
        /* try trigger match */
 
1490
        if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1)) {
 
1491
          if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
 
1492
            malware_name = US "unknown";
 
1493
        }
 
1494
        else /* no virus found */
 
1495
          malware_name = NULL;
 
1496
        break;
 
1497
      }
 
1498
 
 
1499
    case M_MKSD: /* "mksd" scanner type ------------------------------------- */
 
1500
      {
 
1501
        char *mksd_options_end;
 
1502
        int mksd_maxproc = 1;  /* default, if no option supplied */
 
1503
        int sock;
 
1504
        int retval;
 
1505
 
 
1506
        if (scanner_options) {
 
1507
          mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
 
1508
          if (  *scanner_options == '\0'
 
1509
             || *mksd_options_end != '\0'
 
1510
             || mksd_maxproc < 1
 
1511
             || mksd_maxproc > 32
 
1512
             )
 
1513
            return m_errlog_defer(scanent,
 
1514
              string_sprintf("invalid option '%s'", scanner_options));
 
1515
        }
 
1516
 
 
1517
        if((sock = m_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
 
1518
          return m_errlog_defer(scanent, errstr);
 
1519
 
 
1520
        malware_name = NULL;
 
1521
 
 
1522
        DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
 
1523
 
 
1524
        if ((retval = mksd_scan_packed(scanent, sock, eml_filename)) != OK) {
 
1525
          close (sock);
 
1526
          return retval;
 
1527
        }
 
1528
        break;
 
1529
      }
1861
1530
    }
1862
 
    /* ----------------------------------------------------------------------- */
1863
 
 
1864
 
    /* "unknown" scanner type ------------------------------------------------- */
1865
 
    else {
1866
 
      log_write(0, LOG_MAIN|LOG_PANIC,
1867
 
             "malware condition: unknown scanner type '%s'", scanner_name);
1868
 
      return DEFER;
1869
 
    };
1870
 
    /* ----------------------------------------------------------------------- */
1871
 
 
1872
 
    /* set "been here, done that" marker */
1873
 
    malware_ok = 1;
1874
 
  };
 
1531
 
 
1532
    if (sock >= 0)
 
1533
      (void) close (sock);
 
1534
    malware_ok = TRUE;                  /* set "been here, done that" marker */
 
1535
  }
1875
1536
 
1876
1537
  /* match virus name against pattern (caseless ------->----------v) */
1877
 
  if ( (malware_name != NULL) &&
1878
 
       (regex_match_and_setup(re, malware_name, 0, -1)) ) {
 
1538
  if ( malware_name && (regex_match_and_setup(re, malware_name, 0, -1)) ) {
1879
1539
    DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
1880
1540
    return OK;
1881
1541
  }
1882
 
  else {
 
1542
  else
1883
1543
    return FAIL;
1884
 
  };
1885
1544
}
1886
1545
 
1887
1546
 
1888
1547
/* simple wrapper for reading lines from sockets */
1889
 
int recv_line(int sock, uschar *buffer, int size) {
 
1548
int
 
1549
recv_line(int sock, uschar *buffer, int size)
 
1550
{
1890
1551
  uschar *p = buffer;
1891
1552
 
1892
1553
  memset(buffer,0,size);
1895
1556
    if ((p-buffer) > (size-2)) break;
1896
1557
    if (*p == '\n') break;
1897
1558
    if (*p != '\r') p++;
1898
 
  };
 
1559
  }
1899
1560
  *p = '\0';
1900
1561
 
1901
1562
  return (p-buffer);
1906
1567
 
1907
1568
#include <sys/uio.h>
1908
1569
 
1909
 
static int mksd_writev (int sock, struct iovec *iov, int iovcnt)
 
1570
static inline int
 
1571
mksd_writev (int sock, struct iovec *iov, int iovcnt)
1910
1572
{
1911
1573
  int i;
1912
1574
 
1915
1577
      i = writev (sock, iov, iovcnt);
1916
1578
    while ((i < 0) && (errno == EINTR));
1917
1579
    if (i <= 0) {
1918
 
      close (sock);
1919
 
      log_write(0, LOG_MAIN|LOG_PANIC,
1920
 
                "malware acl condition: unable to write to mksd UNIX socket (/var/run/mksd/socket)");
 
1580
      (void) malware_errlog_defer(US"unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1921
1581
      return -1;
1922
1582
    }
1923
1583
 
1935
1595
  }
1936
1596
}
1937
1597
 
1938
 
static int mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
 
1598
static inline int
 
1599
mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1939
1600
{
1940
1601
  int offset = 0;
1941
1602
  int i;
1942
1603
 
1943
1604
  do {
1944
1605
    if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1945
 
      close (sock);
1946
 
      log_write(0, LOG_MAIN|LOG_PANIC,
1947
 
                "malware acl condition: unable to read from mksd UNIX socket (/var/run/mksd/socket)");
 
1606
      (void) malware_errlog_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1948
1607
      return -1;
1949
1608
    }
1950
1609
 
1951
1610
    offset += i;
1952
1611
    /* offset == av_buffer_size -> buffer full */
1953
1612
    if (offset == av_buffer_size) {
1954
 
      close (sock);
1955
 
      log_write(0, LOG_MAIN|LOG_PANIC,
1956
 
                "malware acl condition: malformed reply received from mksd");
 
1613
      (void) malware_errlog_defer(US"malformed reply received from mksd");
1957
1614
      return -1;
1958
1615
    }
1959
1616
  } while (av_buffer[offset-1] != '\n');
1962
1619
  return offset;
1963
1620
}
1964
1621
 
1965
 
static int mksd_parse_line (char *line)
 
1622
static inline int
 
1623
mksd_parse_line(struct scan * scanent, char *line)
1966
1624
{
1967
1625
  char *p;
1968
1626
 
1969
1627
  switch (*line) {
1970
 
    case 'O':
1971
 
      /* OK */
 
1628
    case 'O': /* OK */
1972
1629
      return OK;
 
1630
 
1973
1631
    case 'E':
1974
 
    case 'A':
1975
 
      /* ERR */
 
1632
    case 'A': /* ERR */
1976
1633
      if ((p = strchr (line, '\n')) != NULL)
1977
 
        (*p) = '\0';
1978
 
      log_write(0, LOG_MAIN|LOG_PANIC,
1979
 
                "malware acl condition: mksd scanner failed: %s", line);
1980
 
      return DEFER;
1981
 
    default:
1982
 
      /* VIR */
 
1634
        *p = '\0';
 
1635
      return m_errlog_defer(scanent,
 
1636
        string_sprintf("scanner failed: %s", line));
 
1637
 
 
1638
    default: /* VIR */
1983
1639
      if ((p = strchr (line, '\n')) != NULL) {
1984
 
        (*p) = '\0';
1985
 
        if (((p-line) > 5) && ((p-line) < sizeof (malware_name_buffer)) && (line[3] == ' '))
 
1640
        *p = '\0';
 
1641
        if (((p-line) > 5) && (line[3] == ' '))
1986
1642
          if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1987
 
            (*p) = '\0';
1988
 
            Ustrcpy (malware_name_buffer, line+4);
1989
 
      malware_name = malware_name_buffer;
 
1643
            *p = '\0';
 
1644
            malware_name = string_copy(US line+4);
1990
1645
            return OK;
1991
1646
          }
1992
1647
      }
1993
 
      log_write(0, LOG_MAIN|LOG_PANIC,
1994
 
                "malware acl condition: malformed reply received from mksd: %s", line);
1995
 
      return DEFER;
 
1648
      return m_errlog_defer(scanent,
 
1649
        string_sprintf("malformed reply received: %s", line));
1996
1650
  }
1997
1651
}
1998
1652
 
1999
 
static int mksd_scan_packed(int sock, uschar *scan_filename)
 
1653
static int
 
1654
mksd_scan_packed(struct scan * scanent, int sock, uschar *scan_filename)
2000
1655
{
2001
1656
  struct iovec iov[3];
2002
1657
  const char *cmd = "MSQ\n";
2015
1670
  if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
2016
1671
    return DEFER;
2017
1672
 
2018
 
  close (sock);
2019
 
 
2020
 
  return mksd_parse_line (CS av_buffer);
 
1673
  return mksd_parse_line (scanent, CS av_buffer);
2021
1674
}
2022
1675
 
2023
 
#endif
 
1676
#endif /*WITH_CONTENT_SCAN*/
 
1677
/*
 
1678
 * vi: aw ai sw=2
 
1679
 */