~ubuntu-branches/ubuntu/hardy/exim4/hardy-proposed

« back to all changes in this revision

Viewing changes to src/malware.c

  • Committer: Bazaar Package Importer
  • Author(s): Marc Haber
  • Date: 2005-07-02 06:08:34 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20050702060834-qk17pd52kb9nt3bj
Tags: 4.52-1
* new upstream version 4.51. (mh)
  * adapt 70_remove_exim-users_references
  * remove 37_gnutlsparams
  * adapt 36_pcre
  * adapt 31_eximmanpage
* fix package priorities to have them in sync with override again. (mh)
* Fix error in nb (Norwegian) translation.
  Thanks to Helge Hafting. (mh). Closes: #315775
* Standards-Version: 3.6.2, no changes needed. (mh)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Cambridge: exim/exim-src/src/malware.c,v 1.11 2005/07/01 10:49:02 ph10 Exp $ */
 
2
 
 
3
/*************************************************
 
4
*     Exim - an Internet mail transport agent    *
 
5
*************************************************/
 
6
 
 
7
/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
 
8
/* License: GPL */
 
9
 
 
10
/* Code for calling virus (malware) scanners. Called from acl.c. */
 
11
 
 
12
#include "exim.h"
 
13
#ifdef WITH_CONTENT_SCAN
 
14
 
 
15
/* declaration of private routines */
 
16
int mksd_scan_packed(int sock);
 
17
 
 
18
/* SHUT_WR seems to be undefined on Unixware? */
 
19
#ifndef SHUT_WR
 
20
#define SHUT_WR 1
 
21
#endif
 
22
 
 
23
#define DRWEBD_SCAN_CMD             (1)     /* scan file, buffer or diskfile */
 
24
#define DRWEBD_RETURN_VIRUSES       (1<<0)   /* ask daemon return to us viruses names from report */
 
25
#define DRWEBD_IS_MAIL              (1<<19)  /* say to daemon that format is "archive MAIL" */
 
26
 
 
27
#define DERR_READ_ERR               (1<<0)   /* read error */
 
28
#define DERR_NOMEMORY               (1<<2)   /* no memory */
 
29
#define DERR_TIMEOUT                (1<<9)   /* scan timeout has run out */
 
30
#define DERR_BAD_CALL               (1<<15)  /* wrong command */
 
31
 
 
32
/* Routine to check whether a system is big- or litte-endian.
 
33
   Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
 
34
   Needed for proper kavdaemon implementation. Sigh. */
 
35
#define BIG_MY_ENDIAN      0
 
36
#define LITTLE_MY_ENDIAN   1
 
37
int test_byte_order(void);
 
38
int test_byte_order() {
 
39
      short int word = 0x0001;
 
40
      char *byte = (char *) &word;
 
41
      return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
 
42
}
 
43
 
 
44
uschar malware_name_buffer[256];
 
45
int malware_ok = 0;
 
