Browse code

clamonacc - make clamonacc connect with clamd

Mickey Sola authored on 2018/12/18 01:47:18
Showing 6 changed files
... ...
@@ -38,6 +38,8 @@ clamonacc_SOURCES = \
38 38
     clamonacc.c \
39 39
     onaccess_client.c \
40 40
     onaccess_client.h \
41
+    onaccess_proto.c \
42
+    onaccess_proto.h \
41 43
     onaccess_ddd.c \
42 44
     onaccess_ddd.h \
43 45
     onaccess_fan.c \
... ...
@@ -53,7 +55,7 @@ AM_CFLAGS=@WERR_CFLAGS@
53 53
 endif
54 54
 
55 55
 
56
-DEFS = @DEFS@ -DCL_NOTHREADS -DCL_NOLIBCLAMAV
56
+DEFS = @DEFS@ -DCL_NOLIBCLAMAV
57 57
 LIBS = $(top_builddir)/libclamav/libclamav_internal_utils.la  @CLAMONACC_LIBS@ @THREAD_LIBS@
58 58
 AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/clamd -I$(top_srcdir)/shared -I$(top_srcdir)/libclamav @SSL_CPPFLAGS@ @CLAMONACC_CPPFLAGS@ @JSON_CPPFLAGS@ @PCRE_CPPFLAGS@
59 59
 
... ...
@@ -204,15 +204,16 @@ am__clamonacc_SOURCES_DIST = $(top_srcdir)/shared/output.c \
204 204
 	$(top_srcdir)/shared/getopt.h $(top_srcdir)/shared/actions.c \
205 205
 	$(top_srcdir)/shared/actions.h $(top_srcdir)/shared/clamdcom.c \
206 206
 	$(top_srcdir)/shared/clamdcom.h clamonacc.c onaccess_client.c \
207
-	onaccess_client.h onaccess_ddd.c onaccess_ddd.h onaccess_fan.c \
208
-	onaccess_fan.h onaccess_hash.c onaccess_hash.h \
209
-	onaccess_others.c onaccess_others.h onaccess_scth.c \
210
-	onaccess_scth.h
207
+	onaccess_client.h onaccess_proto.c onaccess_proto.h \
208
+	onaccess_ddd.c onaccess_ddd.h onaccess_fan.c onaccess_fan.h \
209
+	onaccess_hash.c onaccess_hash.h onaccess_others.c \
210
+	onaccess_others.h onaccess_scth.c onaccess_scth.h
211 211
 @BUILD_CLAMD_TRUE@am_clamonacc_OBJECTS = output.$(OBJEXT) \
212 212
 @BUILD_CLAMD_TRUE@	optparser.$(OBJEXT) misc.$(OBJEXT) \
213 213
 @BUILD_CLAMD_TRUE@	getopt.$(OBJEXT) actions.$(OBJEXT) \
214 214
 @BUILD_CLAMD_TRUE@	clamdcom.$(OBJEXT) clamonacc.$(OBJEXT) \
215 215
 @BUILD_CLAMD_TRUE@	onaccess_client.$(OBJEXT) \
216
+@BUILD_CLAMD_TRUE@	onaccess_proto.$(OBJEXT) \
216 217
 @BUILD_CLAMD_TRUE@	onaccess_ddd.$(OBJEXT) \
217 218
 @BUILD_CLAMD_TRUE@	onaccess_fan.$(OBJEXT) \
218 219
 @BUILD_CLAMD_TRUE@	onaccess_hash.$(OBJEXT) \
... ...
@@ -334,7 +335,7 @@ CXXDEPMODE = @CXXDEPMODE@
334 334
 CXXFLAGS = @CXXFLAGS@
335 335
 CYGPATH_W = @CYGPATH_W@
336 336
 DBDIR = @DBDIR@
337
-DEFS = @DEFS@ -DCL_NOTHREADS -DCL_NOLIBCLAMAV
337
+DEFS = @DEFS@ -DCL_NOLIBCLAMAV
338 338
 DEPDIR = @DEPDIR@
339 339
 DLLTOOL = @DLLTOOL@
340 340
 DSYMUTIL = @DSYMUTIL@
