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_fname
->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
;