46
 
 
47
int malware(uschar **listptr) {
 
48
  int sep = 0;
 
49
  uschar *list = *listptr;
 
50
  uschar *av_scanner_work = av_scanner;
 
51
  uschar *scanner_name;
 
52
  uschar scanner_name_buffer[16];
 
53
  uschar *malware_regex;
 
54
  uschar malware_regex_buffer[64];
 
55
  uschar malware_regex_default[] = ".+";
 
56
  unsigned long mbox_size;
 
57
  FILE *mbox_file;
 
58
  int roffset;
 
59
  const pcre *re;
 
60
  const uschar *rerror;
 
61
 
 
62
  /* make sure the eml mbox file is spooled up */
 
63
  mbox_file = spool_mbox(&mbox_size);
 
64
  if (mbox_file == NULL) {
 
65
    /* error while spooling */
 
66
    log_write(0, LOG_MAIN|LOG_PANIC,
 
67
           "malware acl condition: error while creating mbox spool file");
 
68
    return DEFER;
 
69
  };
 
70
  /* none of our current scanners need the mbox
 
71
     file as a stream, so we can close it right away */
 
72
  (void)fclose(mbox_file);
 
73
 
 
74
  /* extract the malware regex to match against from the option list */
 
75
  if ((malware_regex = string_nextinlist(&list, &sep,
 
76
                                         malware_regex_buffer,
 
77
                                         sizeof(malware_regex_buffer))) != NULL) {
 
78
 
 
79
    /* parse 1st option */
 
80
    if ( (strcmpic(malware_regex,US"false") == 0) ||
 
81
         (Ustrcmp(malware_regex,"0") == 0) ) {
 
82
      /* explicitly no matching */
 
83
      return FAIL;
 
84
    };
 
85
 
 
86
    /* special cases (match anything except empty) */
 
87
    if ( (strcmpic(malware_regex,US"true") == 0) ||
 
88
         (Ustrcmp(malware_regex,"*") == 0) ||
 
89
         (Ustrcmp(malware_regex,"1") == 0) ) {
 
90
      malware_regex = malware_regex_default;
 
91
    };
 
92
  }
 
93
  else {
 
94
    /* empty means "don't match anything" */
 
95
    return FAIL;
 
96
  };
 
97
 
 
98
  /* Reset sep that is set by previous string_nextinlist() call */
 
99
  sep = 0;
 
100
 
 
101
  /* compile the regex, see if it works */
 
102
  re = pcre_compile(CS malware_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
 
103
  if (re == NULL) {
 
104
    log_write(0, LOG_MAIN|LOG_PANIC,
 
105
             "malware acl condition: regular expression error in '%s': %s at offset %d", malware_regex, rerror, roffset);
 
106
    return DEFER;
 
107
  };
 
108
 
 
109
  /* if av_scanner starts with a dollar, expand it first */
 
110
  if (*av_scanner == '$') {
 
111
    av_scanner_work = expand_string(av_scanner);
 
112
    if (av_scanner_work == NULL) {
 
113
      log_write(0, LOG_MAIN|LOG_PANIC,
 
114
           "malware acl condition: av_scanner starts with $, but expansion failed: %s", expand_string_message);
 
115
      return DEFER;
 
116
    }
 
117
    else {
 
118
      debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
 
119
      /* disable result caching in this case */
 
120
      malware_name = NULL;
 
121
      malware_ok = 0;
 
122
    };
 
123
  }
 
124
 
 
125
  /* Do not scan twice. */
 
126
  if (malware_ok == 0) {
 
127
 
 
128
    /* find the scanner type from the av_scanner option */
 
129
    if ((scanner_name = string_nextinlist(&av_scanner_work, &sep,
 
130
                                          scanner_name_buffer,
 
131
                                          sizeof(scanner_name_buffer))) == NULL) {
 
132
      /* no scanner given */
 
133
      log_write(0, LOG_MAIN|LOG_PANIC,
 
134
             "malware acl condition: av_scanner configuration variable is empty");
 
135
      return DEFER;
 
136
    };
 
137
 
 
138
  /* "drweb" scanner type ----------------------------------------------- */
 
139
  /* v0.1 - added support for tcp sockets          */
 
140
  /* v0.0 - initial release -- support for unix sockets      */
 
141
  if (strcmpic(scanner_name,US"drweb") == 0) {
 
142
    uschar *drweb_options;
 
143
    uschar drweb_options_buffer[1024];
 
144
    uschar drweb_options_default[] = "/usr/local/drweb/run/drwebd.sock";
 
145
    struct sockaddr_un server;
 
146
    int sock, result, ovector[30];
 
147
    unsigned int port, fsize;
 
148
    uschar tmpbuf[1024], *drweb_fbuf;
 
149
    uschar scanrequest[1024];
 
150
    uschar drweb_match_string[128];
 
151
    int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
 
152
        drweb_vnum, drweb_slen, drweb_fin = 0x0000;
 
153
    unsigned long bread;
 
154
    uschar hostname[256];
 
155
    struct hostent *he;
 
156
    struct in_addr in;
 
157
    pcre *drweb_re;
 
158
 
 
159
    if ((drweb_options = string_nextinlist(&av_scanner_work, &sep,
 
160
      drweb_options_buffer, sizeof(drweb_options_buffer))) == NULL) {
 
161
      /* no options supplied, use default options */
 
162
      drweb_options = drweb_options_default;
 
163
    };
 
164
 
 
165
    if (*drweb_options != '/') {
 
166
 
 
167
      /* extract host and port part */
 
168
      if( sscanf(CS drweb_options, "%s %u", hostname, &port) != 2 ) {
 
169
        log_write(0, LOG_MAIN|LOG_PANIC,
 
170
          "malware acl condition: drweb: invalid socket '%s'", drweb_options);
 
171
        return DEFER;
 
172
      }
 
173
 
 
174
      /* Lookup the host */
 
175
      if((he = gethostbyname(CS hostname)) == 0) {
 
176
        log_write(0, LOG_MAIN|LOG_PANIC,
 
177
          "malware acl condition: drweb: failed to lookup host '%s'", hostname);
 
178
        return DEFER;
 
179
      }
 
180
 
 
181
      in = *(struct in_addr *) he->h_addr_list[0];
 
182
 
 
183
      /* Open the drwebd TCP socket */
 
184
      if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
 
185
        log_write(0, LOG_MAIN|LOG_PANIC,
 
186
          "malware acl condition: drweb: unable to acquire socket (%s)",
 
187
          strerror(errno));
 
188
        return DEFER;
 
189
      }
 
190
 
 
191
      if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
 
192
        (void)close(sock);
 
193
        log_write(0, LOG_MAIN|LOG_PANIC,
 
194
          "malware acl condition: drweb: connection to %s, port %u failed (%s)",
 
195
          inet_ntoa(in), port, strerror(errno));
 
196
        return DEFER;
 
197
      }
 
198
 
 
199
      /* prepare variables */
 
200
      drweb_cmd = htonl(DRWEBD_SCAN_CMD);
 
201
      drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
 
202
      snprintf(CS scanrequest, 1024,CS"%s/scan/%s/%s.eml",
 
203
            spool_directory, message_id, message_id);
 
204
 
 
205
      /* calc file size */
 
206
      drweb_fd = open(CS scanrequest, O_RDONLY);
 
207
      if (drweb_fd == -1) {
 
208
        (void)close(sock);
 
209
        log_write(0, LOG_MAIN|LOG_PANIC,
 
210
          "malware acl condition: drweb: can't open spool file %s: %s",
 
211
          scanrequest, strerror(errno));
 
212
        return DEFER;
 
213
      }
 
214
      fsize = lseek(drweb_fd, 0, SEEK_END);
 
215
      if (fsize == -1) {
 
216
        (void)close(sock);
 
217
        (void)close(drweb_fd);
 
218
        log_write(0, LOG_MAIN|LOG_PANIC,
 
219
          "malware acl condition: drweb: can't seek spool file %s: %s",
 
220
          scanrequest, strerror(errno));
 
221
        return DEFER;
 
222
      }
 
223
      drweb_slen = htonl(fsize);
 
224
      lseek(drweb_fd, 0, SEEK_SET);
 
225
 
 
226
      /* send scan request */
 
227
      if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
 
228
          (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
 
229
          (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
 
230
          (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
 
231
        (void)close(sock);
 
232
        (void)close(drweb_fd);
 
233
        log_write(0, LOG_MAIN|LOG_PANIC,
 
234
          "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
 
235
        return DEFER;
 
236
      }
 
237
 
 
238
      drweb_fbuf = (uschar *) malloc (fsize);
 
239
      if (!drweb_fbuf) {
 
240
        (void)close(sock);
 
241
        (void)close(drweb_fd);
 
242
        log_write(0, LOG_MAIN|LOG_PANIC,
 
243
          "malware acl condition: drweb: unable to allocate memory %u for file (%s)",
 
244
          fsize, scanrequest);
 
245
        return DEFER;
 
246
      }
 
247
 
 
248
      result = read (drweb_fd, drweb_fbuf, fsize);
 
249
      if (result == -1) {
 
250
        (void)close(sock);
 
251
        (void)close(drweb_fd);
 
252
        free(drweb_fbuf);
 
253
        log_write(0, LOG_MAIN|LOG_PANIC,
 
254
          "malware acl condition: drweb: can't read spool file %s: %s",
 
255
          scanrequest, strerror(errno));
 
256
        return DEFER;
 
257
      }
 
258
      (void)close(drweb_fd);
 
259
 
 
260
      /* send file body to socket */
 
261
      if (send(sock, drweb_fbuf, fsize, 0) < 0) {
 
262
        (void)close(sock);
 
263
        free(drweb_fbuf);
 
264
        log_write(0, LOG_MAIN|LOG_PANIC,
 
265
          "malware acl condition: drweb: unable to send file body to socket (%s)", drweb_options);
 
266
        return DEFER;
 
267
      }
 
268
      (void)close(drweb_fd);
 
269
    }
 
270
    else {
 
271
      /* open the drwebd UNIX socket */
 
272
      sock = socket(AF_UNIX, SOCK_STREAM, 0);
 
273
      if (sock < 0) {
 
274
        log_write(0, LOG_MAIN|LOG_PANIC,
 
275
          "malware acl condition: drweb: can't open UNIX socket");
 
276
        return DEFER;
 
277
      }
 
278
      server.sun_family = AF_UNIX;
 
279
      Ustrcpy(server.sun_path, drweb_options);
 
280
      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
 
281
        (void)close(sock);
 
282
        log_write(0, LOG_MAIN|LOG_PANIC,
 
283
          "malware acl condition: drweb: unable to connect to socket (%s). errno=%d", drweb_options, errno);
 
284
        return DEFER;
 
285
      }
 
286
 
 
287
      /* prepare variables */
 
288
      drweb_cmd = htonl(DRWEBD_SCAN_CMD);
 
289
      drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
 
290
      snprintf(CS scanrequest, 1024,CS"%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
 
291
      drweb_slen = htonl(Ustrlen(scanrequest));
 
292
 
 
293
      /* send scan request */
 
294
      if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
 
295
          (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
 
296
          (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
 
297
          (send(sock, scanrequest, Ustrlen(scanrequest), 0) < 0) ||
 
298
          (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
 
299
        (void)close(sock);
 
300
        log_write(0, LOG_MAIN|LOG_PANIC,
 
301
          "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
 
302
        return DEFER;
 
303
      }
 
304
    }
 
305
 
 
306
    /* wait for result */
 
307
    if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) {
 
308
      (void)close(sock);
 
309
      log_write(0, LOG_MAIN|LOG_PANIC,
 
310
        "malware acl condition: drweb: unable to read return code");
 
311
      return DEFER;
 
312
    }
 
313
    drweb_rc = ntohl(drweb_rc);
 
314
 
 
315
    if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) {
 
316
      (void)close(sock);
 
317
      log_write(0, LOG_MAIN|LOG_PANIC,
 
318
        "malware acl condition: drweb: unable to read the number of viruses");
 
319
      return DEFER;
 
320
    }
 
321
    drweb_vnum = ntohl(drweb_vnum);
 
322
 
 
323
    /* "virus(es) found" if virus number is > 0 */
 
324
    if (drweb_vnum)
 
325
    {
 
326
      int i;
 
327
      uschar pre_malware_nb[256];
 
328
 
 
329
      malware_name = malware_name_buffer;
 
330
 
 
331
      /* setup default virus name */
 
332
      Ustrcpy(malware_name_buffer,"unknown");
 
333
 
 
334
      /* read and concatenate virus names into one string */
 
335
      for (i=0;i<drweb_vnum;i++)
 
336
      {
 
337
        /* read the size of report */
 
338
        if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen))) {
 
339
          (void)close(sock);
 
340
          log_write(0, LOG_MAIN|LOG_PANIC,
 
341
            "malware acl condition: drweb: cannot read report size");
 
342
          return DEFER;
 
343
        };
 
344
        drweb_slen = ntohl(drweb_slen);
 
345
 
 
346
        /* read report body */
 
347
        if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen) {
 
348
          (void)close(sock);
 
349
          log_write(0, LOG_MAIN|LOG_PANIC,
 
350
            "malware acl condition: drweb: cannot read report string");
 
351
          return DEFER;
 
352
        };
 
353
        tmpbuf[drweb_slen] = '\0';
 
354
 
 
355
        /* set up match regex, depends on retcode */
 
356
        Ustrcpy(drweb_match_string, "infected\\swith\\s*(.+?)$");
 
357
 
 
358
        drweb_re = pcre_compile( CS drweb_match_string,
 
359
          PCRE_COPT,
 
360
          (const char **)&rerror,
 
361
          &roffset,
 
362
          NULL );
 
363
 
 
364
        /* try matcher on the line, grab substring */
 
365
        result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
 
366
        if (result >= 2) {
 
367
          pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS pre_malware_nb, 255);
 
368
        }
 
369
        /* the first name we just copy to malware_name */
 
370
        if (i==0)
 
371
          Ustrcpy(CS malware_name_buffer, CS pre_malware_nb);
 
372
        else {
 
373
          /* concatenate each new virus name to previous */
 
374
          int slen = Ustrlen(malware_name_buffer);
 
375
          if (slen < (slen+Ustrlen(pre_malware_nb))) {
 
376
            Ustrcat(malware_name_buffer, "/");
 
377
            Ustrcat(malware_name_buffer, pre_malware_nb);
 
378
          }
 
379
        }
 
380
      }
 
381
    }
 
382
    else {
 
383
      char *drweb_s = NULL;
 
384
 
 
385
      if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
 
386
      if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
 
387
      if (drweb_rc & DERR_TIMEOUT)  drweb_s = "timeout";
 
388
      if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
 
389
      /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
 
390
       * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
 
391
       * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
 
392
       * and others are ignored */
 
393
      if (drweb_s) {
 
394
        log_write(0, LOG_MAIN|LOG_PANIC,
 
395
          "malware acl condition: drweb: drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s);
 
396
        (void)close(sock);
 
397
        return DEFER;
 
398
      }
 
399
      /* no virus found */
 
400
      malware_name = NULL;
 
401
    };
 
402
    (void)close(sock);
 
403
  }
 
404
  /* ----------------------------------------------------------------------- */
 
405
    else if (strcmpic(scanner_name,US"aveserver") == 0) {
 
406
      uschar *kav_options;
 
407
      uschar kav_options_buffer[1024];
 
408
      uschar kav_options_default[] = "/var/run/aveserver";
 
409
      uschar buf[32768];
 
410
      struct sockaddr_un server;
 
411
      int sock;
 
412
      int result;
 
413
 
 
414
      if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
 
415
                                           kav_options_buffer,
 
416
                                           sizeof(kav_options_buffer))) == NULL) {
 
417
        /* no options supplied, use default options */
 
418
        kav_options = kav_options_default;
 
419
      };
 
