Fix signed/unsigned and UTF errors Fixes: #901
[exim.git] / src / src / malware.c
index a790b284bc7b12e0f9802480d9ced489418622b7..9b1bd334d8f82b612aff0cd88f420478f087135d 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/malware.c,v 1.11 2005/07/01 10:49:02 ph10 Exp $ */
+/* $Cambridge: exim/src/src/malware.c,v 1.17 2009/11/05 19:40:51 nm4 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -20,6 +20,10 @@ int mksd_scan_packed(int sock);
 #define SHUT_WR 1
 #endif
 
+
+#define        MALWARE_TIMEOUT             120
+
+
 #define DRWEBD_SCAN_CMD             (1)     /* scan file, buffer or diskfile */
 #define DRWEBD_RETURN_VIRUSES       (1<<0)   /* ask daemon return to us viruses names from report */
 #define DRWEBD_IS_MAIL              (1<<19)  /* say to daemon that format is "archive MAIL" */
@@ -135,10 +139,117 @@ int malware(uschar **listptr) {
       return DEFER;
     };
 
+  /* "f-protd" scanner type ----------------------------------------------- */
+  if (strcmpic(scanner_name, US"f-protd") == 0) {
+    uschar *fp_options, *fp_scan_option;
+    uschar fp_scan_option_buffer[1024];
+    uschar fp_options_buffer[1024];
+    uschar fp_options_default[] = "localhost 10200-10204";
+    uschar hostname[256];
+    unsigned int port, portlow, porthigh, connect_ok=0, detected=0, par_count = 0;
+    struct hostent *he;
+    struct in_addr in;
+    int sock;
+    uschar scanrequest[2048], buf[32768], *strhelper, *strhelper2;
+
+    if ((fp_options = string_nextinlist(&av_scanner_work, &sep,
+      fp_options_buffer, sizeof(fp_options_buffer))) == NULL) {
+      /* no options supplied, use default options */
+      fp_options = fp_options_default;
+    };
+
+    /* extract host and port part */
+    if ( sscanf(CS fp_options, "%s %u-%u", hostname, &portlow, &porthigh) != 3 ) {
+      if ( sscanf(CS fp_options, "%s %u", hostname, &portlow) != 2 ) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+          "malware acl condition: f-protd: invalid socket '%s'", fp_options);
+        return DEFER;
+      }
+      porthigh = portlow;
+    }
+
+    /* Lookup the host */
+    if((he = gethostbyname(CS hostname)) == 0) {
+      log_write(0, LOG_MAIN|LOG_PANIC,
+        "malware acl condition: f-protd: failed to lookup host '%s'", hostname);
+      return DEFER;
+    }
+
+    in = *(struct in_addr *) he->h_addr_list[0];
+    port = portlow;
+
+
+    /* Open the f-protd TCP socket */
+    if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
+      log_write(0, LOG_MAIN|LOG_PANIC,
+        "malware acl condition: f-protd: unable to acquire socket (%s)",
+        strerror(errno));
+      return DEFER;
+    }
+
+    /* Try to connect to all portslow-high until connection is established */
+    for (port = portlow; !connect_ok && port < porthigh; port++) {
+      if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) >= 0) {
+        connect_ok = 1;
+      }
+    }
+
+    if ( !connect_ok ) {
+      log_write(0, LOG_MAIN|LOG_PANIC,
+        "malware acl condition: f-protd: connection to %s, port %u-%u failed (%s)",
+        inet_ntoa(in), portlow, porthigh, strerror(errno));
+      (void)close(sock);
+      return DEFER;
+    }
+
+    (void)string_format(scanrequest, 1024, CS"GET %s/scan/%s/%s.eml",
+          spool_directory, message_id, message_id);
+
+    while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
+      fp_scan_option_buffer, sizeof(fp_scan_option_buffer))) != NULL) {
+      if ( par_count ) {
+        Ustrcat(scanrequest, "%20");
+      } else {
+        Ustrcat(scanrequest, "?");
+      }
+      Ustrcat(scanrequest, fp_scan_option);
+      par_count++;
+    }
+    Ustrcat(scanrequest, " HTTP/1.0\r\n\r\n");
+
+    /* send scan request */
+    if (send(sock, &scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
+      (void)close(sock);
+      log_write(0, LOG_MAIN|LOG_PANIC,
+        "malware acl condition: f-protd: unable to send command to socket (%s)", scanrequest);
+      return DEFER;
+    }
+
+    /* We get a lot of empty lines, so we need this hack to check for any data at all */
+    while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
+      if ( recv_line(sock, buf, 32768) > 0) {
+        if ( Ustrstr(buf, US"<detected type=\"") != NULL ) {
+          detected = 1;
+        } else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
+          if (strhelper2 = (Ustrstr(buf, US"</name>"))) {
+            *strhelper2 = '\0';
+            Ustrcpy(malware_name_buffer, strhelper + 6);
+          }
+        } else if ( Ustrstr(buf, US"<summary code=\"") ) {
+          if ( Ustrstr(buf, US"<summary code=\"11\">") ) {
+            malware_name = malware_name_buffer;
+          } else {
+            malware_name = NULL;
+          }
+        }
+      }
+    }
+    (void)close(sock);
+  }
   /* "drweb" scanner type ----------------------------------------------- */
   /* v0.1 - added support for tcp sockets          */
   /* v0.0 - initial release -- support for unix sockets      */
