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