420
 
 
421
      /* open the aveserver socket */
 
422
      sock = socket(AF_UNIX, SOCK_STREAM, 0);
 
423
      if (sock < 0) {
 
424
        log_write(0, LOG_MAIN|LOG_PANIC,
 
425
             "malware acl condition: can't open UNIX socket.");
 
426
        return DEFER;
 
427
      }
 
428
      server.sun_family = AF_UNIX;
 
429
      Ustrcpy(server.sun_path, kav_options);
 
430
      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
 
431
        (void)close(sock);
 
432
        log_write(0, LOG_MAIN|LOG_PANIC,
 
433
             "malware acl condition: unable to connect to aveserver UNIX socket (%s). errno=%d", kav_options, errno);
 
434
        return DEFER;
 
435
      }
 
436
 
 
437
      /* read aveserver's greeting and see if it is ready (2xx greeting) */
 
438
      recv_line(sock, buf, 32768);
 
439
 
 
440
      if (buf[0] != '2') {
 
441
        /* aveserver is having problems */
 
442
        (void)close(sock);
 
443
        log_write(0, LOG_MAIN|LOG_PANIC,
 
444
             "malware acl condition: aveserver is unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
 
445
        return DEFER;
 
446
      };
 
447
 
 
448
      /* prepare our command */
 
449
      snprintf(CS buf, 32768, "SCAN bPQRSTUW %s/scan/%s/%s.eml\r\n", spool_directory, message_id, message_id);
 
450
 
 
451
      /* and send it */
 
452
      if (send(sock, buf, Ustrlen(buf), 0) < 0) {
 
453
        (void)close(sock);
 
454
        log_write(0, LOG_MAIN|LOG_PANIC,
 
455
             "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
 
456
        return DEFER;
 
457
      }
 
458
 
 
459
      malware_name = NULL;
 
460
      result = 0;
 
461
      /* read response lines, find malware name and final response */
 
462
      while (recv_line(sock, buf, 32768) > 0) {
 
463
        debug_printf("aveserver: %s\n", buf);
 
464
        if (buf[0] == '2') {
 
465
    break;
 
466
  } else if (buf[0] == '5') {
 
467
          /* aveserver is having problems */
 
468
          log_write(0, LOG_MAIN|LOG_PANIC,
 
469
             "malware acl condition: unable to scan file %s/scan/%s/%s.eml (Responded: %s).",
 
470
       spool_directory, message_id, message_id, buf);
 
471
          result = DEFER;
 
472
    break;
 
473
  } else if (Ustrncmp(buf,"322",3) == 0) {
 
474
          uschar *p = Ustrchr(&buf[4],' ');
 
475
          *p = '\0';
 
476
          Ustrcpy(malware_name_buffer,&buf[4]);
 
477
          malware_name = malware_name_buffer;
 
478
  };
 
479
      }
 
480
 
 
481
      /* prepare our command */
 
482
      snprintf(CS buf, 32768, "quit\r\n");
 
483
 
 
484
      /* and send it */
 
485
      if (send(sock, buf, Ustrlen(buf), 0) < 0) {
 
486
        (void)close(sock);
 
487
        log_write(0, LOG_MAIN|LOG_PANIC,
 
488
             "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
 
489
        return DEFER;
 
490
      }
 
491
 
 
492
      /* read aveserver's greeting and see if it is ready (2xx greeting) */
 
493
      recv_line(sock, buf, 32768);
 
494
 
 
495
      if (buf[0] != '2') {
 
496
        /* aveserver is having problems */
 
497
        (void)close(sock);
 
498
        log_write(0, LOG_MAIN|LOG_PANIC,
 
499
             "malware acl condition: unable to quit aveserver dialogue (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
 
500
        return DEFER;
 
501
      };
 
502
 
 
503
      (void)close(sock);
 
504
 
 
505
      if (result == DEFER) return DEFER;
 
506
    }
 
507
    /* "fsecure" scanner type ------------------------------------------------- */
 
508
    else if (strcmpic(scanner_name,US"fsecure") == 0) {
 
509
      uschar *fsecure_options;
 
510
      uschar fsecure_options_buffer[1024];
 
511
      uschar fsecure_options_default[] = "/var/run/.fsav";
 
512
      struct sockaddr_un server;
 
513
      int sock, i, j, bread = 0;
 
514
      uschar file_name[1024];
 
515
      uschar av_buffer[1024];
 
516
      pcre *fs_inf;
 
517
      static uschar *cmdoptions[] = { US"CONFIGURE\tARCHIVE\t1\n",
 
518
                                      US"CONFIGURE\tTIMEOUT\t0\n",
 
519
                                      US"CONFIGURE\tMAXARCH\t5\n",
 
520
                                      US"CONFIGURE\tMIME\t1\n" };
 
521
 
 
522
      malware_name = NULL;
 
523
      if ((fsecure_options = string_nextinlist(&av_scanner_work, &sep,
 
524
                                               fsecure_options_buffer,
 
525
                                               sizeof(fsecure_options_buffer))) == NULL) {
 
526
         /* no options supplied, use default options */
 
527
         fsecure_options = fsecure_options_default;
 
528
      };
 
529
 
 
530
      /* open the fsecure socket */
 
531
      sock = socket(AF_UNIX, SOCK_STREAM, 0);
 
532
      if (sock < 0) {
 
533
        log_write(0, LOG_MAIN|LOG_PANIC,
 
534
                  "malware acl condition: unable to open fsecure socket %s (%s)",
 
535
                  fsecure_options, strerror(errno));
 
536
        return DEFER;
 
537
      }
 
538
      server.sun_family = AF_UNIX;
 
539
      Ustrcpy(server.sun_path, fsecure_options);
 
540
      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
 
541
        (void)close(sock);
 
542
        log_write(0, LOG_MAIN|LOG_PANIC,
 
543
                  "malware acl condition: unable to connect to fsecure socket %s (%s)",
 
544
                  fsecure_options, strerror(errno));
 
545
        return DEFER;
 
546
      }
 
547
 
 
548
      /* pass options */
 
549
      memset(av_buffer, 0, sizeof(av_buffer));
 
550
      for (i=0; i != 4; i++) {
 
551
        /* debug_printf("send option \"%s\"",cmdoptions[i]); */
 
552
        if (write(sock, cmdoptions[i], Ustrlen(cmdoptions[i])) < 0) {
 
553
          (void)close(sock);
 
554
          log_write(0, LOG_MAIN|LOG_PANIC,
 
555
                    "malware acl condition: unable to write fsecure option %d to %s (%s)",
 
556
                    i, fsecure_options, strerror(errno));
 
557
          return DEFER;
 
558
        };
 
559
 
 
560
        bread = read(sock, av_buffer, sizeof(av_buffer));
 
561
        if (bread >0) av_buffer[bread]='\0';
 
562
        if (bread < 0) {
 
563
          (void)close(sock);
 
564
          log_write(0, LOG_MAIN|LOG_PANIC,
 
565
                    "malware acl condition: unable to read fsecure answer %d (%s)", i, strerror(errno));
 
566
          return DEFER;
 
567
        };
 
568
        for (j=0;j<bread;j++) if((av_buffer[j]=='\r')||(av_buffer[j]=='\n')) av_buffer[j] ='@';
 
569
        /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
 
570
        /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
 
571
      };
 
572
 
 
573
      /* pass the mailfile to fsecure */
 
574
      snprintf(CS file_name,1024,"SCAN\t%s/scan/%s/%s.eml\n", spool_directory, message_id, message_id);
 
575
      /* debug_printf("send scan %s",file_name); */
 
576
      if (write(sock, file_name, Ustrlen(file_name)) < 0) {
 
577
        (void)close(sock);
 
578
        log_write(0, LOG_MAIN|LOG_PANIC,
 
579
                  "malware acl condition: unable to write fsecure scan to %s (%s)",
 
580
                  fsecure_options, strerror(errno));
 
581
        return DEFER;
 
582
      };
 
583
 
 
584
      /* set up match */
 
585
      /* todo also SUSPICION\t */
 
586
      fs_inf = pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", PCRE_COPT, (const char **)&rerror, &roffset, NULL);
 
587
 
 
588
      /* read report, linewise */
 
589
      do {
 
590
        int ovector[30];
 
591
        i = 0;
 
592
        memset(av_buffer, 0, sizeof(av_buffer));
 
593
        do {
 
594
          bread=read(sock, &av_buffer[i], 1);
 
595
          if (bread < 0) {
 
596
            (void)close(sock);
 
597
            log_write(0, LOG_MAIN|LOG_PANIC,
 
598
                      "malware acl condition: unable to read fsecure result (%s)", strerror(errno));
 
599
            return DEFER;
 
600
          };
 
601
          i++;
 
602
        }
 
603
        while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
 
604
        av_buffer[i-1] = '\0';
 
605
        /* debug_printf("got line \"%s\"\n",av_buffer); */
 
606
 
 
607
        /* Really search for virus again? */
 
608
        if (malware_name == NULL) {
 
609
          /* try matcher on the line, grab substring */
 
610
          i = pcre_exec(fs_inf, NULL, CS av_buffer, Ustrlen(av_buffer), 0, 0, ovector, 30);
 
611
          if (i >= 2) {
 
612
            /* Got it */
 
613
            pcre_copy_substring(CS av_buffer, ovector, i, 1, CS malware_name_buffer, 255);
 
614
            malware_name = malware_name_buffer;
 
615
          };
 
616
        };
 
617
      }
 
618
      while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
 
619
      (void)close(sock);
 
620
    }
 
621
    /* ----------------------------------------------------------------------- */
 
622
 
 
623
    /* "kavdaemon" scanner type ------------------------------------------------ */
 
624
    else if (strcmpic(scanner_name,US"kavdaemon") == 0) {
 
625
      uschar *kav_options;
 
626
      uschar kav_options_buffer[1024];
 
627
      uschar kav_options_default[] = "/var/run/AvpCtl";
 
628
      struct sockaddr_un server;
 
629
      int sock;
 
630
      time_t t;
 
631
      uschar tmpbuf[1024];
 
632
      uschar scanrequest[1024];
 
633
      uschar kav_match_string[128];
 
634
      int kav_rc;
 
635
      unsigned long kav_reportlen, bread;
 
636
      pcre *kav_re;
 
637
 
 
638
      if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
 
639
                                           kav_options_buffer,
 
640
                                           sizeof(kav_options_buffer))) == NULL) {
 
641
        /* no options supplied, use default options */
 
642
        kav_options = kav_options_default;
 
643
      };
 
