s3:libsmb: allow store_cldap_reply() to work with a ipv6 response
[samba.git] / source3 / modules / vfs_virusfilter_sophos.c
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 static int virusfilter_url_quote(const char *src, char *dst, int dst_size)
34 {
35         char *dst_c = dst;
36         static char hex[] = "0123456789ABCDEF";
37
38         for (; *src != '\0'; src++) {
39                 if ((*src < '0' && *src != '-' && *src != '.' && *src != '/') ||
40                     (*src > '9' && *src < 'A') ||
41                     (*src > 'Z' && *src < 'a' && *src != '_') ||
42                     (*src > 'z'))
43                 {
44                         if (dst_size < 4) {
45                                 return -1;
46                         }
47                         *dst_c++ = '%';
48                         *dst_c++ = hex[(*src >> 4) & 0x0F];
49                         *dst_c++ = hex[*src & 0x0F];
50                         dst_size -= 3;
51                 } else {
52                         if (dst_size < 2) {
53                                 return -1;
54                         }
55                         *dst_c++ = *src;
56                         dst_size--;
57                 }
58         }
59
60         *dst_c = '\0';
61
62         return (dst_c - dst);
63 }
64
65 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         virusfilter_io_set_readl_eol(config->io_h, "\x0D\x0A", 2);
72
73         return 0;
74 }
75
76 static virusfilter_result virusfilter_sophos_scan_ping(
77         struct virusfilter_config *config)
78 {
79         struct virusfilter_io_handle *io_h = config->io_h;
80         char *reply = NULL;
81         bool ok;
82         int ret;
83
84         /* SSSP/1.0 has no "PING" command */
85         ok = virusfilter_io_writel(io_h, "SSSP/1.0 OPTIONS\n", 17);
86         if (!ok) {
87                 return VIRUSFILTER_RESULT_ERROR;
88         }
89
90         for (;;) {
91                 ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
92                 if (!ok) {
93                         return VIRUSFILTER_RESULT_ERROR;
94                 }
95                 ret = strcmp(reply, "");
96                 if (ret == 0) {
97                         break;
98                 }
99                 TALLOC_FREE(reply);
100         }
101
102         TALLOC_FREE(reply);
103         return VIRUSFILTER_RESULT_OK;
104 }
105
106 static virusfilter_result virusfilter_sophos_scan_init(
107         struct virusfilter_config *config)
108 {
109         struct virusfilter_io_handle *io_h = config->io_h;
110         char *reply = NULL;
111         int ret;
112         bool ok;
113
114         if (io_h->stream != NULL) {
115                 DBG_DEBUG("SSSP: Checking if connection is alive\n");
116
117                 ret = virusfilter_sophos_scan_ping(config);
118                 if (ret == VIRUSFILTER_RESULT_OK)
119                 {
120                         DBG_DEBUG("SSSP: Re-using existent connection\n");
121                         return VIRUSFILTER_RESULT_OK;
122                 }
123
124                 DBG_INFO("SSSP: Closing dead connection\n");
125                 virusfilter_sophos_scan_end(config);
126         }
127
128
129         DBG_INFO("SSSP: 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("SSSP: Connecting to socket failed: %s: %s\n",
138                         config->socket_path, strerror(errno));
139                 return VIRUSFILTER_RESULT_ERROR;
140         }
141
142         ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
143         if (!ok) {
144                 DBG_ERR("SSSP: Reading greeting message failed: %s\n",
145                         strerror(errno));
146                 goto virusfilter_sophos_scan_init_failed;
147         }
148         ret = strncmp(reply, "OK SSSP/1.0", 11);
149         if (ret != 0) {
150                 DBG_ERR("SSSP: Invalid greeting message: %s\n",
151                         reply);
152                 goto virusfilter_sophos_scan_init_failed;
153         }
154
155         DBG_DEBUG("SSSP: Connected\n");
156
157         DBG_INFO("SSSP: Configuring\n");
158
159         TALLOC_FREE(reply);
160
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);
164         if (!ok) {
165                 DBG_ERR("SSSP: OPTIONS: I/O error: %s\n", strerror(errno));
166                 goto virusfilter_sophos_scan_init_failed;
167         }
168         ret = strncmp(reply, "ACC ", 4);
169         if (ret != 0) {
170                 DBG_ERR("SSSP: OPTIONS: Not accepted: %s\n", reply);
171                 goto virusfilter_sophos_scan_init_failed;
172         }
173
174         TALLOC_FREE(reply);
175
176         ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
177         if (!ok) {
178                 DBG_ERR("SSSP: OPTIONS: Read error: %s\n", strerror(errno));
179                 goto virusfilter_sophos_scan_init_failed;
180         }
181         ret = strncmp(reply, "DONE OK ", 8);
182         if (ret != 0) {
183                 DBG_ERR("SSSP: OPTIONS failed: %s\n", reply);
184                 goto virusfilter_sophos_scan_init_failed;
185         }
186
187         TALLOC_FREE(reply);
188
189         ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
190         if (!ok) {
191                 DBG_ERR("SSSP: OPTIONS: Read error: %s\n", strerror(errno));
192                 goto virusfilter_sophos_scan_init_failed;
193         }
194         ret = strcmp(reply, "");
195         if (ret != 0) {
196                 DBG_ERR("SSSP: OPTIONS: Invalid reply: %s\n", reply);
197                 goto virusfilter_sophos_scan_init_failed;
198         }
199
200         DBG_DEBUG("SSSP: Configured\n");
201
202         return VIRUSFILTER_RESULT_OK;
203
204 virusfilter_sophos_scan_init_failed:
205
206         TALLOC_FREE(reply);
207
208         virusfilter_sophos_scan_end(config);
209
210         return VIRUSFILTER_RESULT_ERROR;
211 }
212
213 static void virusfilter_sophos_scan_end(
214         struct virusfilter_config *config)
215 {
216         struct virusfilter_io_handle *io_h = config->io_h;
217
218         DBG_INFO("SSSP: Disconnecting\n");
219
220         virusfilter_io_disconnect(io_h);
221 }
222
223 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         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;
235         char *report = NULL;
236         char *reply = NULL;
237         char *reply_token = NULL, *reply_saveptr = NULL;
238         int ret;
239         bool ok;
240
241         DBG_INFO("Scanning file: %s/%s\n", cwd_fname, fname);
242
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;
251         }
252         fileurl[fileurl_len] = '/';
253         fileurl_len++;
254
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;
263         }
264         fileurl_len += fileurl_len2;
265
266         ok = virusfilter_io_writevl(io_h, "SSSP/1.0 SCANFILE ", 18, fileurl,
267                                     fileurl_len, NULL);
268         if (!ok) {
269                 DBG_ERR("SSSP: SCANFILE: Write error: %s\n",
270                       strerror(errno));
271                 goto virusfilter_sophos_scan_io_error;
272         }
273
274         ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
275         if (!ok) {
276                 DBG_ERR("SSSP: SCANFILE: Read error: %s\n", strerror(errno));
277                 goto virusfilter_sophos_scan_io_error;
278         }
279         ret = strncmp(reply, "ACC ", 4);
280         if (ret != 0) {
281                 DBG_ERR("SSSP: SCANFILE: Not accepted: %s\n",
282                         reply);
283                 result = VIRUSFILTER_RESULT_ERROR;
284                 goto virusfilter_sophos_scan_return;
285         }
286
287         TALLOC_FREE(reply);
288
289         result = VIRUSFILTER_RESULT_CLEAN;
290         for (;;) {
291                 ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
292                 if (!ok) {
293                         DBG_ERR("SSSP: SCANFILE: Read error: %s\n",
294                                 strerror(errno));
295                         goto virusfilter_sophos_scan_io_error;
296                 }
297
298                 ret = strcmp(reply, "");
299                 if (ret == 0) {
300                         break;
301                 }
302
303                 reply_token = strtok_r(reply, " ", &reply_saveptr);
304
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(),
310                                                          reply_token);
311                         } else {
312                                   report = talloc_asprintf(talloc_tos(),
313                                                         "UNKNOWN INFECTION");
314                         }
315                 } else if (strcmp(reply_token, "OK") == 0) {
316
317                         /* Ignore */
318                 } else if (strcmp(reply_token, "DONE") == 0) {
319                         reply_token = strtok_r(NULL, "", &reply_saveptr);
320                         if (reply_token != NULL &&
321
322                             /* Succeed */
323                             strncmp(reply_token, "OK 0000 ", 8) != 0 &&
324
325                             /* Infected */
326                             strncmp(reply_token, "OK 0203 ", 8) != 0)
327                         {
328                                 DBG_ERR("SSSP: SCANFILE: Error: %s\n",
329                                         reply_token);
330                                 result = VIRUSFILTER_RESULT_ERROR;
331                                 report = talloc_asprintf(talloc_tos(),
332                                                          "Scanner error: %s\n",
333                                                          reply_token);
334                         }
335                 } else {
336                         DBG_ERR("SSSP: SCANFILE: Invalid reply: %s\n",
337                                 reply_token);
338                         result = VIRUSFILTER_RESULT_ERROR;
339                         report = talloc_asprintf(talloc_tos(), "Scanner "
340                                                  "communication error");
341                 }
342
343                 TALLOC_FREE(reply);
344         }
345
346 virusfilter_sophos_scan_return:
347         TALLOC_FREE(reply);
348
349         if (report == NULL) {
350                 *reportp = talloc_asprintf(talloc_tos(),
351                                            "Scanner report memory error");
352         } else {
353                 *reportp = report;
354         }
355
356         return result;
357
358 virusfilter_sophos_scan_io_error:
359         *reportp = talloc_asprintf(talloc_tos(),
360                                    "Scanner I/O error: %s\n", strerror(errno));
361
362         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 int virusfilter_sophos_init(struct virusfilter_config *config)
374 {
375         struct virusfilter_backend *backend = NULL;
376
377         if (config->socket_path == NULL) {
378                 config->socket_path = VIRUSFILTER_DEFAULT_SOCKET_PATH;
379         }
380
381         backend = talloc_zero(config, struct virusfilter_backend);
382         if (backend == NULL) {
383                 return -1;
384         }
385
386         backend->fns = &virusfilter_backend_sophos;
387         backend->name = "sophos";
388
389         config->backend = backend;
390         return 0;
391 }