LCOV - code coverage report
Current view: top level - source3/modules - vfs_virusfilter_sophos.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 0 191 0.0 %
Date: 2024-05-31 13:13:24 Functions: 0 7 0.0 %

          Line data    Source code
       1             : /*
       2             :    Samba-VirusFilter VFS modules
       3             :    Sophos Anti-Virus savdid (SSSP/1.0) support
       4             :    Copyright (C) 2010-2016 SATOH Fumiyasu @ OSS Technology Corp., Japan
       5             : 
       6             :    This program is free software; you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : #include "vfs_virusfilter_common.h"
      21             : #include "vfs_virusfilter_utils.h"
      22             : 
      23             : /* Default values for standard "extra" configuration variables */
      24             : #ifdef SOPHOS_DEFAULT_SOCKET_PATH
      25             : #  define VIRUSFILTER_DEFAULT_SOCKET_PATH       SOPHOS_DEFAULT_SOCKET_PATH
      26             : #else
      27             : #  define VIRUSFILTER_DEFAULT_SOCKET_PATH       "/var/run/savdi/sssp.sock"
      28             : #endif
      29             : 
      30             : static void virusfilter_sophos_scan_end(struct virusfilter_config *config);
      31             : 
      32             : /* Python's urllib.quote(string[, safe]) clone */
      33           0 : static int virusfilter_url_quote(const char *src, char *dst, int dst_size)
      34             : {
      35           0 :         char *dst_c = dst;
      36             :         static char hex[] = "0123456789ABCDEF";
      37             : 
      38           0 :         for (; *src != '\0'; src++) {
      39           0 :                 if ((*src < '0' && *src != '-' && *src != '.' && *src != '/') ||
      40           0 :                     (*src > '9' && *src < 'A') ||
      41           0 :                     (*src > 'Z' && *src < 'a' && *src != '_') ||
      42           0 :                     (*src > 'z'))
      43             :                 {
      44           0 :                         if (dst_size < 4) {
      45           0 :                                 return -1;
      46             :                         }
      47           0 :                         *dst_c++ = '%';
      48           0 :                         *dst_c++ = hex[(*src >> 4) & 0x0F];
      49           0 :                         *dst_c++ = hex[*src & 0x0F];
      50           0 :                         dst_size -= 3;
      51             :                 } else {
      52           0 :                         if (dst_size < 2) {
      53           0 :                                 return -1;
      54             :                         }
      55           0 :                         *dst_c++ = *src;
      56           0 :                         dst_size--;
      57             :                 }
      58             :         }
      59             : 
      60           0 :         *dst_c = '\0';
      61             : 
      62           0 :         return (dst_c - dst);
      63             : }
      64             : 
      65           0 : static int virusfilter_sophos_connect(
      66             :         struct vfs_handle_struct *handle,
      67             :         struct virusfilter_config *config,
      68             :         const char *svc,
      69             :         const char *user)
      70             : {
      71           0 :         virusfilter_io_set_readl_eol(config->io_h, "\x0D\x0A", 2);
      72             : 
      73           0 :         return 0;
      74             : }
      75             : 
      76           0 : static virusfilter_result virusfilter_sophos_scan_ping(
      77             :         struct virusfilter_config *config)
      78             : {
      79           0 :         struct virusfilter_io_handle *io_h = config->io_h;
      80           0 :         char *reply = NULL;
      81             :         bool ok;
      82             :         int ret;
      83             : 
      84             :         /* SSSP/1.0 has no "PING" command */
      85           0 :         ok = virusfilter_io_writel(io_h, "SSSP/1.0 OPTIONS\n", 17);
      86           0 :         if (!ok) {
      87           0 :                 return VIRUSFILTER_RESULT_ERROR;
      88             :         }
      89             : 
      90             :         for (;;) {
      91           0 :                 ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
      92           0 :                 if (!ok) {
      93           0 :                         return VIRUSFILTER_RESULT_ERROR;
      94             :                 }
      95           0 :                 ret = strcmp(reply, "");
      96           0 :                 if (ret == 0) {
      97           0 :                         break;
      98             :                 }
      99           0 :                 TALLOC_FREE(reply);
     100             :         }
     101             : 
     102           0 :         TALLOC_FREE(reply);
     103           0 :         return VIRUSFILTER_RESULT_OK;
     104             : }
     105             : 
     106           0 : static virusfilter_result virusfilter_sophos_scan_init(
     107             :         struct virusfilter_config *config)
     108             : {
     109           0 :         struct virusfilter_io_handle *io_h = config->io_h;
     110           0 :         char *reply = NULL;
     111             :         int ret;
     112             :         bool ok;
     113             : 
     114           0 :         if (io_h->stream != NULL) {
     115           0 :                 DBG_DEBUG("SSSP: Checking if connection is alive\n");
     116             : 
     117           0 :                 ret = virusfilter_sophos_scan_ping(config);
     118           0 :                 if (ret == VIRUSFILTER_RESULT_OK)
     119             :                 {
     120           0 :                         DBG_DEBUG("SSSP: Re-using existent connection\n");
     121           0 :                         return VIRUSFILTER_RESULT_OK;
     122             :                 }
     123             : 
     124           0 :                 DBG_INFO("SSSP: Closing dead connection\n");
     125           0 :                 virusfilter_sophos_scan_end(config);
     126             :         }
     127             : 
     128             : 
     129           0 :         DBG_INFO("SSSP: Connecting to socket: %s\n",
     130             :                 config->socket_path);
     131             : 
     132           0 :         become_root();
     133           0 :         ok = virusfilter_io_connect_path(io_h, config->socket_path);
     134           0 :         unbecome_root();
     135             : 
     136           0 :         if (!ok) {
     137           0 :                 DBG_ERR("SSSP: Connecting to socket failed: %s: %s\n",
     138             :                         config->socket_path, strerror(errno));
     139           0 :                 return VIRUSFILTER_RESULT_ERROR;
     140             :         }
     141             : 
     142           0 :         ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
     143           0 :         if (!ok) {
     144           0 :                 DBG_ERR("SSSP: Reading greeting message failed: %s\n",
     145             :                         strerror(errno));
     146           0 :                 goto virusfilter_sophos_scan_init_failed;
     147             :         }
     148           0 :         ret = strncmp(reply, "OK SSSP/1.0", 11);
     149           0 :         if (ret != 0) {
     150           0 :                 DBG_ERR("SSSP: Invalid greeting message: %s\n",
     151             :                         reply);
     152           0 :                 goto virusfilter_sophos_scan_init_failed;
     153             :         }
     154             : 
     155           0 :         DBG_DEBUG("SSSP: Connected\n");
     156             : 
     157           0 :         DBG_INFO("SSSP: Configuring\n");
     158             : 
     159           0 :         TALLOC_FREE(reply);
     160             : 
     161           0 :         ok = virusfilter_io_writefl_readl(io_h, &reply,
     162             :             "SSSP/1.0 OPTIONS\noutput:brief\nsavigrp:GrpArchiveUnpack %d\n",
     163           0 :             config->scan_archive ? 1 : 0);
     164           0 :         if (!ok) {
     165           0 :                 DBG_ERR("SSSP: OPTIONS: I/O error: %s\n", strerror(errno));
     166           0 :                 goto virusfilter_sophos_scan_init_failed;
     167             :         }
     168           0 :         ret = strncmp(reply, "ACC ", 4);
     169           0 :         if (ret != 0) {
     170           0 :                 DBG_ERR("SSSP: OPTIONS: Not accepted: %s\n", reply);
     171           0 :                 goto virusfilter_sophos_scan_init_failed;
     172             :         }
     173             : 
     174           0 :         TALLOC_FREE(reply);
     175             : 
     176           0 :         ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
     177           0 :         if (!ok) {
     178           0 :                 DBG_ERR("SSSP: OPTIONS: Read error: %s\n", strerror(errno));
     179           0 :                 goto virusfilter_sophos_scan_init_failed;
     180             :         }
     181           0 :         ret = strncmp(reply, "DONE OK ", 8);
     182           0 :         if (ret != 0) {
     183           0 :                 DBG_ERR("SSSP: OPTIONS failed: %s\n", reply);
     184           0 :                 goto virusfilter_sophos_scan_init_failed;
     185             :         }
     186             : 
     187           0 :         TALLOC_FREE(reply);
     188             : 
     189           0 :         ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
     190           0 :         if (!ok) {
     191           0 :                 DBG_ERR("SSSP: OPTIONS: Read error: %s\n", strerror(errno));
     192           0 :                 goto virusfilter_sophos_scan_init_failed;
     193             :         }
     194           0 :         ret = strcmp(reply, "");
     195           0 :         if (ret != 0) {
     196           0 :                 DBG_ERR("SSSP: OPTIONS: Invalid reply: %s\n", reply);
     197           0 :                 goto virusfilter_sophos_scan_init_failed;
     198             :         }
     199             : 
     200           0 :         DBG_DEBUG("SSSP: Configured\n");
     201             : 
     202           0 :         return VIRUSFILTER_RESULT_OK;
     203             : 
     204           0 : virusfilter_sophos_scan_init_failed:
     205             : 
     206           0 :         TALLOC_FREE(reply);
     207             : 
     208           0 :         virusfilter_sophos_scan_end(config);
     209             : 
     210           0 :         return VIRUSFILTER_RESULT_ERROR;
     211             : }
     212             : 
     213           0 : static void virusfilter_sophos_scan_end(
     214             :         struct virusfilter_config *config)
     215             : {
     216           0 :         struct virusfilter_io_handle *io_h = config->io_h;
     217             : 
     218           0 :         DBG_INFO("SSSP: Disconnecting\n");
     219             : 
     220           0 :         virusfilter_io_disconnect(io_h);
     221           0 : }
     222             : 
     223           0 : static virusfilter_result virusfilter_sophos_scan(
     224             :         struct vfs_handle_struct *handle,
     225             :         struct virusfilter_config *config,
     226             :         const struct files_struct *fsp,
     227             :         char **reportp)
     228             : {
     229           0 :         char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
     230           0 :         const char *fname = fsp->fsp_name->base_name;
     231             :         char fileurl[VIRUSFILTER_IO_URL_MAX+1];
     232             :         int fileurl_len, fileurl_len2;
     233           0 :         struct virusfilter_io_handle *io_h = config->io_h;
     234           0 :         virusfilter_result result = VIRUSFILTER_RESULT_ERROR;
     235           0 :         char *report = NULL;
     236           0 :         char *reply = NULL;
     237           0 :         char *reply_token = NULL, *reply_saveptr = NULL;
     238             :         int ret;
     239             :         bool ok;
     240             : 
     241           0 :         DBG_INFO("Scanning file: %s/%s\n", cwd_fname, fname);
     242             : 
     243           0 :         fileurl_len = virusfilter_url_quote(cwd_fname, fileurl,
     244             :                                             VIRUSFILTER_IO_URL_MAX);
     245           0 :         if (fileurl_len < 0) {
     246           0 :                 DBG_ERR("virusfilter_url_quote failed: File path too long: "
     247             :                         "%s/%s\n", cwd_fname, fname);
     248           0 :                 result = VIRUSFILTER_RESULT_ERROR;
     249           0 :                 report = talloc_asprintf(talloc_tos(), "File path too long");
     250           0 :                 goto virusfilter_sophos_scan_return;
     251             :         }
     252           0 :         fileurl[fileurl_len] = '/';
     253           0 :         fileurl_len++;
     254             : 
     255           0 :         fileurl_len += fileurl_len2 = virusfilter_url_quote(fname,
     256             :                 fileurl + fileurl_len, VIRUSFILTER_IO_URL_MAX - fileurl_len);
     257           0 :         if (fileurl_len2 < 0) {
     258           0 :                 DBG_ERR("virusfilter_url_quote failed: File path too long: "
     259             :                         "%s/%s\n", cwd_fname, fname);
     260           0 :                 result = VIRUSFILTER_RESULT_ERROR;
     261           0 :                 report = talloc_asprintf(talloc_tos(), "File path too long");
     262           0 :                 goto virusfilter_sophos_scan_return;
     263             :         }
     264           0 :         fileurl_len += fileurl_len2;
     265             : 
     266           0 :         ok = virusfilter_io_writevl(io_h, "SSSP/1.0 SCANFILE ", 18, fileurl,
     267             :                                     fileurl_len, NULL);
     268           0 :         if (!ok) {
     269           0 :                 DBG_ERR("SSSP: SCANFILE: Write error: %s\n",
     270             :                       strerror(errno));
     271           0 :                 goto virusfilter_sophos_scan_io_error;
     272             :         }
     273             : 
     274           0 :         ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
     275           0 :         if (!ok) {
     276           0 :                 DBG_ERR("SSSP: SCANFILE: Read error: %s\n", strerror(errno));
     277           0 :                 goto virusfilter_sophos_scan_io_error;
     278             :         }
     279           0 :         ret = strncmp(reply, "ACC ", 4);
     280           0 :         if (ret != 0) {
     281           0 :                 DBG_ERR("SSSP: SCANFILE: Not accepted: %s\n",
     282             :                         reply);
     283           0 :                 result = VIRUSFILTER_RESULT_ERROR;
     284           0 :                 goto virusfilter_sophos_scan_return;
     285             :         }
     286             : 
     287           0 :         TALLOC_FREE(reply);
     288             : 
     289           0 :         result = VIRUSFILTER_RESULT_CLEAN;
     290             :         for (;;) {
     291           0 :                 ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
     292           0 :                 if (!ok) {
     293           0 :                         DBG_ERR("SSSP: SCANFILE: Read error: %s\n",
     294             :                                 strerror(errno));
     295           0 :                         goto virusfilter_sophos_scan_io_error;
     296             :                 }
     297             : 
     298           0 :                 ret = strcmp(reply, "");
     299           0 :                 if (ret == 0) {
     300           0 :                         break;
     301             :                 }
     302             : 
     303           0 :                 reply_token = strtok_r(reply, " ", &reply_saveptr);
     304             : 
     305           0 :                 if (strcmp(reply_token, "VIRUS") == 0) {
     306           0 :                         result = VIRUSFILTER_RESULT_INFECTED;
     307           0 :                         reply_token = strtok_r(NULL, " ", &reply_saveptr);
     308           0 :                         if (reply_token != NULL) {
     309           0 :                                   report = talloc_strdup(talloc_tos(),
     310             :                                                          reply_token);
     311             :                         } else {
     312           0 :                                   report = talloc_asprintf(talloc_tos(),
     313             :                                                         "UNKNOWN INFECTION");
     314             :                         }
     315           0 :                 } else if (strcmp(reply_token, "OK") == 0) {
     316             : 
     317             :                         /* Ignore */
     318           0 :                 } else if (strcmp(reply_token, "DONE") == 0) {
     319           0 :                         reply_token = strtok_r(NULL, "", &reply_saveptr);
     320           0 :                         if (reply_token != NULL &&
     321             : 
     322             :                             /* Succeed */
     323           0 :                             strncmp(reply_token, "OK 0000 ", 8) != 0 &&
     324             : 
     325             :                             /* Infected */
     326           0 :                             strncmp(reply_token, "OK 0203 ", 8) != 0)
     327             :                         {
     328           0 :                                 DBG_ERR("SSSP: SCANFILE: Error: %s\n",
     329             :                                         reply_token);
     330           0 :                                 result = VIRUSFILTER_RESULT_ERROR;
     331           0 :                                 report = talloc_asprintf(talloc_tos(),
     332             :                                                          "Scanner error: %s\n",
     333             :                                                          reply_token);
     334             :                         }
     335             :                 } else {
     336           0 :                         DBG_ERR("SSSP: SCANFILE: Invalid reply: %s\n",
     337             :                                 reply_token);
     338           0 :                         result = VIRUSFILTER_RESULT_ERROR;
     339           0 :                         report = talloc_asprintf(talloc_tos(), "Scanner "
     340             :                                                  "communication error");
     341             :                 }
     342             : 
     343           0 :                 TALLOC_FREE(reply);
     344             :         }
     345             : 
     346           0 : virusfilter_sophos_scan_return:
     347           0 :         TALLOC_FREE(reply);
     348             : 
     349           0 :         if (report == NULL) {
     350           0 :                 *reportp = talloc_asprintf(talloc_tos(),
     351             :                                            "Scanner report memory error");
     352             :         } else {
     353           0 :                 *reportp = report;
     354             :         }
     355             : 
     356           0 :         return result;
     357             : 
     358           0 : virusfilter_sophos_scan_io_error:
     359           0 :         *reportp = talloc_asprintf(talloc_tos(),
     360           0 :                                    "Scanner I/O error: %s\n", strerror(errno));
     361             : 
     362           0 :         return result;
     363             : }
     364             : 
     365             : static struct virusfilter_backend_fns virusfilter_backend_sophos ={
     366             :         .connect = virusfilter_sophos_connect,
     367             :         .disconnect = NULL,
     368             :         .scan_init = virusfilter_sophos_scan_init,
     369             :         .scan = virusfilter_sophos_scan,
     370             :         .scan_end = virusfilter_sophos_scan_end,
     371             : };
     372             : 
     373           0 : int virusfilter_sophos_init(struct virusfilter_config *config)
     374             : {
     375           0 :         struct virusfilter_backend *backend = NULL;
     376             : 
     377           0 :         if (config->socket_path == NULL) {
     378           0 :                 config->socket_path = VIRUSFILTER_DEFAULT_SOCKET_PATH;
     379             :         }
     380             : 
     381           0 :         backend = talloc_zero(config, struct virusfilter_backend);
     382           0 :         if (backend == NULL) {
     383           0 :                 return -1;
     384             :         }
     385             : 
     386           0 :         backend->fns = &virusfilter_backend_sophos;
     387           0 :         backend->name = "sophos";
     388             : 
     389           0 :         config->backend = backend;
     390           0 :         return 0;
     391             : }

Generated by: LCOV version 1.14