644
 
 
645
      /* open the kavdaemon socket */
 
646
      sock = socket(AF_UNIX, SOCK_STREAM, 0);
 
647
      if (sock < 0) {
 
648
        log_write(0, LOG_MAIN|LOG_PANIC,
 
649
             "malware acl condition: can't open UNIX socket.");
 
650
        return DEFER;
 
651
      }
 
652
      server.sun_family = AF_UNIX;
 
653
      Ustrcpy(server.sun_path, kav_options);
 
654
      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
 
655
        (void)close(sock);
 
656
        log_write(0, LOG_MAIN|LOG_PANIC,
 
657
             "malware acl condition: unable to connect to kavdaemon UNIX socket (%s). errno=%d", kav_options, errno);
 
658
        return DEFER;
 
659
      }
 
660
 
 
661
      /* get current date and time, build scan request */
 
662
      time(&t);
 
663
      strftime(CS tmpbuf, sizeof(tmpbuf), "<0>%d %b %H:%M:%S:%%s/scan/%%s", localtime(&t));
 
664
      snprintf(CS scanrequest, 1024,CS tmpbuf, spool_directory, message_id);
 
665
 
 
666
      /* send scan request */
 
667
      if (send(sock, scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
 
668
        (void)close(sock);
 
669
        log_write(0, LOG_MAIN|LOG_PANIC,
 
670
             "malware acl condition: unable to write to kavdaemon UNIX socket (%s)", kav_options);
 
671
        return DEFER;
 
672
      }
 
673
 
 
674
      /* wait for result */
 
675
      if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
 
676
        (void)close(sock);
 
677
        log_write(0, LOG_MAIN|LOG_PANIC,
 
678
             "malware acl condition: unable to read 2 bytes from kavdaemon socket.");
 
679
        return DEFER;
 
680
      }
 
681
 
 
682
      /* get errorcode from one nibble */
 
683
      if (test_byte_order() == LITTLE_MY_ENDIAN) {
 
684
        kav_rc = tmpbuf[0] & 0x0F;
 
685
      }
 
686
      else {
 
687
        kav_rc = tmpbuf[1] & 0x0F;
 
688
      };
 
689
 
 
690
      /* improper kavdaemon configuration */
 
691
      if ( (kav_rc == 5) || (kav_rc == 6) ) {
 
692
        (void)close(sock);
 
693
        log_write(0, LOG_MAIN|LOG_PANIC,
 
694
             "malware acl condition: please reconfigure kavdaemon to NOT disinfect or remove infected files.");
 
695
        return DEFER;
 
696
      };
 
697
 
 
698
      if (kav_rc == 1) {
 
699
        (void)close(sock);
 
700
        log_write(0, LOG_MAIN|LOG_PANIC,
 
701
             "malware acl condition: kavdaemon reported 'scanning not completed' (code 1).");
 
702
        return DEFER;
 
703
      };
 
704
 
 
705
      if (kav_rc == 7) {
 
706
        (void)close(sock);
 
707
        log_write(0, LOG_MAIN|LOG_PANIC,
 
708
             "malware acl condition: kavdaemon reported 'kavdaemon damaged' (code 7).");
 
709
        return DEFER;
 
710
      };
 
711
 
 
712
      /* code 8 is not handled, since it is ambigous. It appears mostly on
 
713
      bounces where part of a file has been cut off */
 
714
 
 
715
      /* "virus found" return codes (2-4) */
 
716
      if ((kav_rc > 1) && (kav_rc < 5)) {
 
717
        int report_flag = 0;
 
718
 
 
719
        /* setup default virus name */
 
720
        Ustrcpy(malware_name_buffer,"unknown");
 
721
        malware_name = malware_name_buffer;
 
722
 
 
723
        if (test_byte_order() == LITTLE_MY_ENDIAN) {
 
724
          report_flag = tmpbuf[1];
 
725
        }
 
726
        else {
 
727
          report_flag = tmpbuf[0];
 
728
        };
 
729
 
 
730
        /* read the report, if available */
 
731
        if( report_flag == 1 ) {
 
732
          /* read report size */
 
733
          if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
 
734
            (void)close(sock);
 
735
            log_write(0, LOG_MAIN|LOG_PANIC,
 
736
                  "malware acl condition: cannot read report size from kavdaemon");
 
737
            return DEFER;
 
738
          };
 
739
 
 
740
          /* it's possible that avp returns av_buffer[1] == 1 but the
 
741
          reportsize is 0 (!?) */
 
742
          if (kav_reportlen > 0) {
 
743
            /* set up match regex, depends on retcode */
 
744
            if( kav_rc == 3 )
 
745
              Ustrcpy(kav_match_string, "suspicion:\\s*(.+?)\\s*$");
 
746
            else
 
747
              Ustrcpy(kav_match_string, "infected:\\s*(.+?)\\s*$");
 
748
 
 
749
            kav_re = pcre_compile( CS kav_match_string,
 
750
                                   PCRE_COPT,
 
751
                                   (const char **)&rerror,
 
752
                                   &roffset,
 
753
                                   NULL );
 
754
 
 
755
            /* read report, linewise */
 
756
            while (kav_reportlen > 0) {
 
757
              int result = 0;
 
758
              int ovector[30];
 
759
 
 
760
              bread = 0;
 
761
              while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
 
762
                kav_reportlen--;
 
763
                if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
 
764
                bread++;
 
765
              };
 
766
              bread++;
 
767
              tmpbuf[bread] = '\0';
 
768
 
 
769
              /* try matcher on the line, grab substring */
 
770
              result = pcre_exec(kav_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
 
771
              if (result >= 2) {
 
772
                pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS malware_name_buffer, 255);
 
773
                break;
 
774
              };
 
775
            };
 
776
          };
 
