1 /*
2  * Copyright 2007 Niels Provos <provos@citi.umich.edu>
3  * All rights reserved.
4  */
5 
6 #include <sys/types.h>
7 
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11 
12 #include <sys/time.h>
13 #include <sys/param.h>
14 #include <sys/socket.h>
15 #include <sys/queue.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <signal.h>
20 #include <err.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <assert.h>
24 #include <errno.h>
25 
26 #include <event.h>
27 
28 #include "virus.h"
29 #include "spybye.gen.h"
30 
31 ssize_t atomicio(ssize_t (*f) (), int fd, void *_s, size_t n);
32 
33 static struct cl_node *engine = NULL;
34 static struct virusq children;
35 static struct scanctxq scans;
36 
37 static struct virus_child *virus_child_new();
38 
39 #ifndef HAVE_CLAMAV
virus_init(void)40 int virus_init(void) {
41 	return (-1);
42 }
43 static const char *
clamav_scan_buffer(char * data,int length)44 clamav_scan_buffer(char *data, int length) {
45 	return "error";
46 }
47 #else
48 #include <clamav.h>
49 
50 /*
51  * initalize the virus scanning system; this really mixes clamav and our
52  * generic frame work but that does not matter for now
53  */
54 
55 int
virus_init(void)56 virus_init(void)
57 {
58 	int res = 0, i;
59 	unsigned int sigs = 0;
60 
61 	res = cl_loaddbdir(cl_retdbdir(), &engine, &sigs);
62 	if (res) {
63 		fprintf(stderr, "[VIRUS] Failed to load clamav: %s\n",
64 		    cl_strerror(res));
65 		return (-1);
66 	}
67 
68 	res = cl_build(engine);
69 	if (res) {
70 		fprintf(stderr, "[VIRUS] Failed to build engine: %s\n",
71 		    cl_strerror(res));
72 		return (-1);
73 	}
74 
75 	fprintf(stderr, "[VIRUS] Loaded %d signatures\n", sigs);
76 
77 	TAILQ_INIT(&children);
78 	TAILQ_INIT(&scans);
79 
80 	for(i = 0; i < NUM_VIRUS_CHILDREN; ++i)
81 		virus_child_new();
82 
83 	return (0);
84 }
85 
86 static const char *
clamav_scan_buffer(char * data,int length)87 clamav_scan_buffer(char *data, int length)
88 {
89 	const char *virname;
90 	struct cl_limits limits;
91 	int res;
92 
93 	/* somewhat stupid but here we go */
94 	FILE *fp = tmpfile();
95 	if (fp == NULL)
96 		return ("error");
97 
98 	if (atomicio(write, fileno(fp), data, length) != length) {
99 		fclose(fp);
100 		return ("error");
101 	}
102 	rewind(fp);
103 
104 	memset(&limits, 0, sizeof(limits));
105 	limits.maxfiles = 1000;
106 	limits.maxfilesize = 10 * 1048576;
107 	limits.maxreclevel = 5;
108 #ifdef HAVE_CLLIMITS_MAXMAILREC
109 	limits.maxmailrec = 64;
110 #endif
111 	limits.maxratio = 200;
112 
113 	res = cl_scandesc(fileno(fp), &virname, NULL, engine,
114 	    &limits, CL_SCAN_STDOPT);
115 	fclose(fp);
116 
117 	if (res == CL_VIRUS)
118 		return (virname);
119 	else if (res == CL_CLEAN)
120 		return ("clean");
121 
122 	return ("error");
123 }
124 #endif
125 
126 /* happens in the child */
127 
128 static void
virus_process(int fd,struct virusscan * vs)129 virus_process(int fd, struct virusscan *vs)
130 {
131 	struct virusresult *vr;
132 	struct evbuffer *data;
133 	u_int8_t *buffer;
134 	u_int32_t buflen;
135 	u_int8_t *context;
136 	u_int32_t conlen;
137 	const char *result;
138 	int res;
139 
140 	EVTAG_GET(vs, buffer, &buffer, &buflen);
141 	EVTAG_GET(vs, context, &context, &conlen);
142 
143 	result = clamav_scan_buffer((char *)buffer, buflen);
144 	fprintf(stderr, "[VIRUS] Scanned %d bytes; result: %s\n",
145 	    buflen, result);
146 
147 	vr = virusresult_new();
148 	assert(vr != NULL);
149 	EVTAG_ASSIGN(vr, result, result);
150 	EVTAG_ASSIGN(vr, context, context, conlen);
151 
152 	data = evbuffer_new();
153 	assert(data != NULL);
154 	evtag_marshal_virusresult(data, VIRUSRESULT_TAG, vr);
155 	virusresult_free(vr);
156 
157 	res = atomicio(write, fd, EVBUFFER_DATA(data), EVBUFFER_LENGTH(data));
158 	if (res != EVBUFFER_LENGTH(data)) {
159 		fprintf(stderr, "[VIRUS] Write of results failed: %ld got %d\n",
160 		    EVBUFFER_LENGTH(data), res);
161 		exit(1);
162 	}
163 
164 	evbuffer_free(data);
165 }
166 
167 static void
virus_child_processing(int fd)168 virus_child_processing(int fd)
169 {
170 	struct evbuffer *data = evbuffer_new();
171 	assert(data != NULL);
172 
173 	do {
174 		u_int32_t length;
175 
176 		int res = evbuffer_read(data, fd, -1);
177 		if (res == 0)
178 			break;
179 		if (res == -1) {
180 			if (errno == EINTR || errno == EAGAIN)
181 				continue;
182 			break;
183 		}
184 
185 		while (evtag_peek_length(data, &length) != -1) {
186 			struct virusscan *vs;
187 
188 			if (EVBUFFER_LENGTH(data) < length)
189 				break;
190 
191 			if ((vs = virusscan_new()) == NULL)
192 				err(1, "malloc");
193 			if (evtag_unmarshal_virusscan(
194 				    data, VIRUSSCAN_TAG, vs) == -1) {
195 				fprintf(stderr, "[VIRUS] Corrupt message\n");
196 				exit(1);
197 			}
198 
199 			/* okay - do something here */
200 			virus_process(fd, vs);
201 
202 			virusscan_free(vs);
203 		}
204 	} while (1);
205 }
206 
207 static void
virus_result(struct virusresult * vr)208 virus_result(struct virusresult *vr)
209 {
210 	struct scanctx *ctx;
211 	struct scanctx *wanted;
212 	u_char *buf;
213 	u_int buflen;
214 	char *result;
215 
216 	EVTAG_GET(vr, result, &result);
217 	EVTAG_GET(vr, context, &buf, &buflen);
218 	assert(buflen == sizeof(wanted));
219 	memcpy(&wanted, buf, buflen);
220 
221 	TAILQ_FOREACH(ctx, &scans, next) {
222 		if ((void *)ctx == (void *)wanted)
223 			break;
224 	}
225 
226 	if ((void *)ctx != (void *)wanted)
227 		return;
228 
229 	TAILQ_REMOVE(&scans, ctx, next);
230 
231 	(*ctx->cb)(result, ctx->cb_arg);
232 
233 	free(ctx);
234 }
235 
236 /* called if we can read scan results */
237 
238 static void
virus_readcb(struct bufferevent * bev,void * arg)239 virus_readcb(struct bufferevent *bev, void *arg)
240 {
241 	u_int32_t length;
242 	while (evtag_peek_length(bev->input, &length) != -1) {
243 		struct virusresult *vr;
244 
245 		if (EVBUFFER_LENGTH(bev->input) < length)
246 			break;
247 
248 		if ((vr = virusresult_new()) == NULL)
249 			err(1, "malloc");
250 		if (evtag_unmarshal_virusresult(
251 			    bev->input, VIRUSRESULT_TAG, vr) == -1) {
252 			/* this really means that we need to kill the child */
253 			fprintf(stderr, "[VIRUS] Corrupt message\n");
254 			exit(1);
255 		}
256 
257 		/* okay - do something here */
258 		virus_result(vr);
259 
260 		virusresult_free(vr);
261 	}
262 }
263 
264 /* called if we can write more data */
265 
266 static void
virus_writecb(struct bufferevent * bev,void * arg)267 virus_writecb(struct bufferevent *bev, void *arg)
268 {
269 	bufferevent_disable(bev, EV_WRITE);
270 }
271 
272 /* let's hope we don't get any errors */
273 
274 static void
virus_errorcb(struct bufferevent * bev,short what,void * arg)275 virus_errorcb(struct bufferevent *bev, short what, void *arg)
276 {
277 	/* this means that we need to kill the child */
278 }
279 
280 static struct virus_child *
virus_child_new()281 virus_child_new()
282 {
283 	struct virus_child *child;
284 	int pair[2];
285 
286 	if ((child = calloc(1, sizeof(struct virus_child))) == NULL)
287 		err(1, "calloc");
288 
289 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1)
290 		err(1, "socketpair");
291 
292 	/* should make the system automatically reap child processes */
293 	if (signal(SIGCHLD, SIG_IGN) == SIG_ERR)
294 		err(1, "signal");
295 
296 	if ((child->pid = fork()) == -1)
297 		err(1, "fork");
298 
299 	if (child->pid == 0) {
300 		/* running in the child */
301 		close(pair[0]);
302 		virus_child_processing(pair[1]);
303 		exit(0);
304 	}
305 
306 	close(pair[1]);
307 	child->fd = pair[0];
308 	child->bev = bufferevent_new(child->fd,
309 		virus_readcb, virus_writecb, virus_errorcb,
310 		child);
311 	if (child->bev == NULL)
312 		err(1, "bufferevent_new");
313 
314 	TAILQ_INSERT_TAIL(&children, child, next);
315 
316 	bufferevent_enable(child->bev, EV_READ);
317 
318 	return (child);
319 }
320 
321 /*
322  * send something to our children; when the scan is done the specified
323  * callback is going to be executed.
324  */
325 
326 void
virus_scan_buffer(char * buffer,size_t buflen,void (* cb)(const char *,void *),void * cb_arg)327 virus_scan_buffer(char *buffer, size_t buflen,
328     void (*cb)(const char *, void *), void *cb_arg)
329 {
330 	struct scanctx *ctx = calloc(1, sizeof(struct scanctx));
331 	struct virusscan *vs = virusscan_new();
332 	struct evbuffer *data = evbuffer_new();
333 	struct virus_child *child;
334 	assert(ctx != NULL);
335 	assert(vs != NULL);
336 	assert(data != NULL);
337 
338 	ctx->cb = cb;
339 	ctx->cb_arg = cb_arg;
340 
341 	TAILQ_INSERT_TAIL(&scans, ctx, next);
342 
343 	/* okay now prepare the actual scan job */
344 	EVTAG_ASSIGN(vs, buffer, (u_char *)buffer, buflen);
345 	EVTAG_ASSIGN(vs, context, (u_char *)&ctx, sizeof(ctx));
346 
347 	/* weeh - double copy for our convenience */
348 	evtag_marshal_virusscan(data, VIRUSSCAN_TAG, vs);
349 
350 	virusscan_free(vs);
351 
352 	/* get the first available child */
353 	child = TAILQ_FIRST(&children);
354 	assert(child != NULL);
355 	TAILQ_REMOVE(&children, child, next);
356 	TAILQ_INSERT_TAIL(&children, child, next);
357 
358 	bufferevent_write_buffer(child->bev, data);
359 	bufferevent_enable(child->bev, EV_WRITE);
360 	evbuffer_free(data);
361 }
362