-  if (strcmpic(scanner_name,US"drweb") == 0) {
+  else if (strcmpic(scanner_name,US"drweb") == 0) {
     uschar *drweb_options;
     uschar drweb_options_buffer[1024];
     uschar drweb_options_default[] = "/usr/local/drweb/run/drwebd.sock";
@@ -199,7 +310,7 @@ int malware(uschar **listptr) {
       /* prepare variables */
       drweb_cmd = htonl(DRWEBD_SCAN_CMD);
       drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
-      snprintf(CS scanrequest, 1024,CS"%s/scan/%s/%s.eml",
+      (void)string_format(scanrequest, 1024,CS"%s/scan/%s/%s.eml",
             spool_directory, message_id, message_id);
 
       /* calc file size */
@@ -287,7 +398,7 @@ int malware(uschar **listptr) {
       /* prepare variables */
       drweb_cmd = htonl(DRWEBD_SCAN_CMD);
       drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
-      snprintf(CS scanrequest, 1024,CS"%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
+      (void)string_format(scanrequest, 1024,CS"%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
       drweb_slen = htonl(Ustrlen(scanrequest));
 
       /* send scan request */
@@ -446,7 +557,7 @@ int malware(uschar **listptr) {
       };
 
       /* prepare our command */
-      snprintf(CS buf, 32768, "SCAN bPQRSTUW %s/scan/%s/%s.eml\r\n", spool_directory, message_id, message_id);
+      (void)string_format(buf, 32768, "SCAN bPQRSTUW %s/scan/%s/%s.eml\r\n", spool_directory, message_id, message_id);
 
       /* and send it */
       if (send(sock, buf, Ustrlen(buf), 0) < 0) {
@@ -479,7 +590,7 @@ int malware(uschar **listptr) {
       }
 
       /* prepare our command */
-      snprintf(CS buf, 32768, "quit\r\n");
+      (void)string_format(buf, 32768, "quit\r\n");
 
       /* and send it */
       if (send(sock, buf, Ustrlen(buf), 0) < 0) {
@@ -557,7 +668,7 @@ int malware(uschar **listptr) {
           return DEFER;
         };
 
-        bread = read(sock, av_buffer, sizeof(av_buffer));
+        bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
         if (bread >0) av_buffer[bread]='\0';
         if (bread < 0) {
           (void)close(sock);
@@ -571,7 +682,7 @@ int malware(uschar **listptr) {
       };
 
       /* pass the mailfile to fsecure */
-      snprintf(CS file_name,1024,"SCAN\t%s/scan/%s/%s.eml\n", spool_directory, message_id, message_id);
+      (void)string_format(file_name,1024,"SCAN\t%s/scan/%s/%s.eml\n", spool_directory, message_id, message_id);
       /* debug_printf("send scan %s",file_name); */
       if (write(sock, file_name, Ustrlen(file_name)) < 0) {
         (void)close(sock);
@@ -591,7 +702,7 @@ int malware(uschar **listptr) {
         i = 0;
         memset(av_buffer, 0, sizeof(av_buffer));
         do {
-          bread=read(sock, &av_buffer[i], 1);
+          bread=ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT);
           if (bread < 0) {
             (void)close(sock);
             log_write(0, LOG_MAIN|LOG_PANIC,
@@ -661,7 +772,7 @@ int malware(uschar **listptr) {
       /* get current date and time, build scan request */
       time(&t);
       strftime(CS tmpbuf, sizeof(tmpbuf), "<0>%d %b %H:%M:%S:%%s/scan/%%s", localtime(&t));
-      snprintf(CS scanrequest, 1024,CS tmpbuf, spool_directory, message_id);
+      (void)string_format(scanrequest, 1024,CS tmpbuf, spool_directory, message_id);
 
       /* send scan request */
       if (send(sock, scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
@@ -854,8 +965,8 @@ int malware(uschar **listptr) {
       };
 
       /* prepare scanner call */
-      snprintf(CS file_name,1024,"%s/scan/%s", spool_directory, message_id);
-      snprintf(CS commandline,1024, CS cmdline_scanner,file_name);
+      (void)string_format(file_name,1024,"%s/scan/%s", spool_directory, message_id);
+      (void)string_format(commandline,1024, CS cmdline_scanner,file_name);
       /* redirect STDERR too */
       Ustrcat(commandline," 2>&1");
 
@@ -872,8 +983,8 @@ int malware(uschar **listptr) {
         return DEFER;
       };
 
-      snprintf(CS file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
-      scanner_record = fopen(CS file_name,"wb");
+      (void)string_format(file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
+      scanner_record = modefopen(file_name,"wb",SPOOL_MODE);
 
       if (scanner_record == NULL) {
         log_write(0, LOG_MAIN|LOG_PANIC,
@@ -964,7 +1075,7 @@ int malware(uschar **listptr) {
       }
 
       /* pass the scan directory to sophie */
-      snprintf(CS file_name,1024,"%s/scan/%s", spool_directory, message_id);
+      (void)string_format(file_name,1024,"%s/scan/%s", spool_directory, message_id);
       if (write(sock, file_name, Ustrlen(file_name)) < 0) {
         (void)close(sock);
         log_write(0, LOG_MAIN|LOG_PANIC,
@@ -976,7 +1087,7 @@ int malware(uschar **listptr) {
 
       /* wait for result */
       memset(av_buffer, 0, sizeof(av_buffer));
-      if ((!(bread = read(sock, av_buffer, sizeof(av_buffer))) > 0)) {
+      if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0)) {
         (void)close(sock);
         log_write(0, LOG_MAIN|LOG_PANIC,
              "malware acl condition: unable to read from sophie UNIX socket (%s)", sophie_options);
@@ -1079,7 +1190,7 @@ int malware(uschar **listptr) {
 
       /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
 
-          snprintf(CS file_name,1024,"SCAN %s/scan/%s\n", spool_directory, message_id);
+          (void)string_format(file_name,1024,"SCAN %s/scan/%s\n", spool_directory, message_id);
 
           if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
             (void)close(sock);
@@ -1098,7 +1209,7 @@ int malware(uschar **listptr) {
             return DEFER;
           }
           memset(av_buffer2, 0, sizeof(av_buffer2));
-          bread = read(sock, av_buffer2, sizeof(av_buffer2));
+          bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
 
           if (bread < 0) {
             log_write(0, LOG_MAIN|LOG_PANIC,
@@ -1141,7 +1252,7 @@ int malware(uschar **listptr) {
             return DEFER;
           }
 
-    snprintf(CS scanrequest, 1024,CS"%s/scan/%s/%s.eml",
+      (void)string_format(scanrequest, 1024,CS"%s/scan/%s/%s.eml",
       spool_directory, message_id, message_id);
 
     /* calc file size */
@@ -1218,7 +1329,7 @@ int malware(uschar **listptr) {
 
       /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
 
-      snprintf(CS file_name,1024,"SCAN %s/scan/%s\n", spool_directory, message_id);
+      (void)string_format(file_name,1024,"SCAN %s/scan/%s\n", spool_directory, message_id);
 
       if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
         (void)close(sock);
@@ -1238,7 +1349,7 @@ int malware(uschar **listptr) {
 
       /* Read the result */
       memset(av_buffer, 0, sizeof(av_buffer));
-      bread = read(sock, av_buffer, sizeof(av_buffer));
+      bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
       (void)close(sock);
 
       if (!(bread  > 0)) {
@@ -1265,6 +1376,10 @@ int malware(uschar **listptr) {
         return DEFER;
       }
 
+      /* strip newline at the end */
+      p = av_buffer + Ustrlen(av_buffer) - 1;
+      if( *p == '\n' ) *p = '\0';
+
       /* colon in returned output? */
       if((p = Ustrrchr(av_buffer,':')) == NULL) {
         log_write(0, LOG_MAIN|LOG_PANIC,
@@ -1273,13 +1388,10 @@ int malware(uschar **listptr) {
         return DEFER;
       }
 
-      /* strip filename strip CR at the end */
+      /* strip filename */
       ++p;
       while (*p == ' ') ++p;
       vname = p;
-      p = vname + Ustrlen(vname) - 1;
-      if( *p == '\n' ) *p = '\0';
-
       if ((p = Ustrstr(vname, "FOUND"))!=NULL) {
            *p=0;
            for (--p;p>vname && *p<=32;p--) *p=0;