777
        };
 
778
      }
 
779
      else {
 
780
        /* no virus found */
 
781
        malware_name = NULL;
 
782
      };
 
783
 
 
784
      (void)close(sock);
 
785
    }
 
786
    /* ----------------------------------------------------------------------- */
 
787
 
 
788
 
 
789
    /* "cmdline" scanner type ------------------------------------------------ */
 
790
    else if (strcmpic(scanner_name,US"cmdline") == 0) {
 
791
      uschar *cmdline_scanner;
 
792
      uschar cmdline_scanner_buffer[1024];
 
793
      uschar *cmdline_trigger;
 
794
      uschar cmdline_trigger_buffer[1024];
 
795
      const pcre *cmdline_trigger_re;
 
796
      uschar *cmdline_regex;
 
797
      uschar cmdline_regex_buffer[1024];
 
798
      const pcre *cmdline_regex_re;
 
799
      uschar file_name[1024];
 
800
      uschar commandline[1024];
 
801
      void (*eximsigchld)(int);
 
802
      void (*eximsigpipe)(int);
 
803
      FILE *scanner_out = NULL;
 
804
      FILE *scanner_record = NULL;
 
805
      uschar linebuffer[32767];
 
806
      int trigger = 0;
 
807
      int result;
 
808
      int ovector[30];
 
809
 
 
810
      /* find scanner command line */
 
811
      if ((cmdline_scanner = string_nextinlist(&av_scanner_work, &sep,
 
812
                                          cmdline_scanner_buffer,
 
813
                                          sizeof(cmdline_scanner_buffer))) == NULL) {
 
814
        /* no command line supplied */
 
815
        log_write(0, LOG_MAIN|LOG_PANIC,
 
816
             "malware acl condition: missing commandline specification for cmdline scanner type.");
 
817
        return DEFER;
 
818
      };
 
819
 
 
820
      /* find scanner output trigger */
 
821
      if ((cmdline_trigger = string_nextinlist(&av_scanner_work, &sep,
 
822
                                          cmdline_trigger_buffer,
 
823
                                          sizeof(cmdline_trigger_buffer))) == NULL) {
 
824
        /* no trigger regex supplied */
 
825
        log_write(0, LOG_MAIN|LOG_PANIC,
 
826
             "malware acl condition: missing trigger specification for cmdline scanner type.");
 
827
        return DEFER;
 
828
      };
 
829
 
 
830
      /* precompile trigger regex */
 
831
      cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
 
832
      if (cmdline_trigger_re == NULL) {
 
833
        log_write(0, LOG_MAIN|LOG_PANIC,
 
834
                 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_trigger_re, rerror, roffset);
 
835
        return DEFER;
 
836
      };
 
837
 
 
838
      /* find scanner name regex */
 
839
      if ((cmdline_regex = string_nextinlist(&av_scanner_work, &sep,
 
840
                                             cmdline_regex_buffer,
 
841
                                             sizeof(cmdline_regex_buffer))) == NULL) {
 
842
        /* no name regex supplied */
 
843
        log_write(0, LOG_MAIN|LOG_PANIC,
 
844
             "malware acl condition: missing virus name regex specification for cmdline scanner type.");
 
845
        return DEFER;
 
846
      };
 
847
 
 
848
      /* precompile name regex */
 
849
      cmdline_regex_re = pcre_compile(CS cmdline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
 
850
      if (cmdline_regex_re == NULL) {
 
851
        log_write(0, LOG_MAIN|LOG_PANIC,
 
852
                 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_regex_re, rerror, roffset);
 
853
        return DEFER;
 
854
      };
 
855
 
 
856
      /* prepare scanner call */
 
857
      snprintf(CS file_name,1024,"%s/scan/%s", spool_directory, message_id);
 
858
      snprintf(CS commandline,1024, CS cmdline_scanner,file_name);
 
859
      /* redirect STDERR too */
 
860
      Ustrcat(commandline," 2>&1");
 
861
 
 
862
      /* store exims signal handlers */
 
863
      eximsigchld = signal(SIGCHLD,SIG_DFL);
 
864
      eximsigpipe = signal(SIGPIPE,SIG_DFL);
 
865
 
 
866
      scanner_out = popen(CS commandline,"r");
 
867
      if (scanner_out == NULL) {
 
868
        log_write(0, LOG_MAIN|LOG_PANIC,
 
869
                 "malware acl condition: calling cmdline scanner (%s) failed: %s.", commandline, strerror(errno));
 
870
        signal(SIGCHLD,eximsigchld);
 
871
        signal(SIGPIPE,eximsigpipe);
 
872
        return DEFER;
 
873
      };
 
874
 
 
875
      snprintf(CS file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
 
876
      scanner_record = fopen(CS file_name,"wb");
 
877
 
 
878
      if (scanner_record == NULL) {
 
879
        log_write(0, LOG_MAIN|LOG_PANIC,
 
880
                 "malware acl condition: opening scanner output file (%s) failed: %s.", file_name, strerror(errno));
 
881
        pclose(scanner_out);
 
882
        signal(SIGCHLD,eximsigchld);
 
883
        signal(SIGPIPE,eximsigpipe);
 
884
        return DEFER;
 
885
      };
 
886
 
 
887
      /* look for trigger while recording output */
 
888
      while(fgets(CS linebuffer,32767,scanner_out) != NULL) {
 
889
        if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
 
890
          /* short write */
 
891
          log_write(0, LOG_MAIN|LOG_PANIC,
 
892
                 "malware acl condition: short write on scanner output file (%s).", file_name);
 
893
          pclose(scanner_out);
 
894
          signal(SIGCHLD,eximsigchld);
 
895
          signal(SIGPIPE,eximsigpipe);
 
896
          return DEFER;
 
897
        };
 
898
        /* try trigger match */
 
899
        if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
 
900
          trigger = 1;
 
901
      };
 
902
 
 
903
      (void)fclose(scanner_record);
 
904
      pclose(scanner_out);
 
905
      signal(SIGCHLD,eximsigchld);
 
906
      signal(SIGPIPE,eximsigpipe);
 
907
 
 
908
      if (trigger) {
 
909
        /* setup default virus name */
 
910
        Ustrcpy(malware_name_buffer,"unknown");
 
911
        malware_name = malware_name_buffer;
 
912
 
 
913
        /* re-open the scanner output file, look for name match */
 
914
        scanner_record = fopen(CS file_name,"rb");
 
915
        while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
 
916
          /* try match */
 
917
          result = pcre_exec(cmdline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
 
918
          if (result >= 2) {
 
919
            pcre_copy_substring(CS linebuffer, ovector, result, 1, CS malware_name_buffer, 255);
 
920
          };
 
921
        };
 
922
        (void)fclose(scanner_record);
 
923
      }
 
924
      else {
 
925
        /* no virus found */
 
926
        malware_name = NULL;
 
927
      };
 
928
    }
 
929
    /* ----------------------------------------------------------------------- */
 
930
 
 
931
 
 
932
    /* "sophie" scanner type ------------------------------------------------- */
 
933
    else if (strcmpic(scanner_name,US"sophie") == 0) {
 
934
      uschar *sophie_options;
 
935
      uschar sophie_options_buffer[1024];
 
936
      uschar sophie_options_default[] = "/var/run/sophie";
 
937
      int bread = 0;
 
938
      struct sockaddr_un server;
 
939
      int sock;
 
940
      uschar file_name[1024];
 
941
      uschar av_buffer[1024];
 
942
 
 
943
      if ((sophie_options = string_nextinlist(&av_scanner_work, &sep,
 
944
                                          sophie_options_buffer,
 
945
                                          sizeof(sophie_options_buffer))) == NULL) {
 
946
        /* no options supplied, use default options */
 
947
        sophie_options = sophie_options_default;
 
948
      };
 
949
 
 
950
      /* open the sophie socket */
 
951
      sock = socket(AF_UNIX, SOCK_STREAM, 0);
 
952
      if (sock < 0) {
 
953
        log_write(0, LOG_MAIN|LOG_PANIC,
 
954
             "malware acl condition: can't open UNIX socket.");
 
955
        return DEFER;
 
956
      }
 
957
      server.sun_family = AF_UNIX;
 
958
      Ustrcpy(server.sun_path, sophie_options);
 
959
      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
 
960
        (void)close(sock);
 
961
        log_write(0, LOG_MAIN|LOG_PANIC,
 
962
             "malware acl condition: unable to connect to sophie UNIX socket (%s). errno=%d", sophie_options, errno);
 
963
        return DEFER;
 
964
      }
 
965
 
 
966
      /* pass the scan directory to sophie */
 
967
      snprintf(CS file_name,1024,"%s/scan/%s", spool_directory, message_id);
 
968
      if (write(sock, file_name, Ustrlen(file_name)) < 0) {
 
969
        (void)close(sock);
 
970
        log_write(0, LOG_MAIN|LOG_PANIC,
 
971
             "malware acl condition: unable to write to sophie UNIX socket (%s)", sophie_options);
 
972
        return DEFER;
 
973
      };
 
974
 
 
975
      (void)write(sock, "\n", 1);
 
976
 
 
977
      /* wait for result */
 
978
      memset(av_buffer, 0, sizeof(av_buffer));
 
979
      if ((!(bread = read(sock, av_buffer, sizeof(av_buffer))) > 0)) {
 
980
        (void)close(sock);
 
981
        log_write(0, LOG_MAIN|LOG_PANIC,
 
982
             "malware acl condition: unable to read from sophie UNIX socket (%s)", sophie_options);
 
983
        return DEFER;
 
984
      };
 
985
 
 
986
      (void)close(sock);
 
987
 
 
988
      /* infected ? */
 
989
      if (av_buffer[0] == '1') {
 
990
        if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
 
991
        Ustrcpy(malware_name_buffer,&av_buffer[2]);
 
992
        malware_name = malware_name_buffer;
 
993
      }
 
994
      else if (!strncmp(CS av_buffer, "-1", 2)) {
 
995
        log_write(0, LOG_MAIN|LOG_PANIC,
 
996
             "malware acl condition: malware acl condition: sophie reported error");
 
997
        return DEFER;
 
998
      }
 
999
      else {
 
1000
        /* all ok, no virus */
 
1001
        malware_name = NULL;
 
1002
      };
 
1003
    }
 