... ...
@@ -532,6 +533,8 @@ top_srcdir = @top_srcdir@
532 532
 @BUILD_CLAMD_TRUE@    clamonacc.c \
533 533
 @BUILD_CLAMD_TRUE@    onaccess_client.c \
534 534
 @BUILD_CLAMD_TRUE@    onaccess_client.h \
535
+@BUILD_CLAMD_TRUE@    onaccess_proto.c \
536
+@BUILD_CLAMD_TRUE@    onaccess_proto.h \
535 537
 @BUILD_CLAMD_TRUE@    onaccess_ddd.c \
536 538
 @BUILD_CLAMD_TRUE@    onaccess_ddd.h \
537 539
 @BUILD_CLAMD_TRUE@    onaccess_fan.c \
... ...
@@ -666,6 +669,7 @@ distclean-compile:
666 666
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/onaccess_fan.Po@am__quote@
667 667
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/onaccess_hash.Po@am__quote@
668 668
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/onaccess_others.Po@am__quote@
669
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/onaccess_proto.Po@am__quote@
669 670
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/onaccess_scth.Po@am__quote@
670 671
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/optparser.Po@am__quote@
671 672
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@
... ...
@@ -147,7 +147,7 @@ int main(int argc, char **argv)
147 147
 
148 148
     gettimeofday(&t1, NULL);
149 149
 
150
-    /*ret = client(opts, &infected, &err);*/
150
+    ret = client(opts, &infected, &err);
151 151
 
152 152
     optfree(clamdopts);
153 153
     logg_close();
... ...
@@ -62,7 +62,7 @@
62 62
 #include "libclamav/others.h"
63 63
 
64 64
 #include "onaccess_client.h"
65
-#include "clamdscan/proto.h"
65
+#include "onaccess_proto.h"
66 66
 
67 67
 unsigned long int maxstream;
68 68
 struct sockaddr_un nixsock;
