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
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.
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.
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/>.
20 #include "vfs_virusfilter_common.h"
21 #include "vfs_virusfilter_utils.h"
23 /* Default values for standard "extra" configuration variables */
24 #ifdef SOPHOS_DEFAULT_SOCKET_PATH
25 # define VIRUSFILTER_DEFAULT_SOCKET_PATH SOPHOS_DEFAULT_SOCKET_PATH
27 # define VIRUSFILTER_DEFAULT_SOCKET_PATH "/var/run/savdi/sssp.sock"
30 static void virusfilter_sophos_scan_end(struct virusfilter_config *config);
32 /* Python's urllib.quote(string[, safe]) clone */
33 static int virusfilter_url_quote(const char *src, char *dst, int dst_size)
36 static char hex[] = "0123456789ABCDEF";
38 for (; *src != '\0'; src++) {
39 if ((*src < '0' && *src != '-' && *src != '.' && *src != '/') ||
40 (*src > '9' && *src < 'A') ||
41 (*src > 'Z' && *src < 'a' && *src != '_') ||
48 *dst_c++ = hex[(*src >> 4) & 0x0F];
49 *dst_c++ = hex[*src & 0x0F];
65 static int virusfilter_sophos_connect(
66 struct vfs_handle_struct *handle,
67 struct virusfilter_config *config,
71 virusfilter_io_set_readl_eol(config->io_h, "\x0D\x0A", 2);
76 static virusfilter_result virusfilter_sophos_scan_ping(
77 struct virusfilter_config *config)
79 struct virusfilter_io_handle *io_h = config->io_h;
84 /* SSSP/1.0 has no "PING" command */
85 ok = virusfilter_io_writel(io_h, "SSSP/1.0 OPTIONS\n", 17);
87 return VIRUSFILTER_RESULT_ERROR;
91 ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
93 return VIRUSFILTER_RESULT_ERROR;
95 ret = strcmp(reply, "");
103 return VIRUSFILTER_RESULT_OK;
106 static virusfilter_result virusfilter_sophos_scan_init(
107 struct virusfilter_config *config)
109 struct virusfilter_io_handle *io_h = config->io_h;
114 if (io_h->stream != NULL) {
115 DBG_DEBUG("SSSP: Checking if connection is alive\n");
117 ret = virusfilter_sophos_scan_ping(config);
118 if (ret == VIRUSFILTER_RESULT_OK)
120 DBG_DEBUG("SSSP: Re-using existent connection\n");
121 return VIRUSFILTER_RESULT_OK;
124 DBG_INFO("SSSP: Closing dead connection\n");
125 virusfilter_sophos_scan_end(config);
129 DBG_INFO("SSSP: Connecting to socket: %s\n",
130 config->socket_path);
133 ok = virusfilter_io_connect_path(io_h, config->socket_path);
137 DBG_ERR("SSSP: Connecting to socket failed: %s: %s\n",
138 config->socket_path, strerror(errno));
139 return VIRUSFILTER_RESULT_ERROR;
142 ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
144 DBG_ERR("SSSP: Reading greeting message failed: %s\n",
146 goto virusfilter_sophos_scan_init_failed;
148 ret = strncmp(reply, "OK SSSP/1.0", 11);
150 DBG_ERR("SSSP: Invalid greeting message: %s\n",
152 goto virusfilter_sophos_scan_init_failed;
155 DBG_DEBUG("SSSP: Connected\n");
157 DBG_INFO("SSSP: Configuring\n");
161 ok = virusfilter_io_writefl_readl(io_h, &reply,
162 "SSSP/1.0 OPTIONS\noutput:brief\nsavigrp:GrpArchiveUnpack %d\n",
163 config->scan_archive ? 1 : 0);
165 DBG_ERR("SSSP: OPTIONS: I/O error: %s\n", strerror(errno));
166 goto virusfilter_sophos_scan_init_failed;
168 ret = strncmp(reply, "ACC ", 4);
170 DBG_ERR("SSSP: OPTIONS: Not accepted: %s\n", reply);
171 goto virusfilter_sophos_scan_init_failed;
176 ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
178 DBG_ERR("SSSP: OPTIONS: Read error: %s\n", strerror(errno));
179 goto virusfilter_sophos_scan_init_failed;
181 ret = strncmp(reply, "DONE OK ", 8);
183 DBG_ERR("SSSP: OPTIONS failed: %s\n", reply);
184 goto virusfilter_sophos_scan_init_failed;
189 ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
191 DBG_ERR("SSSP: OPTIONS: Read error: %s\n", strerror(errno));
192 goto virusfilter_sophos_scan_init_failed;
194 ret = strcmp(reply, "");
196 DBG_ERR("SSSP: OPTIONS: Invalid reply: %s\n", reply);
197 goto virusfilter_sophos_scan_init_failed;
200 DBG_DEBUG("SSSP: Configured\n");
202 return VIRUSFILTER_RESULT_OK;
204 virusfilter_sophos_scan_init_failed:
208 virusfilter_sophos_scan_end(config);
210 return VIRUSFILTER_RESULT_ERROR;
213 static void virusfilter_sophos_scan_end(
214 struct virusfilter_config *config)
216 struct virusfilter_io_handle *io_h = config->io_h;
218 DBG_INFO("SSSP: Disconnecting\n");
220 virusfilter_io_disconnect(io_h);
223 static virusfilter_result virusfilter_sophos_scan(
224 struct vfs_handle_struct *handle,
225 struct virusfilter_config *config,
226 const struct files_struct *fsp,
229 char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
230 const char *fname = fsp->fsp_name->base_name;
231 char fileurl[VIRUSFILTER_IO_URL_MAX+1];
232 int fileurl_len, fileurl_len2;
233 struct virusfilter_io_handle *io_h = config->io_h;
234 virusfilter_result result = VIRUSFILTER_RESULT_ERROR;
237 char *reply_token = NULL, *reply_saveptr = NULL;
241 DBG_INFO("Scanning file: %s/%s\n", cwd_fname, fname);
243 fileurl_len = virusfilter_url_quote(cwd_fname, fileurl,
244 VIRUSFILTER_IO_URL_MAX);
245 if (fileurl_len < 0) {
246 DBG_ERR("virusfilter_url_quote failed: File path too long: "
247 "%s/%s\n", cwd_fname, fname);
248 result = VIRUSFILTER_RESULT_ERROR;
249 report = talloc_asprintf(talloc_tos(), "File path too long");
250 goto virusfilter_sophos_scan_return;
252 fileurl[fileurl_len] = '/';
255 fileurl_len += fileurl_len2 = virusfilter_url_quote(fname,
256 fileurl + fileurl_len, VIRUSFILTER_IO_URL_MAX - fileurl_len);
257 if (fileurl_len2 < 0) {
258 DBG_ERR("virusfilter_url_quote failed: File path too long: "
259 "%s/%s\n", cwd_fname, fname);
260 result = VIRUSFILTER_RESULT_ERROR;
261 report = talloc_asprintf(talloc_tos(), "File path too long");
262 goto virusfilter_sophos_scan_return;
264 fileurl_len += fileurl_len2;
266 ok = virusfilter_io_writevl(io_h, "SSSP/1.0 SCANFILE ", 18, fileurl,
269 DBG_ERR("SSSP: SCANFILE: Write error: %s\n",
271 goto virusfilter_sophos_scan_io_error;
274 ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
276 DBG_ERR("SSSP: SCANFILE: Read error: %s\n", strerror(errno));
277 goto virusfilter_sophos_scan_io_error;
279 ret = strncmp(reply, "ACC ", 4);
281 DBG_ERR("SSSP: SCANFILE: Not accepted: %s\n",
283 result = VIRUSFILTER_RESULT_ERROR;
284 goto virusfilter_sophos_scan_return;
289 result = VIRUSFILTER_RESULT_CLEAN;
291 ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
293 DBG_ERR("SSSP: SCANFILE: Read error: %s\n",
295 goto virusfilter_sophos_scan_io_error;
298 ret = strcmp(reply, "");
303 reply_token = strtok_r(reply, " ", &reply_saveptr);
305 if (strcmp(reply_token, "VIRUS") == 0) {
306 result = VIRUSFILTER_RESULT_INFECTED;
307 reply_token = strtok_r(NULL, " ", &reply_saveptr);
308 if (reply_token != NULL) {
309 report = talloc_strdup(talloc_tos(),
312 report = talloc_asprintf(talloc_tos(),
313 "UNKNOWN INFECTION");
315 } else if (strcmp(reply_token, "OK") == 0) {
318 } else if (strcmp(reply_token, "DONE") == 0) {
319 reply_token = strtok_r(NULL, "", &reply_saveptr);
320 if (reply_token != NULL &&
323 strncmp(reply_token, "OK 0000 ", 8) != 0 &&
326 strncmp(reply_token, "OK 0203 ", 8) != 0)
328 DBG_ERR("SSSP: SCANFILE: Error: %s\n",
330 result = VIRUSFILTER_RESULT_ERROR;
331 report = talloc_asprintf(talloc_tos(),
332 "Scanner error: %s\n",
336 DBG_ERR("SSSP: SCANFILE: Invalid reply: %s\n",
338 result = VIRUSFILTER_RESULT_ERROR;
339 report = talloc_asprintf(talloc_tos(), "Scanner "
340 "communication error");
346 virusfilter_sophos_scan_return:
349 if (report == NULL) {
350 *reportp = talloc_asprintf(talloc_tos(),
351 "Scanner report memory error");
358 virusfilter_sophos_scan_io_error:
359 *reportp = talloc_asprintf(talloc_tos(),
360 "Scanner I/O error: %s\n", strerror(errno));
365 static struct virusfilter_backend_fns virusfilter_backend_sophos ={
366 .connect = virusfilter_sophos_connect,
368 .scan_init = virusfilter_sophos_scan_init,
369 .scan = virusfilter_sophos_scan,
370 .scan_end = virusfilter_sophos_scan_end,
373 int virusfilter_sophos_init(struct virusfilter_config *config)
375 struct virusfilter_backend *backend = NULL;
377 if (config->socket_path == NULL) {
378 config->socket_path = VIRUSFILTER_DEFAULT_SOCKET_PATH;
381 backend = talloc_zero(config, struct virusfilter_backend);
382 if (backend == NULL) {
386 backend->fns = &virusfilter_backend_sophos;
387 backend->name = "sophos";
389 config->backend = backend;