1004
    /* ----------------------------------------------------------------------- */
 
1005
 
 
1006
 
 
1007
    /* "clamd" scanner type ------------------------------------------------- */
 
1008
    /* This code was contributed by David Saez */
 
1009
    else if (strcmpic(scanner_name,US"clamd") == 0) {
 
1010
      uschar *clamd_options;
 
1011
      uschar clamd_options_buffer[1024];
 
1012
      uschar clamd_options_default[] = "/tmp/clamd";
 
1013
      uschar *p,*vname;
 
1014
      struct sockaddr_un server;
 
1015
      int sock,bread=0;
 
1016
      unsigned int port;
 
1017
      uschar file_name[1024];
 
1018
      uschar av_buffer[1024];
 
1019
      uschar hostname[256];
 
1020
      struct hostent *he;
 
1021
      struct in_addr in;
 
1022
      uschar *clamd_options2;
 
1023
      uschar clamd_options2_buffer[1024];
 
1024
      uschar clamd_options2_default[] = "";
 
1025
      uschar av_buffer2[1024];
 
1026
      uschar *clamav_fbuf;
 
1027
      uschar scanrequest[1024];
 
1028
      int sockData, clam_fd, result;
 
1029
      unsigned int fsize;
 
1030
 
 
1031
      if ((clamd_options = string_nextinlist(&av_scanner_work, &sep,
 
1032
                                             clamd_options_buffer,
 
1033
                                             sizeof(clamd_options_buffer))) == NULL) {
 
1034
        /* no options supplied, use default options */
 
1035
        clamd_options = clamd_options_default;
 
1036
      }
 
1037
      if ((clamd_options2 = string_nextinlist(&av_scanner_work, &sep,
 
1038
                                             clamd_options2_buffer,
 
1039
                                             sizeof(clamd_options2_buffer))) == NULL) {
 
1040
        clamd_options2 = clamd_options2_default;
 
1041
      }
 
1042
 
 
1043
      /* socket does not start with '/' -> network socket */
 
1044
      if (*clamd_options != '/') {
 
1045
 
 
1046
        /* extract host and port part */
 
1047
        if( sscanf(CS clamd_options, "%s %u", hostname, &port) != 2 ) {
 
1048
          log_write(0, LOG_MAIN|LOG_PANIC,
 
1049
                    "malware acl condition: clamd: invalid socket '%s'", clamd_options);
 
1050
          return DEFER;
 
1051
        };
 
1052
 
 
1053
        /* Lookup the host */
 
1054
        if((he = gethostbyname(CS hostname)) == 0) {
 
1055
          log_write(0, LOG_MAIN|LOG_PANIC,
 
1056
                    "malware acl condition: clamd: failed to lookup host '%s'", hostname);
 
1057
          return DEFER;
 
1058
        }
 
1059
 
 
1060
        in = *(struct in_addr *) he->h_addr_list[0];
 
1061
 
 
1062
        /* Open the ClamAV Socket */
 
1063
        if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
 
1064
          log_write(0, LOG_MAIN|LOG_PANIC,
 
1065
                    "malware acl condition: clamd: unable to acquire socket (%s)",
 
1066
                    strerror(errno));
 
1067
          return DEFER;
 
1068
        }
 
1069
 
 
1070
        if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
 
1071
          (void)close(sock);
 
1072
          log_write(0, LOG_MAIN|LOG_PANIC,
 
1073
                    "malware acl condition: clamd: connection to %s, port %u failed (%s)",
 
1074
                    inet_ntoa(in), port, strerror(errno));
 
1075
          return DEFER;
 
1076
        }
 
1077
 
 
1078
        if (strcmpic(clamd_options2,US"local") == 0) {
 
1079
 
 
1080
      /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
 
1081
 
 
1082
          snprintf(CS file_name,1024,"SCAN %s/scan/%s\n", spool_directory, message_id);
 
1083
 
 
1084
          if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
 
1085
            (void)close(sock);
 
1086
            log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
 
1087
                  strerror(errno));
 
1088
            return DEFER;
 
1089
          }
 
1090
        } else {
 
1091
 
 
1092
      /* Pass the string to ClamAV (7 = "STREAM\n") */
 
1093
 
 
1094
          if (send(sock, "STREAM\n", 7, 0) < 0) {
 
1095
            (void)close(sock);
 
1096
            log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
 
1097
                  strerror(errno));
 
1098
            return DEFER;
 
1099
          }
 
1100
          memset(av_buffer2, 0, sizeof(av_buffer2));
 
1101
          bread = read(sock, av_buffer2, sizeof(av_buffer2));
 
1102
 
 
1103
          if (bread < 0) {
 
1104
            log_write(0, LOG_MAIN|LOG_PANIC,
 
1105
                  "malware acl condition: clamd: unable to read PORT from socket (%s)",
 
1106
                  strerror(errno));
 
1107
            return DEFER;
 
1108
          }
 
1109
 
 
1110
          if (bread == sizeof(av_buffer)) {
 
1111
            log_write(0, LOG_MAIN|LOG_PANIC,
 
1112
                  "malware acl condition: clamd: buffer too small");
 
1113
            return DEFER;
 
1114
          }
 
1115
 
 
1116
          if (!(*av_buffer2)) {
 
1117
            log_write(0, LOG_MAIN|LOG_PANIC,
 
1118
                  "malware acl condition: clamd: ClamAV returned null");
 
1119
            return DEFER;
 
1120
          }
 
1121
 
 
1122
          av_buffer2[bread] = '\0';
 
1123
          if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) {
 
1124
            log_write(0, LOG_MAIN|LOG_PANIC,
 
1125
                    "malware acl condition: clamd: Expected port information from clamd, got '%s'", av_buffer2);
 
1126
            return DEFER;
 
1127
          };
 
