Line data Source code
1 : /*
2 : Samba-VirusFilter VFS modules
3 : F-Secure Anti-Virus fsavd 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 : #ifdef FSAV_DEFAULT_SOCKET_PATH
24 : # define VIRUSFILTER_DEFAULT_SOCKET_PATH FSAV_DEFAULT_SOCKET_PATH
25 : #else
26 : # define VIRUSFILTER_DEFAULT_SOCKET_PATH "/tmp/.fsav-0"
27 : #endif
28 :
29 : /* Default values for module-specific configuration variables */
30 : /* 5 = F-Secure Linux 7 or later? */
31 :
32 : #define VIRUSFILTER_DEFAULT_FSAV_PROTOCOL 5
33 : #define VIRUSFILTER_DEFAULT_SCAN_RISKWARE false
34 : #define VIRUSFILTER_DEFAULT_STOP_SCAN_ON_FIRST true
35 : #define VIRUSFILTER_DEFAULT_FILTER_FILENAME false
36 :
37 : struct virusfilter_fsav_config {
38 : /* Backpointer */
39 : struct virusfilter_config *config;
40 :
41 : int fsav_protocol;
42 : bool scan_riskware;
43 : bool stop_scan_on_first;
44 : bool filter_filename;
45 : };
46 :
47 : static void virusfilter_fsav_scan_end(struct virusfilter_config *config);
48 :
49 0 : static int virusfilter_fsav_destruct_config(
50 : struct virusfilter_fsav_config *fsav_config)
51 : {
52 0 : virusfilter_fsav_scan_end(fsav_config->config);
53 0 : return 0;
54 : }
55 :
56 0 : static int virusfilter_fsav_connect(
57 : struct vfs_handle_struct *handle,
58 : struct virusfilter_config *config,
59 : const char *svc,
60 : const char *user)
61 : {
62 0 : int snum = SNUM(handle->conn);
63 0 : struct virusfilter_fsav_config *fsav_config = NULL;
64 :
65 0 : fsav_config = talloc_zero(config->backend,
66 : struct virusfilter_fsav_config);
67 0 : if (fsav_config == NULL) {
68 0 : return -1;
69 : }
70 :
71 0 : fsav_config->config = config;
72 :
73 0 : fsav_config->fsav_protocol = lp_parm_int(
74 : snum, "virusfilter", "fsav protocol",
75 : VIRUSFILTER_DEFAULT_FSAV_PROTOCOL);
76 :
77 0 : fsav_config->scan_riskware = lp_parm_bool(
78 : snum, "virusfilter", "scan riskware",
79 : VIRUSFILTER_DEFAULT_SCAN_RISKWARE);
80 :
81 0 : fsav_config->stop_scan_on_first = lp_parm_bool(
82 : snum, "virusfilter", "stop scan on first",
83 : VIRUSFILTER_DEFAULT_STOP_SCAN_ON_FIRST);
84 :
85 0 : fsav_config->filter_filename = lp_parm_bool(
86 : snum, "virusfilter", "filter filename",
87 : VIRUSFILTER_DEFAULT_FILTER_FILENAME);
88 :
89 0 : talloc_set_destructor(fsav_config, virusfilter_fsav_destruct_config);
90 :
91 0 : config->backend->backend_private = fsav_config;
92 :
93 0 : config->block_suspected_file = lp_parm_bool(
94 : snum, "virusfilter", "block suspected file", false);
95 :
96 0 : return 0;
97 : }
98 :
99 0 : static virusfilter_result virusfilter_fsav_scan_init(
100 : struct virusfilter_config *config)
101 : {
102 0 : struct virusfilter_fsav_config *fsav_config = NULL;
103 0 : struct virusfilter_io_handle *io_h = config->io_h;
104 0 : char *reply = NULL;
105 : bool ok;
106 : int ret;
107 :
108 0 : fsav_config = talloc_get_type_abort(config->backend->backend_private,
109 : struct virusfilter_fsav_config);
110 :
111 0 : if (io_h->stream != NULL) {
112 0 : DBG_DEBUG("fsavd: Checking if connection is alive\n");
113 :
114 : /* FIXME: I don't know the correct PING command format... */
115 0 : ok = virusfilter_io_writefl_readl(io_h, &reply, "PING");
116 0 : if (ok) {
117 0 : ret = strncmp(reply, "ERROR\t", 6);
118 0 : if (ret == 0) {
119 0 : DBG_DEBUG("fsavd: Re-using existent "
120 : "connection\n");
121 0 : goto virusfilter_fsav_init_succeed;
122 : }
123 : }
124 :
125 0 : DBG_DEBUG("fsavd: Closing dead connection\n");
126 0 : virusfilter_fsav_scan_end(config);
127 : }
128 :
129 0 : DBG_INFO("fsavd: 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("fsavd: Connecting to socket failed: %s: %s\n",
138 : config->socket_path, strerror(errno));
139 0 : goto virusfilter_fsav_init_failed;
140 : }
141 :
142 0 : TALLOC_FREE(reply);
143 :
144 0 : ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
145 0 : if (!ok) {
146 0 : DBG_ERR("fsavd: Reading greeting message failed: %s\n",
147 : strerror(errno));
148 0 : goto virusfilter_fsav_init_failed;
149 : }
150 0 : ret = strncmp(reply, "DBVERSION\t", 10);
151 0 : if (ret != 0) {
152 0 : DBG_ERR("fsavd: Invalid greeting message: %s\n",
153 : reply);
154 0 : goto virusfilter_fsav_init_failed;
155 : }
156 :
157 0 : DBG_DEBUG("fsavd: Connected\n");
158 :
159 0 : DBG_INFO("fsavd: Configuring\n");
160 :
161 0 : TALLOC_FREE(reply);
162 :
163 0 : ok = virusfilter_io_writefl_readl(io_h, &reply, "PROTOCOL\t%d",
164 : fsav_config->fsav_protocol);
165 0 : if (!ok) {
166 0 : DBG_ERR("fsavd: PROTOCOL: I/O error: %s\n", strerror(errno));
167 0 : goto virusfilter_fsav_init_failed;
168 : }
169 0 : ret = strncmp(reply, "OK\t", 3);
170 0 : if (ret != 0) {
171 0 : DBG_ERR("fsavd: PROTOCOL: Not accepted: %s\n",
172 : reply);
173 0 : goto virusfilter_fsav_init_failed;
174 : }
175 :
176 0 : TALLOC_FREE(reply);
177 :
178 0 : ok = virusfilter_io_writefl_readl(io_h, &reply,
179 : "CONFIGURE\tSTOPONFIRST\t%d",
180 0 : fsav_config->stop_scan_on_first ?
181 : 1 : 0);
182 0 : if (!ok) {
183 0 : DBG_ERR("fsavd: CONFIGURE STOPONFIRST: I/O error: %s\n",
184 : strerror(errno));
185 0 : goto virusfilter_fsav_init_failed;
186 : }
187 0 : ret = strncmp(reply, "OK\t", 3);
188 0 : if (ret != 0) {
189 0 : DBG_ERR("fsavd: CONFIGURE STOPONFIRST: Not accepted: %s\n",
190 : reply);
191 0 : goto virusfilter_fsav_init_failed;
192 : }
193 :
194 0 : TALLOC_FREE(reply);
195 :
196 0 : ok = virusfilter_io_writefl_readl(io_h, &reply, "CONFIGURE\tFILTER\t%d",
197 0 : fsav_config->filter_filename ? 1 : 0);
198 0 : if (!ok) {
199 0 : DBG_ERR("fsavd: CONFIGURE FILTER: I/O error: %s\n",
200 : strerror(errno));
201 0 : goto virusfilter_fsav_init_failed;
202 : }
203 0 : ret = strncmp(reply, "OK\t", 3);
204 0 : if (ret != 0) {
205 0 : DBG_ERR("fsavd: CONFIGURE FILTER: Not accepted: %s\n",
206 : reply);
207 0 : goto virusfilter_fsav_init_failed;
208 : }
209 :
210 0 : TALLOC_FREE(reply);
211 :
212 0 : ok = virusfilter_io_writefl_readl(io_h, &reply,
213 : "CONFIGURE\tARCHIVE\t%d",
214 0 : config->scan_archive ? 1 : 0);
215 0 : if (!ok) {
216 0 : DBG_ERR("fsavd: CONFIGURE ARCHIVE: I/O error: %s\n",
217 : strerror(errno));
218 0 : goto virusfilter_fsav_init_failed;
219 : }
220 0 : ret = strncmp(reply, "OK\t", 3);
221 0 : if (ret != 0) {
222 0 : DBG_ERR("fsavd: CONFIGURE ARCHIVE: Not accepted: %s\n",
223 : reply);
224 0 : goto virusfilter_fsav_init_failed;
225 : }
226 :
227 0 : TALLOC_FREE(reply);
228 :
229 0 : ok = virusfilter_io_writefl_readl(io_h, &reply,
230 : "CONFIGURE\tMAXARCH\t%d",
231 : config->max_nested_scan_archive);
232 0 : if (!ok) {
233 0 : DBG_ERR("fsavd: CONFIGURE MAXARCH: I/O error: %s\n",
234 : strerror(errno));
235 0 : goto virusfilter_fsav_init_failed;
236 : }
237 0 : ret = strncmp(reply, "OK\t", 3);
238 0 : if (ret != 0) {
239 0 : DBG_ERR("fsavd: CONFIGURE MAXARCH: Not accepted: %s\n",
240 : reply);
241 0 : goto virusfilter_fsav_init_failed;
242 : }
243 :
244 0 : TALLOC_FREE(reply);
245 :
246 0 : ok = virusfilter_io_writefl_readl(io_h, &reply,
247 : "CONFIGURE\tMIME\t%d",
248 0 : config->scan_mime ? 1 : 0);
249 0 : if (!ok) {
250 0 : DBG_ERR("fsavd: CONFIGURE MIME: I/O error: %s\n",
251 : strerror(errno));
252 0 : goto virusfilter_fsav_init_failed;
253 : }
254 0 : ret = strncmp(reply, "OK\t", 3);
255 0 : if (ret != 0) {
256 0 : DBG_ERR("fsavd: CONFIGURE MIME: Not accepted: %s\n",
257 : reply);
258 0 : goto virusfilter_fsav_init_failed;
259 : }
260 :
261 0 : TALLOC_FREE(reply);
262 :
263 0 : ok = virusfilter_io_writefl_readl(io_h, &reply, "CONFIGURE\tRISKWARE\t%d",
264 0 : fsav_config->scan_riskware ? 1 : 0);
265 0 : if (!ok) {
266 0 : DBG_ERR("fsavd: CONFIGURE RISKWARE: I/O error: %s\n",
267 : strerror(errno));
268 0 : goto virusfilter_fsav_init_failed;
269 : }
270 0 : ret = strncmp(reply, "OK\t", 3);
271 0 : if (ret != 0) {
272 0 : DBG_ERR("fsavd: CONFIGURE RISKWARE: Not accepted: %s\n",
273 : reply);
274 0 : goto virusfilter_fsav_init_failed;
275 : }
276 :
277 0 : DBG_DEBUG("fsavd: Configured\n");
278 :
279 0 : virusfilter_fsav_init_succeed:
280 0 : TALLOC_FREE(reply);
281 0 : return VIRUSFILTER_RESULT_OK;
282 :
283 0 : virusfilter_fsav_init_failed:
284 0 : TALLOC_FREE(reply);
285 0 : virusfilter_fsav_scan_end(config);
286 :
287 0 : return VIRUSFILTER_RESULT_ERROR;
288 : }
289 :
290 0 : static void virusfilter_fsav_scan_end(struct virusfilter_config *config)
291 : {
292 0 : struct virusfilter_io_handle *io_h = config->io_h;
293 :
294 0 : DBG_INFO("fsavd: Disconnecting\n");
295 0 : virusfilter_io_disconnect(io_h);
296 0 : }
297 :
298 0 : static virusfilter_result virusfilter_fsav_scan(
299 : struct vfs_handle_struct *handle,
300 : struct virusfilter_config *config,
301 : const struct files_struct *fsp,
302 : char **reportp)
303 : {
304 0 : char *cwd_fname = fsp->conn->cwd_fsp->fsp_name->base_name;
305 0 : const char *fname = fsp->fsp_name->base_name;
306 0 : struct virusfilter_io_handle *io_h = config->io_h;
307 0 : virusfilter_result result = VIRUSFILTER_RESULT_CLEAN;
308 0 : char *report = NULL;
309 0 : char *reply = NULL;
310 0 : char *reply_token = NULL, *reply_saveptr = NULL;
311 : bool ok;
312 :
313 0 : DBG_INFO("Scanning file: %s/%s\n", cwd_fname, fname);
314 :
315 0 : ok = virusfilter_io_writevl(io_h, "SCAN\t", 5, cwd_fname,
316 0 : (int)strlen(cwd_fname), "/", 1, fname,
317 0 : (int)strlen(fname), NULL);
318 0 : if (!ok) {
319 0 : DBG_ERR("fsavd: SCAN: Write error: %s\n", strerror(errno));
320 0 : result = VIRUSFILTER_RESULT_ERROR;
321 0 : report = talloc_asprintf(talloc_tos(),
322 : "Scanner I/O error: %s\n",
323 0 : strerror(errno));
324 0 : goto virusfilter_fsav_scan_return;
325 : }
326 :
327 0 : TALLOC_FREE(reply);
328 :
329 : for (;;) {
330 0 : if (virusfilter_io_readl(talloc_tos(), io_h, &reply) != true) {
331 0 : DBG_ERR("fsavd: SCANFILE: Read error: %s\n",
332 : strerror(errno));
333 0 : result = VIRUSFILTER_RESULT_ERROR;
334 0 : report = talloc_asprintf(talloc_tos(),
335 : "Scanner I/O error: %s\n",
336 0 : strerror(errno));
337 0 : break;
338 : }
339 :
340 0 : reply_token = strtok_r(reply, "\t", &reply_saveptr);
341 :
342 0 : if (strcmp(reply_token, "OK") == 0) {
343 0 : break;
344 0 : } else if (strcmp(reply_token, "CLEAN") == 0) {
345 :
346 : /* CLEAN\t<FILEPATH> */
347 0 : result = VIRUSFILTER_RESULT_CLEAN;
348 0 : report = talloc_asprintf(talloc_tos(), "Clean");
349 0 : } else if (strcmp(reply_token, "INFECTED") == 0 ||
350 0 : strcmp(reply_token, "ARCHIVE_INFECTED") == 0 ||
351 0 : strcmp(reply_token, "MIME_INFECTED") == 0 ||
352 0 : strcmp(reply_token, "RISKWARE") == 0 ||
353 0 : strcmp(reply_token, "ARCHIVE_RISKWARE") == 0 ||
354 0 : strcmp(reply_token, "MIME_RISKWARE") == 0)
355 : {
356 :
357 : /* INFECTED\t<FILEPATH>\t<REPORT>\t<ENGINE> */
358 0 : result = VIRUSFILTER_RESULT_INFECTED;
359 0 : reply_token = strtok_r(NULL, "\t", &reply_saveptr);
360 0 : reply_token = strtok_r(NULL, "\t", &reply_saveptr);
361 0 : if (reply_token != NULL) {
362 0 : report = talloc_strdup(talloc_tos(),
363 : reply_token);
364 : } else {
365 0 : report = talloc_asprintf(talloc_tos(),
366 : "UNKNOWN INFECTION");
367 : }
368 0 : } else if (strcmp(reply_token, "OPEN_ARCHIVE") == 0) {
369 :
370 : /* Ignore */
371 0 : } else if (strcmp(reply_token, "CLOSE_ARCHIVE") == 0) {
372 :
373 : /* Ignore */
374 0 : } else if ((strcmp(reply_token, "SUSPECTED") == 0 ||
375 0 : strcmp(reply_token, "ARCHIVE_SUSPECTED") == 0 ||
376 0 : strcmp(reply_token, "MIME_SUSPECTED") == 0) &&
377 0 : config->block_suspected_file)
378 : {
379 0 : result = VIRUSFILTER_RESULT_SUSPECTED;
380 0 : reply_token = strtok_r(NULL, "\t", &reply_saveptr);
381 0 : reply_token = strtok_r(NULL, "\t", &reply_saveptr);
382 0 : if (reply_token != NULL) {
383 0 : report = talloc_strdup(talloc_tos(),
384 : reply_token);
385 : } else {
386 0 : report = talloc_asprintf(talloc_tos(),
387 : "UNKNOWN REASON SUSPECTED");
388 : }
389 0 : } else if (strcmp(reply_token, "SCAN_FAILURE") == 0) {
390 :
391 : /* SCAN_FAILURE\t<FILEPATH>\t0x<CODE>\t<REPORT> [<ENGINE>] */
392 0 : result = VIRUSFILTER_RESULT_ERROR;
393 0 : reply_token = strtok_r(NULL, "\t", &reply_saveptr);
394 0 : reply_token = strtok_r(NULL, "\t", &reply_saveptr);
395 0 : DBG_ERR("fsavd: SCANFILE: Scaner error: %s\n",
396 : reply_token ? reply_token : "UNKNOWN ERROR");
397 0 : report = talloc_asprintf(talloc_tos(),
398 : "Scanner error: %s",
399 : reply_token ? reply_token :
400 : "UNKNOWN ERROR");
401 : } else {
402 0 : result = VIRUSFILTER_RESULT_ERROR;
403 0 : DBG_ERR("fsavd: SCANFILE: Invalid reply: %s\n",
404 : reply_token);
405 0 : report = talloc_asprintf(talloc_tos(),
406 : "Scanner communication error");
407 : }
408 :
409 0 : TALLOC_FREE(reply);
410 : }
411 :
412 0 : virusfilter_fsav_scan_return:
413 0 : TALLOC_FREE(reply);
414 :
415 0 : if (report == NULL) {
416 0 : *reportp = talloc_asprintf(talloc_tos(), "Scanner report memory "
417 : "error");
418 : } else {
419 0 : *reportp = report;
420 : }
421 :
422 0 : return result;
423 : }
424 :
425 : static struct virusfilter_backend_fns virusfilter_backend_fsav ={
426 : .connect = virusfilter_fsav_connect,
427 : .disconnect = NULL,
428 : .scan_init = virusfilter_fsav_scan_init,
429 : .scan = virusfilter_fsav_scan,
430 : .scan_end = virusfilter_fsav_scan_end,
431 : };
432 :
433 0 : int virusfilter_fsav_init(struct virusfilter_config *config)
434 : {
435 0 : struct virusfilter_backend *backend = NULL;
436 :
437 0 : if (config->socket_path == NULL) {
438 0 : config->socket_path = VIRUSFILTER_DEFAULT_SOCKET_PATH;
439 : }
440 :
441 0 : backend = talloc_zero(config, struct virusfilter_backend);
442 0 : if (backend == NULL) {
443 0 : return -1;
444 : }
445 :
446 0 : backend->fns = &virusfilter_backend_fsav;
447 0 : backend->name = "fsav";
448 :
449 0 : config->backend = backend;
450 0 : return 0;
451 : }
|