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