1128
 
 
1129
          if ( (sockData = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
 
1130
            log_write(0, LOG_MAIN|LOG_PANIC,
 
1131
                    "malware acl condition: clamd: unable to acquire socket (%s)",
 
1132
                    strerror(errno));
 
1133
            return DEFER;
 
1134
          }
 
1135
 
 
1136
          if (ip_connect(sockData, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
 
1137
            (void)close(sockData);
 
1138
            log_write(0, LOG_MAIN|LOG_PANIC,
 
1139
                    "malware acl condition: clamd: connection to %s, port %u failed (%s)",
 
1140
                    inet_ntoa(in), port, strerror(errno));
 
1141
            return DEFER;
 
1142
          }
 
1143
 
 
1144
    snprintf(CS scanrequest, 1024,CS"%s/scan/%s/%s.eml",
 
1145
      spool_directory, message_id, message_id);
 
1146
 
 
1147
    /* calc file size */
 
1148
    clam_fd = open(CS scanrequest, O_RDONLY);
 
1149
    if (clam_fd == -1) {
 
1150
      log_write(0, LOG_MAIN|LOG_PANIC,
 
1151
        "malware acl condition: clamd: can't open spool file %s: %s",
 
1152
        scanrequest, strerror(errno));
 
1153
      return DEFER;
 
1154
    }
 
1155
    fsize = lseek(clam_fd, 0, SEEK_END);
 
1156
    if (fsize == -1) {
 
1157
      log_write(0, LOG_MAIN|LOG_PANIC,
 
1158
        "malware acl condition: clamd: can't seek spool file %s: %s",
 
1159
        scanrequest, strerror(errno));
 
1160
      return DEFER;
 
1161
    }
 
1162
    lseek(clam_fd, 0, SEEK_SET);
 
1163
 
 
1164
    clamav_fbuf = (uschar *) malloc (fsize);
 
1165
    if (!clamav_fbuf) {
 
1166
      (void)close(sockData);
 
1167
      (void)close(clam_fd);
 
1168
      log_write(0, LOG_MAIN|LOG_PANIC,
 
1169
        "malware acl condition: clamd: unable to allocate memory %u for file (%s)",
 
1170
        fsize, scanrequest);
 
1171
      return DEFER;
 
1172
    }
 
1173
 
 
1174
    result = read (clam_fd, clamav_fbuf, fsize);
 
1175
    if (result == -1) {
 
1176
      (void)close(sockData);
 
1177
      (void)close(clam_fd);
 
1178
      free(clamav_fbuf);
 
1179
      log_write(0, LOG_MAIN|LOG_PANIC,
 
1180
        "malware acl condition: clamd: can't read spool file %s: %s",
 
1181
        scanrequest, strerror(errno));
 
1182
      return DEFER;
 
1183
    }
 
1184
    (void)close(clam_fd);
 
1185
 
 
1186
    /* send file body to socket */
 
1187
    if (send(sockData, clamav_fbuf, fsize, 0) < 0) {
 
1188
      (void)close(sockData);
 
1189
      free(clamav_fbuf);
 
1190
      log_write(0, LOG_MAIN|LOG_PANIC,
 
1191
        "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
 
1192
      return DEFER;
 
1193
    }
 
1194
    free(clamav_fbuf);
 
1195
          (void)close(sockData);
 
1196
        }
 
1197
      }
 
1198
      else {
 
1199
        /* open the local socket */
 
1200
        if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
 
1201
          log_write(0, LOG_MAIN|LOG_PANIC,
 
1202
                    "malware acl condition: clamd: unable to acquire socket (%s)",
 
1203
                    strerror(errno));
 
1204
          return DEFER;
 
1205
        }
 
1206
 
 
1207
        server.sun_family = AF_UNIX;
 
1208
        Ustrcpy(server.sun_path, clamd_options);
 
1209
 
 
1210
        if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
 
1211
          (void)close(sock);
 
1212
          log_write(0, LOG_MAIN|LOG_PANIC,
 
1213
                    "malware acl condition: clamd: unable to connect to UNIX socket %s (%s)",
 
1214
                    clamd_options, strerror(errno) );
 
1215
          return DEFER;
 
1216
        }
 
1217
      }
 
1218
 
 
1219
      /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
 
1220
 
 
1221
      snprintf(CS file_name,1024,"SCAN %s/scan/%s\n", spool_directory, message_id);
 
1222
 
 
1223
      if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
 
1224
        (void)close(sock);
 
1225
        log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
 
1226
                  strerror(errno));
 
1227
        return DEFER;
 
1228
      }
 
1229
 
 
1230
      /*
 
1231
        We're done sending, close socket for writing.
 
1232
 
 
1233
        One user reported that clamd 0.70 does not like this any more ...
 
1234
 
 
1235
      */
 
1236
 
 
1237
      /* shutdown(sock, SHUT_WR); */
 
1238
 
 
1239
      /* Read the result */
 
1240
      memset(av_buffer, 0, sizeof(av_buffer));
 
1241
      bread = read(sock, av_buffer, sizeof(av_buffer));
 
1242
      (void)close(sock);
 
1243
 
 
1244
      if (!(bread  > 0)) {
 
1245
        log_write(0, LOG_MAIN|LOG_PANIC,
 
1246
                  "malware acl condition: clamd: unable to read from socket (%s)",
 
1247
                  strerror(errno));
 
1248
        return DEFER;
 
1249
      }
 
1250
 
 
1251
      if (bread == sizeof(av_buffer)) {
 
1252
        log_write(0, LOG_MAIN|LOG_PANIC,
 
1253
                  "malware acl condition: clamd: buffer too small");
 
1254
        return DEFER;
 
1255
      }
 
1256
 
 
1257
      /* Check the result. ClamAV Returns
 
1258
         infected: -> "<filename>: <virusname> FOUND"
 
1259
         not-infected: -> "<filename>: OK"
 
1260
    error: -> "<filename>: <errcode> ERROR */
 
1261
 
 
1262
      if (!(*av_buffer)) {
 
1263
        log_write(0, LOG_MAIN|LOG_PANIC,
 
1264
                  "malware acl condition: clamd: ClamAV returned null");
 
1265
        return DEFER;
 
1266
      }
 
1267
 
 
1268
      /* colon in returned output? */
 
1269
      if((p = Ustrrchr(av_buffer,':')) == NULL) {
 
1270
        log_write(0, LOG_MAIN|LOG_PANIC,
 
1271
                  "malware acl condition: clamd: ClamAV returned malformed result: %s",
 
1272
                  av_buffer);
 
1273
        return DEFER;
 
1274
      }
 
1275
 
 
1276
      /* strip filename strip CR at the end */
 
1277
      ++p;
 
1278
      while (*p == ' ') ++p;
 
1279
      vname = p;
 
1280
      p = vname + Ustrlen(vname) - 1;
 
1281
      if( *p == '\n' ) *p = '\0';
 
1282
 
 
1283
      if ((p = Ustrstr(vname, "FOUND"))!=NULL) {
 
1284
           *p=0;
 
1285
           for (--p;p>vname && *p<=32;p--) *p=0;
 
1286
           for (;*vname==32;vname++);
 
1287
           Ustrcpy(malware_name_buffer,vname);
 
1288
           malware_name = malware_name_buffer;
 
1289
      }
 
1290
      else {
 
1291
           if (Ustrstr(vname, "ERROR")!=NULL) {
 
1292
              /* ClamAV reports ERROR
 
1293
              Find line start */
 
1294
              for (;*vname!='\n' && vname>av_buffer; vname--);
 
1295
              if (*vname=='\n') vname++;
 
1296
 
 
1297
              log_write(0, LOG_MAIN|LOG_PANIC,
 
1298
                     "malware acl condition: clamd: ClamAV returned %s",vname);
 
1299
              return DEFER;
 
1300
           }
 
1301
           else {
 
1302
              /* Everything should be OK */
 
1303
              malware_name = NULL;
 
1304
           }
 
1305
      }
 
1306
    }
 
1307
    /* ----------------------------------------------------------------------- */
 
1308
 
 
1309
 
 
1310
    /* "mksd" scanner type --------------------------------------------------- */
 