69 69
new file mode 100644
... ...
@@ -0,0 +1,691 @@
0
+/*
1
+ *  Copyright (C) 2015 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
2
+ *  Copyright (C) 2009 Sourcefire, Inc.
3
+ *
4
+ *  Authors: Tomasz Kojm, aCaB
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 version 2 as
8
+ *  published by the Free Software Foundation.
9
+ *
10
+ *  This program is distributed in the hope that it will be useful,
11
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ *  GNU General Public License for more details.
14
+ *
15
+ *  You should have received a copy of the GNU General Public License
16
+ *  along with this program; if not, write to the Free Software
17
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18
+ *  MA 02110-1301, USA.
19
+ */
20
+
21
+#if HAVE_CONFIG_H
22
+#include "clamav-config.h"
23
+#endif
24
+
25
+#if defined(C_SOLARIS)
26
+#ifndef __EXTENSIONS__
27
+#define __EXTENSIONS__
28
+#endif
29
+#endif
30
+
31
+/* must be first because it may define _XOPEN_SOURCE */
32
+#include "shared/fdpassing.h"
33
+#include <stdio.h>
34
+#ifdef HAVE_UNISTD_H
35
+#include <unistd.h>
36
+#endif
37
+#include <string.h>
38
+#include <errno.h>
39
+#include <stdlib.h>
40
+#include <sys/types.h>
41
+#include <sys/stat.h>
42
+#include <fcntl.h>
43
+#include <sys/types.h>
44
+#ifdef HAVE_SYS_SELECT_H
45
+#include <sys/select.h>
46
+#endif
47
+#ifndef _WIN32
48
+#include <arpa/inet.h>
49
+#include <sys/socket.h>
50
+#include <sys/un.h>
51
+#include <netdb.h>
52
+#endif
53
+
54
+#include "libclamav/clamav.h"
55
+#include "libclamav/others.h"
56
+#include "shared/actions.h"
57
+#include "shared/output.h"
58
+#include "shared/misc.h"
59
+#include "shared/clamdcom.h"
60
+
61
+#include "onaccess_proto.h"
62
+#include "onaccess_client.h"
63
+
64
+extern unsigned long int maxstream;
65
+int printinfected;
66
+extern struct optstruct *clamdopts;
67
+#ifndef _WIN32
68
+extern struct sockaddr_un nixsock;
69
+#endif
70
+
71
+static const char *scancmd[] = { "CONTSCAN", "MULTISCAN", "INSTREAM", "FILDES", "ALLMATCHSCAN" };
72
+
73
+/* Connects to clamd 
74
+ * Returns a FD or -1 on error */
75
+int dconnect() {
76
+    int sockd, res;
77
+    const struct optstruct *opt;
78
+    struct addrinfo hints, *info, *p;
79
+    char port[10];
80
+    char *ipaddr;
81
+
82
+#ifndef _WIN32
83
+    opt = optget(clamdopts, "LocalSocket");
84
+    if (opt->enabled) {
85
+        if ((sockd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) {
86
+            if (connect(sockd, (struct sockaddr *)&nixsock, sizeof(nixsock)) == 0)
87
+                return sockd;
88
+            else {
89
+                logg("!Could not connect to clamd on LocalSocket %s: %s\n", opt->strarg, strerror(errno));
90
+                close(sockd);
91
+            }
92
+        }
93
+    }
94
+#endif
95
+
96
+    snprintf(port, sizeof(port), "%lld", optget(clamdopts, "TCPSocket")->numarg);
97
+
98
+    opt = optget(clamdopts, "TCPAddr");
99
+    while (opt) {
100
+        if (opt->enabled) {
101
+            ipaddr = NULL;
102
+            if (opt->strarg)
103
+                ipaddr = (!strcmp(opt->strarg, "any") ? NULL : opt->strarg);
104
+
105
+            memset(&hints, 0x00, sizeof(struct addrinfo));
106
+            hints.ai_family = AF_UNSPEC;
107
+            hints.ai_socktype = SOCK_STREAM;
108
+
109
+            if ((res = getaddrinfo(ipaddr, port, &hints, &info))) {
110
+                logg("!Could not lookup %s: %s\n", ipaddr ? ipaddr : "", gai_strerror(res));
111
+                opt = opt->nextarg;
112
+                continue;
113
+            }
114
+
115
+            for (p = info; p != NULL; p = p->ai_next) {
116
+                if((sockd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) {
117
+                    logg("!Can't create the socket: %s\n", strerror(errno));
118
+                    continue;
119
+                }
120
+
121
+                if(connect(sockd, p->ai_addr, p->ai_addrlen) < 0) {
122
+                    logg("!Could not connect to clamd on %s: %s\n", opt->strarg, strerror(errno));
123
+                    closesocket(sockd);
124
+                    continue;
125
+                }
126
+
127
+                freeaddrinfo(info);
128
+                return sockd;
129
+            }
130
+
131
+            freeaddrinfo(info);
132
+        }
133
+        opt = opt->nextarg;
134
+    }
135
+
136
+    return -1;
137
+}
138
+
139
+/* Issues an INSTREAM command to clamd and streams the given file
140
+ * Returns >0 on success, 0 soft fail, -1 hard fail */
141
+static int send_stream(int sockd, const char *filename) {
142
+    uint32_t buf[BUFSIZ/sizeof(uint32_t)];
143
+    int fd, len;
144
+    unsigned long int todo = maxstream;
145
+
146
+    if(filename) {
147
+	if((fd = safe_open(filename, O_RDONLY | O_BINARY))<0) {
148
+	    logg("~%s: Access denied. ERROR\n", filename);
149
+	    return 0;
150
+	}
151
+    } else fd = 0;
152
+
153
+    if(sendln(sockd, "zINSTREAM", 10)) {
154
+	close(fd);
155
+	return -1;
156
+    }
157
+
158
+    while((len = read(fd, &buf[1], sizeof(buf) - sizeof(uint32_t))) > 0) {
159
+	if((unsigned int)len > todo) len = todo;
160
+	buf[0] = htonl(len);
161
+	if(sendln(sockd, (const char *)buf, len+sizeof(uint32_t))) {
162
+	    close(fd);
163
+	    return -1;
164
+	}
165
+	todo -= len;
166
+	if(!todo) {
167
+	    len = 0;
168
+	    break;
169
+	}
170
+    }
171
+    close(fd);
172
+    if(len) {
173
+	logg("!Failed to read from %s.\n", filename ? filename : "STDIN");
174
+	return 0;
175
+    }
176
+    *buf=0;
177
+    sendln(sockd, (const char *)buf, 4);
178
+    return 1;
179
+}
180
+
181
+#ifdef HAVE_FD_PASSING
182
+/* Issues a FILDES command and pass a FD to clamd
183
+ * Returns >0 on success, 0 soft fail, -1 hard fail */
184
+static int send_fdpass(int sockd, const char *filename) {
185
+    struct iovec iov[1];
186
+    struct msghdr msg;
187
+    struct cmsghdr *cmsg;
188
+    unsigned char fdbuf[CMSG_SPACE(sizeof(int))];
189
+    char dummy[]="";
190
+    int fd;
191
+
192
+    if(filename) {
193
+	if((fd = open(filename, O_RDONLY))<0) {
194
+	    logg("~%s: Access denied. ERROR\n", filename);
195
+	    return 0;
196
+	}
197
+    } else fd = 0;
198
+    if(sendln(sockd, "zFILDES", 8)) {
199
+      close(fd);
200
+      return -1;
201
+    }
202
+
203
+    iov[0].iov_base = dummy;
204
+    iov[0].iov_len = 1;
205
+    memset(&msg, 0, sizeof(msg));
206
+    msg.msg_control = fdbuf;
207
+    msg.msg_iov = iov;
208
+    msg.msg_iovlen = 1;
209
+    msg.msg_controllen = CMSG_LEN(sizeof(int));
210
+    cmsg = CMSG_FIRSTHDR(&msg);
211
+    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
212
+    cmsg->cmsg_level = SOL_SOCKET;
213
+    cmsg->cmsg_type = SCM_RIGHTS;
214
+    *(int *)CMSG_DATA(cmsg) = fd;
215
+    if(sendmsg(sockd, &msg, 0) == -1) {
216
+	logg("!FD send failed: %s\n", strerror(errno));
217
+	close(fd);
218
+	return -1;
219
+    }
220
+    close(fd);
221
+    return 1;
222
+}
223
+#endif
224
+
225
+/* 0: scan, 1: skip */
226
+static int chkpath(const char *path)
227
+{
228
+	const struct optstruct *opt;
229
+
230
+   if((opt = optget(clamdopts, "ExcludePath"))->enabled) {
231
+	while(opt) {
232
+	    if(match_regex(path, opt->strarg) == 1) {
233
+                if (printinfected != 1)
234
+                    logg("~%s: Excluded\n", path);
235
+		return 1;
236
+	    }
237
+	    opt = opt->nextarg;
238
+	}
239
+    }
240
+    return 0;
241
+}
242
+
243
+static int ftw_chkpath(const char *path, struct cli_ftw_cbdata *data)
244
+{
245
+    UNUSEDPARAM(data);
246
+    return chkpath(path);
247
+}
248
+
249
+/* Sends a proper scan request to clamd and parses its replies
250
+ * This is used only in non IDSESSION mode
251
+ * Returns the number of infected files or -1 on error
252
+ * NOTE: filename may be NULL for STREAM scantype. */
253
+int dsresult(int sockd, int scantype, const char *filename, int *printok, int *errors) {
254
+    int infected = 0, len = 0, beenthere = 0;
255
+    char *bol, *eol;
256
+    struct RCVLN rcv;
257
+    STATBUF sb;
258
+
259
+    if(filename && chkpath(filename))
260
+	return 0;
261
+    recvlninit(&rcv, sockd);
262
+
263
+    switch(scantype) {
264
+    case MULTI:
265
+    case CONT:
266
+    case ALLMATCH:
267
+    if (!filename) {
268
+	logg("Filename cannot be NULL for MULTISCAN or CONTSCAN.\n");
269
+	return -1;
270
+    }
271
+    len = strlen(filename) + strlen(scancmd[scantype]) + 3;
272
+    if (!(bol = malloc(len))) {
273
+	logg("!Cannot allocate a command buffer: %s\n", strerror(errno));
274
+	return -1;
275
+    }
276
+    sprintf(bol, "z%s %s", scancmd[scantype], filename);
277
+    if(sendln(sockd, bol, len)) {
278
+	free(bol);
279
+	return -1;
280
+    }
281
+    free(bol);
282
+    break;
283
+
284
+    case STREAM:
285
+        /* NULL filename safe in send_stream() */
286
+	len = send_stream(sockd, filename);
287
+	break;
288
+#ifdef HAVE_FD_PASSING
289
+    case FILDES:
290
+        /* NULL filename safe in send_fdpass() */
291
+	len = send_fdpass(sockd, filename);
292
+	break;
293
+#endif
294
+    }
295
+
296
+    if(len <=0) {
297
+	*printok = 0;
298
+	if(errors)
299
+	    (*errors)++;
300
+	return len;
301
+    }
302
+
303
+    while((len = recvln(&rcv, &bol, &eol))) {
304
+	if(len == -1) return -1;
305
+	beenthere = 1;
306
+	if(!filename) logg("~%s\n", bol);
307
+	if(len > 7) {
308
+	    char *colon = strrchr(bol, ':');
309
+	    if(colon && colon[1] != ' ') {
310
+		char *br;
311
+		*colon = 0;
312
+		br = strrchr(bol, '(');
313
+		if(br)
314
+		    *br = 0;
315
+		colon = strrchr(bol, ':');
316
+	    }
317
+	    if(!colon) {
318
+		char * unkco = "UNKNOWN COMMAND";
319
+		if (!strncmp(bol, unkco, sizeof(unkco) - 1))
320
+		    logg("clamd replied \"UNKNOWN COMMAND\". Command was %s\n", 
321
+			 (scantype < 0 || scantype > MAX_SCANTYPE) ? "unidentified" :
322
+			                                             scancmd[scantype]);
323
+		else
324
+		    logg("Failed to parse reply: \"%s\"\n", bol);
325
+		return -1;
326
+	    } else if(!memcmp(eol - 7, " FOUND", 6)) {
327
+                static char last_filename[PATH_MAX+1] = {'\0'};
328
+		*(eol - 7) = 0;
329
+		*printok = 0;
330
+                if (scantype != ALLMATCH) {
331
+                    infected++;
332
+                } else {
333
+                    if (filename != NULL && strcmp(filename, last_filename)) {
334
+                        infected++;
335
+                        strncpy(last_filename, filename, PATH_MAX);
336
+                        last_filename[PATH_MAX] = '\0';
337
+                    }
338
+                }
339
+		if(filename) {
340
+		    if(scantype >= STREAM) {
341
+			logg("~%s%s FOUND\n", filename, colon);
342
+			if(action) action(filename);
343
+		    } else {
344
+			logg("~%s FOUND\n", bol);
345
+			*colon = '\0';
346
+			if(action)
347
+			    action(bol);
348
+		    }
349
+		}
350
+	    } else if(!memcmp(eol-7, " ERROR", 6)) {
351
+		if(errors)
352
+		    (*errors)++;
353
+		*printok = 0;
354
+		if(filename) {
355
+		    if(scantype >= STREAM)
356
+			logg("~%s%s\n", filename, colon);
357
+		    else
358
+			logg("~%s\n", bol);
359
+		}
360
+	    }
361
+	}
362
+    }
363
+    if(!beenthere) {
364
+        if (!filename) {
365
+	    logg("STDIN: noreply from clamd\n.");
366
+	    return -1;
367
+	}
368
+        if(CLAMSTAT(filename, &sb) == -1) {
369
+	    logg("~%s: stat() failed with %s, clamd may not be responding\n",
370
+		 filename, strerror(errno));
371
+	    return -1;
372
+	}
373
+	if(!S_ISDIR(sb.st_mode)) {
374
+	    logg("~%s: no reply from clamd\n", filename);
375
+	    return -1;
376
+	}
377
+    }
378
+    return infected;
379
+}
380
+
381
+/* Used by serial_callback() */
382
+struct client_serial_data {
383
+    int infected;
384
+    int scantype;
385
+    int printok;
386
+    int files;
387
+    int errors;
388
+};
389
+
390
+/* FTW callback for scanning in non IDSESSION mode
391
+ * Returns SUCCESS or BREAK on success, CL_EXXX on error */
392
+static int serial_callback(STATBUF *sb, char *filename, const char *path, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data) {
393
+    struct client_serial_data *c = (struct client_serial_data *)data->data;
394
+    int sockd, ret;
395
+    const char *f = filename;
396
+
397
+    UNUSEDPARAM(sb);
398
+
399
+    if(chkpath(path))
400
+	return CL_SUCCESS;
401
+    c->files++;
402
+    switch(reason) {
403
+    case error_stat:
404
+	logg("!Can't access file %s\n", path);
405
+	c->errors++;
406
+	return CL_SUCCESS;
407
+    case error_mem:
408
+	logg("!Memory allocation failed in ftw\n");
409
+	c->errors++;
410
+	return CL_EMEM;
411
+    case warning_skipped_dir:
412
+	logg("^Directory recursion limit reached\n");
413
+    case warning_skipped_link:
414
+	return CL_SUCCESS;
415
+    case warning_skipped_special:
416
+	logg("^%s: Not supported file type\n", path);
417
+	c->errors++;
418
+	return CL_SUCCESS;
419
+    case visit_directory_toplev:
420
+	if(c->scantype >= STREAM)
421
+	    return CL_SUCCESS;
422
+	f = path;
423
+	filename = NULL;
424
+    case visit_file:
425
+	break;
426
+    }
427
+
428
+    if((sockd = dconnect()) < 0) {
429
+	if(filename) free(filename);
430
+	c->errors++;
431
+	return CL_EOPEN;
432
+    }
433
+    ret = dsresult(sockd, c->scantype, f, &c->printok, &c->errors);
434
+    if(filename) free(filename);
435
+    closesocket(sockd);
436
+    if(ret < 0) {
437
+	c->errors++;
438
+	return CL_EOPEN;
439
+    }
440
+    c->infected += ret;
441
+    if(reason == visit_directory_toplev)
442
+	return CL_BREAK;
443
+    return CL_SUCCESS;
444
+}
445
+
446
+/* Non-IDSESSION handler
447
+ * Returns non zero for serious errors, zero otherwise */
448
+int serial_client_scan(char *file, int scantype, int *infected, int *err, int maxlevel, int flags) {
449
+    struct cli_ftw_cbdata data;
450
+    struct client_serial_data cdata;
451
+    int ftw;
452
+
453
+    cdata.infected = 0;
454
+    cdata.files = 0;
455
+    cdata.errors = 0;
456
+    cdata.printok = printinfected^1;
457
+    cdata.scantype = scantype;
458
+    data.data = &cdata;
459
+
460
+    ftw = cli_ftw(file, flags, maxlevel ? maxlevel : INT_MAX, serial_callback, &data, ftw_chkpath);
461
+    *infected += cdata.infected;
462
+    *err += cdata.errors;
463
+
464
+    if(!cdata.errors && (ftw == CL_SUCCESS || ftw == CL_BREAK)) {
465
+	if(cdata.printok)
466
+	    logg("~%s: OK\n", file);
467
+	return 0;
468
+    } else if(!cdata.files) {
469
+	logg("~%s: No files scanned\n", file);
470
+	return 0;
471
+    }
472
+    return 1;
473
+}
474
+
475
+/* Used in IDSESSION mode */
476
+struct client_parallel_data {
477
+    int infected;
478
+    int files;
479
+    int errors;
480
+    int scantype;
481
+    int sockd;
482
+    int lastid;
483
+    int printok;
484
+    struct SCANID {
485
+	unsigned int id;
486
+	const char *file;
487
+	struct SCANID *next;
488
+    } *ids;
489
+};
490
+
491
+/* Sends a proper scan request to clamd and parses its replies
492
+ * This is used only in IDSESSION mode
493
+ * Returns 0 on success, 1 on hard failures, 2 on len == 0 (bb#1717) */
494
+static int dspresult(struct client_parallel_data *c) {
495
+    const char *filename;
496
+    char *bol, *eol;
497
+    unsigned int rid;
498
+    int len;
499
+    struct SCANID **id = NULL;
500
+    struct RCVLN rcv;
501
+
502
+    recvlninit(&rcv, c->sockd);
503
+    do {
504
+	len = recvln(&rcv, &bol, &eol);
505
+	if(len < 0) return 1;
506
+	if(!len) return 2;
507
+	if((rid = atoi(bol))) {
508
+	    id = &c->ids;
509
+	    while(*id) {
510
+		if((*id)->id == rid) break;
511
+		id = &((*id)->next);
512
+	    }
513
+	    if(!*id) id = NULL;
514
+	}
515
+	if(!id) {
516
+	    logg("!Bogus session id from clamd\n");
517
+	    return 1;
518
+	}
519
+	filename = (*id)->file;
520
+	if(len > 7) {
521
+	    char *colon = strrchr(bol, ':');
522
+	    if(!colon) {
523
+		logg("!Failed to parse reply\n");
524
+		free((void *)filename);
525
+		return 1;
526
+	    } else if(!memcmp(eol - 7, " FOUND", 6)) {
527
+		c->infected++;
528
+		c->printok = 0;
529
+		logg("~%s%s\n", filename, colon);
530
+		if(action) action(filename);
531
+	    } else if(!memcmp(eol-7, " ERROR", 6)) {
532
+		c->errors++;
533
+		c->printok = 0;
534
+		logg("~%s%s\n", filename, colon);
535
+	    }
536
+	}
537
+	free((void *)filename);
538
+	bol = (char *)*id;
539
+	*id = (*id)->next;
540
+	free(bol);
541
+    } while(rcv.cur != rcv.buf); /* clamd sends whole lines, so, on partial lines, we just assume
542
+				    more data can be recv()'d with close to zero latency */
543
+    return 0;
544
+}
545
+
546
+/* FTW callback for scanning in IDSESSION mode
547
+ * Returns SUCCESS on success, CL_EXXX or BREAK on error */
548
+static int parallel_callback(STATBUF *sb, char *filename, const char *path, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data) {
549
+    struct client_parallel_data *c = (struct client_parallel_data *)data->data;
550
+    struct SCANID *cid;
551
+    int res = CL_CLEAN;
552
+
553
+    UNUSEDPARAM(sb);
554
+
555
+    if(chkpath(path))
556
+	return CL_SUCCESS;
557
+    c->files++;
558
+    switch(reason) {
559
+    case error_stat:
560
+	logg("!Can't access file %s\n", path);
561
+	c->errors++;
562
+	return CL_SUCCESS;
563
+    case error_mem:
564
+	logg("!Memory allocation failed in ftw\n");
565
+	c->errors++;
566
+	return CL_EMEM;
567
+    case warning_skipped_dir:
568
+	logg("^Directory recursion limit reached\n");
569
+	return CL_SUCCESS;
570
+    case warning_skipped_special:
571
+	logg("^%s: Not supported file type\n", path);
572
+	c->errors++;
573
+    case warning_skipped_link:
574
+    case visit_directory_toplev:
575
+	return CL_SUCCESS;
576
+    case visit_file:
577
+	break;
578
+    }
579
+
580
+    while(1) {
581
+	/* consume all the available input to let some of the clamd
582
+	 * threads blocked on send() to be dead.
583
+	 * by doing so we shouldn't deadlock on the next recv() */
584
+	fd_set rfds, wfds;
585
+	FD_ZERO(&rfds);
586
+	FD_SET(c->sockd, &rfds);
587
+	FD_ZERO(&wfds);
588
+	FD_SET(c->sockd, &wfds);
589
+	if(select(c->sockd + 1, &rfds, &wfds, NULL, NULL) < 0) {
590
+	    if(errno == EINTR) continue;
591
+	    free(filename);
592
+	    logg("!select() failed during session: %s\n", strerror(errno));
593
+	    return CL_BREAK;
594
+	}
595
+	if(FD_ISSET(c->sockd, &rfds)) {
596
+	    if(dspresult(c)) {
597
+		free(filename);
598
+		return CL_BREAK;
599
+	    } else continue;
600
+	}
601
+	if(FD_ISSET(c->sockd, &wfds)) break;
602
+    }
603
+
604
+    cid = (struct SCANID *)malloc(sizeof(struct SCANID));
605
+    if(!cid) {
606
+	free(filename);
607
+	logg("!Failed to allocate scanid entry: %s\n", strerror(errno));
608
+	return CL_BREAK;
609
+    }
610
+    cid->id = ++c->lastid;
611
+    cid->file = filename;
612
+    cid->next = c->ids;
613
+    c->ids = cid;
614
+
615
+    switch(c->scantype) {
616
+#ifdef HAVE_FD_PASSING
617
+    case FILDES:
618
+	res = send_fdpass(c->sockd, filename);
619
+	break;
620
+#endif
621
+    case STREAM:
622
+	res = send_stream(c->sockd, filename);
623
+	break;
624
+    }
625
+    if(res <= 0) {
626
+	c->printok = 0;
627
+	c->errors++;
628
+	c->ids = cid->next;
629
+	c->lastid--;
630
+	free(cid);
631
+	free(filename);
632
+	return res ? CL_BREAK : CL_SUCCESS;
633
+    }
634
+    return CL_SUCCESS;
635
+}
636
+
637
+/* IDSESSION handler
638
+ * Returns non zero for serious errors, zero otherwise */
639
+int parallel_client_scan(char *file, int scantype, int *infected, int *err, int maxlevel, int flags) {
640
+    struct cli_ftw_cbdata data;
641
+    struct client_parallel_data cdata;
642
+    int ftw;
643
+
644
+    if((cdata.sockd = dconnect()) < 0)
645
+	return 1;
646
+
647
+    if(sendln(cdata.sockd, "zIDSESSION", 11)) {
648
+	closesocket(cdata.sockd);
649
+	return 1;
650
+    }
651
+
652
+    cdata.infected = 0;
653
+    cdata.files = 0;
654
+    cdata.errors = 0;
655
+    cdata.scantype = scantype;
656
+    cdata.lastid = 0;
657
+    cdata.ids = NULL;
658
+    cdata.printok = printinfected^1;
659
+    data.data = &cdata;
660
+
661
+    ftw = cli_ftw(file, flags, maxlevel ? maxlevel : INT_MAX, parallel_callback, &data, ftw_chkpath);
662
+
663
+    if(ftw != CL_SUCCESS) {
664
+	*err += cdata.errors;
665
+	*infected += cdata.infected;
666
+	closesocket(cdata.sockd);
667
+	return 1;
668
+    }
669
+
670
+    sendln(cdata.sockd, "zEND", 5);
671
+    while(cdata.ids && !dspresult(&cdata));
672
+    closesocket(cdata.sockd);
673
+
674
+    *infected += cdata.infected;
675
+    *err += cdata.errors;
676
+
677
+    if(cdata.ids) {
678
+	logg("!Clamd closed the connection before scanning all files.\n");
679
+	return 1;
680
+    }
681
+    if(cdata.errors)
682
+	return 1;
683
+
684
+    if(!cdata.files)
685
+	return 0;
686
+
687
+    if(cdata.printok)
688
+	logg("~%s: OK\n", file);
689
+    return 0;
690
+}
0 691
new file mode 100644
... ...
@@ -0,0 +1,30 @@
0
+/*
1
+ *  Copyright (C) 2015 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
2
+ *  Copyright (C) 2009 Sourcefire, Inc.
3
+ *
4
+ *  Authors: Tomasz Kojm, aCaB
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 version 2 as
8
+ *  published by the Free Software Foundation.
9
+ *
10
+ *  This program is distributed in the hope that it will be useful,
11
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ *  GNU General Public License for more details.
14
+ *
15
+ *  You should have received a copy of the GNU General Public License
16
+ *  along with this program; if not, write to the Free Software
17
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18
+ *  MA 02110-1301, USA.
19
+ */
20
+
21
+#ifndef ONAS_PROTO_H
22
+#define ONAS_PROTO_H
23
+#include "shared/misc.h"
24
+
25
+int dconnect(void);
26
+int serial_client_scan(char *file, int scantype, int *infected, int *err, int maxlevel, int flags);
27
+int parallel_client_scan(char *file, int scantype, int *infected, int *err, int maxlevel, int flags);
28
+int dsresult(int sockd, int scantype, const char *filename, int *printok, int *errors);
29
+#endif