s3: VFS: Remove fsync_fn() from the VFS and all modules. VFS ABI change.
[samba.git] / source3 / modules / vfs_virusfilter_clamav.c
1 /*
2    Samba-VirusFilter VFS modules
3    ClamAV clamd 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 /* Default values for standard "extra" configuration variables */
21
22 #ifdef CLAMAV_DEFAULT_SOCKET_PATH
23 #  define VIRUSFILTER_DEFAULT_SOCKET_PATH       CLAMAV_DEFAULT_SOCKET_PATH
24 #else
25 #  define VIRUSFILTER_DEFAULT_SOCKET_PATH       "/var/run/clamav/clamd.ctl"
26 #endif
27
28 #include "modules/vfs_virusfilter_common.h"
29 #include "modules/vfs_virusfilter_utils.h"
30
31 static int virusfilter_clamav_connect(struct vfs_handle_struct *handle,
32                                       struct virusfilter_config *config,
33                                       const char *svc,
34                                       const char *user)
35 {
36
37         /* To use clamd "zXXXX" commands */
38         virusfilter_io_set_writel_eol(config->io_h, "\0", 1);
39         virusfilter_io_set_readl_eol(config->io_h, "\0", 1);
40
41         return 0;
42 }
43
44 static virusfilter_result virusfilter_clamav_scan_init(
45         struct virusfilter_config *config)
46 {
47         struct virusfilter_io_handle *io_h = config->io_h;
48         bool ok;
49
50         DBG_INFO("clamd: Connecting to socket: %s\n",
51                  config->socket_path);
52
53         become_root();
54         ok = virusfilter_io_connect_path(io_h, config->socket_path);
55         unbecome_root();
56
57         if (!ok) {
58                 DBG_ERR("clamd: Connecting to socket failed: %s: %s\n",
59                         config->socket_path, strerror(errno));
60                 return VIRUSFILTER_RESULT_ERROR;
61         }
62
63         DBG_INFO("clamd: Connected\n");
64
65         return VIRUSFILTER_RESULT_OK;
66 }
67
68 static void virusfilter_clamav_scan_end(
69         struct virusfilter_config *config)
70 {
71         struct virusfilter_io_handle *io_h = config->io_h;
72
73         DBG_INFO("clamd: Disconnecting\n");
74
75         virusfilter_io_disconnect(io_h);
76 }
77
78 static virusfilter_result virusfilter_clamav_scan(
79         struct vfs_handle_struct *handle,
80         struct virusfilter_config *config,
81         const struct files_struct *fsp,
82         char **reportp)
83 {
84         char *cwd_fname = fsp->conn->cwd_fname->base_name;
85         const char *fname = fsp->fsp_name->base_name;
86         size_t filepath_len = strlen(cwd_fname) + 1 /* slash */ + strlen(fname);
87         struct virusfilter_io_handle *io_h = config->io_h;
88         virusfilter_result result = VIRUSFILTER_RESULT_CLEAN;
89         char *report = NULL;
90         char *reply = NULL;
91         char *reply_msg = NULL;
92         char *reply_token;
93         bool ok;
94
95         DBG_INFO("Scanning file: %s/%s\n", cwd_fname, fname);
96
97         ok = virusfilter_io_writefl_readl(io_h, &reply, "zSCAN %s/%s",
98                                           cwd_fname, fname);
99         if (!ok) {
100                 DBG_ERR("clamd: zSCAN: I/O error: %s\n", strerror(errno));
101                 result = VIRUSFILTER_RESULT_ERROR;
102                 report = talloc_asprintf(talloc_tos(),
103                                          "Scanner I/O error: %s\n",
104                                          strerror(errno));
105                 goto virusfilter_clamav_scan_return;
106         }
107
108         if (reply[filepath_len] != ':' ||
109             reply[filepath_len+1] != ' ')
110         {
111                 DBG_ERR("clamd: zSCAN: Invalid reply: %s\n",
112                         reply);
113                 result = VIRUSFILTER_RESULT_ERROR;
114                 report = talloc_asprintf(talloc_tos(),
115                                          "Scanner communication error");
116                 goto virusfilter_clamav_scan_return;
117         }
118         reply_msg = reply + filepath_len + 2;
119
120         reply_token = strrchr(reply, ' ');
121
122         if (reply_token == NULL) {
123                 DBG_ERR("clamd: zSCAN: Invalid reply: %s\n",
124                         reply);
125                 result = VIRUSFILTER_RESULT_ERROR;
126                 report = talloc_asprintf(talloc_tos(),
127                                          "Scanner communication error");
128                 goto virusfilter_clamav_scan_return;
129         }
130         *reply_token = '\0';
131         reply_token++;
132
133         if (strcmp(reply_token, "OK") == 0) {
134
135                 /* <FILEPATH>: OK */
136                 result = VIRUSFILTER_RESULT_CLEAN;
137                 report = talloc_asprintf(talloc_tos(), "Clean");
138         } else if (strcmp(reply_token, "FOUND") == 0) {
139
140                 /* <FILEPATH>: <REPORT> FOUND */
141                 result = VIRUSFILTER_RESULT_INFECTED;
142                 report = talloc_strdup(talloc_tos(), reply_msg);
143         } else if (strcmp(reply_token, "ERROR") == 0) {
144
145                 /* <FILEPATH>: <REPORT> ERROR */
146                 DBG_ERR("clamd: zSCAN: Error: %s\n", reply_msg);
147                 result = VIRUSFILTER_RESULT_ERROR;
148                 report = talloc_asprintf(talloc_tos(),
149                                          "Scanner error: %s\t", reply_msg);
150         } else {
151                 DBG_ERR("clamd: zSCAN: Invalid reply: %s\n", reply_token);
152                 result = VIRUSFILTER_RESULT_ERROR;
153                 report = talloc_asprintf(talloc_tos(),
154                                          "Scanner communication error");
155         }
156
157 virusfilter_clamav_scan_return:
158         TALLOC_FREE(reply);
159         if (report == NULL) {
160                 *reportp = talloc_asprintf(talloc_tos(),
161                                            "Scanner report memory error");
162         } else {
163                 *reportp = report;
164         }
165
166         return result;
167 }
168
169 static struct virusfilter_backend_fns virusfilter_backend_clamav = {
170         .connect = virusfilter_clamav_connect,
171         .disconnect = NULL,
172         .scan_init = virusfilter_clamav_scan_init,
173         .scan = virusfilter_clamav_scan,
174         .scan_end = virusfilter_clamav_scan_end,
175 };
176
177 int virusfilter_clamav_init(struct virusfilter_config *config)
178 {
179         struct virusfilter_backend *backend = NULL;
180
181         if (config->socket_path == NULL) {
182                 config->socket_path = VIRUSFILTER_DEFAULT_SOCKET_PATH;
183         }
184
185         backend = talloc_zero(config, struct virusfilter_backend);
186         if (backend == NULL) {
187                 return -1;
188         }
189
190         backend->fns = &virusfilter_backend_clamav;
191         backend->name = "clamav";
192
193         config->backend = backend;
194         return 0;
195 }