1311
    else if (strcmpic(scanner_name,US"mksd") == 0) {
 
1312
      uschar *mksd_options;
 
1313
      char *mksd_options_end;
 
1314
      uschar mksd_options_buffer[32];
 
1315
      int mksd_maxproc = 1;  /* default, if no option supplied */
 
1316
      struct sockaddr_un server;
 
1317
      int sock;
 
1318
      int retval;
 
1319
 
 
1320
      if ((mksd_options = string_nextinlist(&av_scanner_work, &sep,
 
1321
                                            mksd_options_buffer,
 
1322
                                            sizeof(mksd_options_buffer))) != NULL) {
 
1323
        mksd_maxproc = (int) strtol(CS mksd_options, &mksd_options_end, 10);
 
1324
        if ((*mksd_options == '\0') || (*mksd_options_end != '\0') ||
 
1325
      (mksd_maxproc < 1) || (mksd_maxproc > 32)) {
 
1326
          log_write(0, LOG_MAIN|LOG_PANIC,
 
1327
                    "malware acl condition: mksd: invalid option '%s'", mksd_options);
 
1328
          return DEFER;
 
1329
        }
 
1330
      }
 
1331
 
 
1332
      /* open the mksd socket */
 
1333
      sock = socket(AF_UNIX, SOCK_STREAM, 0);
 
1334
      if (sock < 0) {
 
1335
        log_write(0, LOG_MAIN|LOG_PANIC,
 
1336
             "malware acl condition: can't open UNIX socket.");
 
1337
        return DEFER;
 
1338
      }
 
1339
      server.sun_family = AF_UNIX;
 
1340
      Ustrcpy(server.sun_path, "/var/run/mksd/socket");
 
1341
      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
 
1342
        (void)close(sock);
 
1343
        log_write(0, LOG_MAIN|LOG_PANIC,
 
1344
             "malware acl condition: unable to connect to mksd UNIX socket (/var/run/mksd/socket). errno=%d", errno);
 
1345
        return DEFER;
 
1346
      }
 
1347
 
 
1348
      malware_name = NULL;
 
1349
 
 
1350
      retval = mksd_scan_packed(sock);
 
1351
 
 
1352
      if (retval != OK)
 
1353
        return retval;
 
1354
    }
 
1355
    /* ----------------------------------------------------------------------- */
 
1356
 
 
1357
    /* "unknown" scanner type ------------------------------------------------- */
 
1358
    else {
 
1359
      log_write(0, LOG_MAIN|LOG_PANIC,
 
1360
             "malware condition: unknown scanner type '%s'", scanner_name);
 
1361
      return DEFER;
 
1362
    };
 
1363
    /* ----------------------------------------------------------------------- */
 
1364
 
 
1365
    /* set "been here, done that" marker */
 
1366
    malware_ok = 1;
 
1367
  };
 
1368
 
 
1369
  /* match virus name against pattern (caseless ------->----------v) */
 
1370
  if ( (malware_name != NULL) &&
 
1371
       (regex_match_and_setup(re, malware_name, 0, -1)) ) {
 
1372
    return OK;
 
1373
  }
 
1374
  else {
 
1375
    return FAIL;
 
1376
  };
 
1377
}
 
1378
 
 
1379
 
 
1380
/* simple wrapper for reading lines from sockets */
 
1381
int recv_line(int sock, uschar *buffer, int size) {
 
1382
  uschar *p = buffer;
 
1383
 
 
1384
  memset(buffer,0,size);
 
1385
  /* read until \n */
 
1386
  while(recv(sock,p,1,0) > -1) {
 
1387
    if ((p-buffer) > (size-2)) break;
 
1388
    if (*p == '\n') break;
 
1389
    if (*p != '\r') p++;
 
1390
  };
 
1391
  *p = '\0';
 
1392
 
 
1393
  return (p-buffer);
 
1394
}
 
1395
 
 
1396
 
 
1397
/* ============= private routines for the "mksd" scanner type ============== */
 
1398
 
 
1399
#include <sys/uio.h>
 
1400
 
 
1401
int mksd_writev (int sock, struct iovec *iov, int iovcnt)
 
1402
{
 
1403
  int i;
 
1404
 
 
1405
  for (;;) {
 
1406
    do
 
1407
      i = writev (sock, iov, iovcnt);
 
1408
    while ((i < 0) && (errno == EINTR));
 
1409
    if (i <= 0) {
 
1410
      close (sock);
 
1411
      log_write(0, LOG_MAIN|LOG_PANIC,
 
1412
                "malware acl condition: unable to write to mksd UNIX socket (/var/run/mksd/socket)");
 
1413
      return -1;
 
1414
    }
 
1415
 
 
1416
    for (;;)
 
1417
      if (i >= iov->iov_len) {
 
1418
        if (--iovcnt == 0)
 
1419
          return 0;
 
1420
        i -= iov->iov_len;
 
1421
        iov++;
 
1422
      } else {
 
1423
        iov->iov_len -= i;
 
1424
        iov->iov_base = CS iov->iov_base + i;
 
1425
        break;
 
1426
      }
 
1427
  }
 
1428
}
 
1429
 
 
1430
int mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
 
1431
{
 
1432
  int offset = 0;
 
1433
  int i;
 
1434
 
 
1435
  do {
 
1436
    if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
 
1437
      close (sock);
 
1438
      log_write(0, LOG_MAIN|LOG_PANIC,
 
1439
                "malware acl condition: unable to read from mksd UNIX socket (/var/run/mksd/socket)");
 
1440
      return -1;
 
1441
    }
 
1442
 
 
1443
    offset += i;
 
1444
    /* offset == av_buffer_size -> buffer full */
 
1445
    if (offset == av_buffer_size) {
 
1446
      close (sock);
 
1447
      log_write(0, LOG_MAIN|LOG_PANIC,
 
1448
                "malware acl condition: malformed reply received from mksd");
 
1449
      return -1;
 
1450
    }
 
1451
  } while (av_buffer[offset-1] != '\n');
 
1452
 
 
1453
  av_buffer[offset] = '\0';
 
1454
  return offset;
 
1455
}
 
1456
 
 
1457
int mksd_parse_line (char *line)
 
1458
{
 
1459
  char *p;
 
1460
 
 
1461
  switch (*line) {
 
1462
    case 'O':
 
1463
      /* OK */
 
1464
      return OK;
 
1465
    case 'E':
 
1466
    case 'A':
 
1467
      /* ERR */
 
1468
      if ((p = strchr (line, '\n')) != NULL)
 
1469
        (*p) = '\0';
 
1470
      log_write(0, LOG_MAIN|LOG_PANIC,
 
1471
                "malware acl condition: mksd scanner failed: %s", line);
 
1472
      return DEFER;
 
1473
    default:
 
1474
      /* VIR */
 
1475
      if ((p = strchr (line, '\n')) != NULL) {
 
1476
        (*p) = '\0';
 
1477
        if (((p-line) > 5) && ((p-line) < sizeof (malware_name_buffer)) && (line[3] == ' '))
 
1478
          if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
 
1479
            (*p) = '\0';
 
1480
            Ustrcpy (malware_name_buffer, line+4);
 
1481
      malware_name = malware_name_buffer;
 
1482
            return OK;
 
1483
          }
 
1484
      }
 
1485
      log_write(0, LOG_MAIN|LOG_PANIC,
 
1486
                "malware acl condition: malformed reply received from mksd: %s", line);
 
1487
      return DEFER;
 
1488
  }
 
1489
}
 
1490
 
 
1491
int mksd_scan_packed (int sock)
 
1492
{
 
1493
  struct iovec iov[7];
 
1494
  char *cmd = "MSQ/scan/.eml\n";
 
1495
  uschar av_buffer[1024];
 
1496
 
 
1497
  iov[0].iov_base = cmd;
 
1498
  iov[0].iov_len = 3;
 
1499
  iov[1].iov_base = CS spool_directory;
 
1500
  iov[1].iov_len = Ustrlen (spool_directory);
 
1501
  iov[2].iov_base = cmd + 3;
 
1502
  iov[2].iov_len = 6;
 
1503
  iov[3].iov_base = iov[5].iov_base = CS message_id;
 
1504
  iov[3].iov_len = iov[5].iov_len = Ustrlen (message_id);
 
1505
  iov[4].iov_base = cmd + 3;
 
1506
  iov[4].iov_len = 1;
 
1507
  iov[6].iov_base = cmd + 9;
 
1508
  iov[6].iov_len = 5;
 
1509
 
 
1510
  if (mksd_writev (sock, iov, 7) < 0)
 
1511
    return DEFER;
 
1512
 
 
1513
  if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
 
1514
    return DEFER;
 
1515
 
 
1516
  close (sock);
 
1517
 
 
1518
  return mksd_parse_line (CS av_buffer);
 
1519
}
 
1520
 
 
1521
#endif