CVE-2023-0614 ldb: Centralise checking for inaccessible matches
[Samba.git] / source3 / modules / vfs_virusfilter_sophos.c
blobc8cdec5fd7fc278550b878548f085bdf869ee14d
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
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
26 #else
27 # define VIRUSFILTER_DEFAULT_SOCKET_PATH "/var/run/savdi/sssp.sock"
28 #endif
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)
35 char *dst_c = dst;
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 != '_') ||
42 (*src > 'z'))
44 if (dst_size < 4) {
45 return -1;
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;
55 *dst_c++ = *src;
56 dst_size--;
60 *dst_c = '\0';
62 return (dst_c - dst);
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)
71 virusfilter_io_set_readl_eol(config->io_h, "\x0D\x0A", 2);
73 return 0;
76 static virusfilter_result virusfilter_sophos_scan_ping(
77 struct virusfilter_config *config)
79 struct virusfilter_io_handle *io_h = config->io_h;
80 char *reply = NULL;
81 bool ok;
82 int ret;
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;
90 for (;;) {
91 ok = virusfilter_io_readl(talloc_tos(), io_h, &reply);
92 if (!ok) {
93 return VIRUSFILTER_RESULT_ERROR;
95 ret = strcmp(reply, "");
96 if (ret == 0) {
97 break;
99 TALLOC_FREE(reply);
102 TALLOC_FREE(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;
110 char *reply = NULL;
111 int ret;
112 bool ok;
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);
132 become_root();
133 ok = virusfilter_io_connect_path(io_h, config->socket_path);
134 unbecome_root();
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;
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;
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;
155 DBG_DEBUG("SSSP: Connected\n");
157 DBG_INFO("SSSP: Configuring\n");
159 TALLOC_FREE(reply);
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;
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;
174 TALLOC_FREE(reply);
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;
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;
187 TALLOC_FREE(reply);
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;
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;
200 DBG_DEBUG("SSSP: Configured\n");
202 return VIRUSFILTER_RESULT_OK;
204 virusfilter_sophos_scan_init_failed:
206 TALLOC_FREE(reply);
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,
227 char **reportp)
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;
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] = '/';
253 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,
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;
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;
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;
287 TALLOC_FREE(reply);
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;
298 ret = strcmp(reply, "");
299 if (ret == 0) {
300 break;
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(),
310 reply_token);
311 } else {
312 report = talloc_asprintf(talloc_tos(),
313 "UNKNOWN INFECTION");
315 } else if (strcmp(reply_token, "OK") == 0) {
317 /* Ignore */
318 } else if (strcmp(reply_token, "DONE") == 0) {
319 reply_token = strtok_r(NULL, "", &reply_saveptr);
320 if (reply_token != NULL &&
322 /* Succeed */
323 strncmp(reply_token, "OK 0000 ", 8) != 0 &&
325 /* Infected */
326 strncmp(reply_token, "OK 0203 ", 8) != 0)
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);
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");
343 TALLOC_FREE(reply);
346 virusfilter_sophos_scan_return:
347 TALLOC_FREE(reply);
349 if (report == NULL) {
350 *reportp = talloc_asprintf(talloc_tos(),
351 "Scanner report memory error");
352 } else {
353 *reportp = report;
356 return result;
358 virusfilter_sophos_scan_io_error:
359 *reportp = talloc_asprintf(talloc_tos(),
360 "Scanner I/O error: %s\n", strerror(errno));
362 return result;
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,
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) {
383 return -1;
386 backend->fns = &virusfilter_backend_sophos;
387 backend->name = "sophos";
389 config->backend = backend;
390 return 0;