2
* Copyright (C) 2007-2009 Sourcefire, Inc.
4
* Authors: Tomasz Kojm, Török Edvin
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.
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.
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,
22
#include "clamav-config.h"
33
#include <sys/types.h>
38
#include <sys/param.h>
40
#include <sys/socket.h>
41
#include <netinet/in.h>
42
#include <arpa/inet.h>
47
#if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
52
#include <openssl/ssl.h>
53
#include <openssl/err.h>
54
#include "libclamav/crypto.h"
56
#include "libclamav/clamav.h"
57
#include "libclamav/others.h"
58
#include "libclamav/scanners.h"
60
#include "shared/optparser.h"
61
#include "shared/output.h"
62
#include "shared/misc.h"
71
dev_t procdev; /* /proc device */
75
extern time_t reloaded_time;
76
extern pthread_mutex_t reload_mutex;
78
void msg_callback(enum cl_msg severity, const char *fullmsg, const char *msg, void *ctx)
80
struct cb_context *c = ctx;
81
const char *filename = (c && c->filename) ? c->filename : "";
85
logg("^[LibClamAV] %s: %s", filename, msg);
88
logg("~[LibClamAV] %s: %s", filename, msg);
90
case CL_MSG_INFO_VERBOSE:
91
logg("*[LibClamAV] %s: %s", filename, msg);
94
logg("$[LibClamAV] %s: %s", filename, msg);
99
void hash_callback(int fd, unsigned long long size, const unsigned char *md5, const char *virname, void *ctx)
101
struct cb_context *c = ctx;
105
strncpy(c->virhash, md5, 32);
106
c->virhash[32] = '\0';
109
#define BUFFSIZE 1024
110
int scan_callback(STATBUF *sb, char *filename, const char *msg, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data)
112
struct scan_cb_data *scandata = data->data;
113
const char *virname = NULL;
114
const char **virpp = &virname;
116
int type = scandata->type;
117
struct cb_context context;
119
/* detect disconnected socket,
120
* this should NOT detect half-shutdown sockets (SHUT_WR) */
121
if (send(scandata->conn->sd, &ret, 0, 0) == -1 && errno != EINTR) {
122
logg("$Client disconnected while command was active!\n");
123
thrmgr_group_terminate(scandata->conn->group);
124
if (reason == visit_file)
129
if (thrmgr_group_need_terminate(scandata->conn->group)) {
130
logg("^Client disconnected while scanjob was active\n");
131
if (reason == visit_file)
139
logg("!Memory allocation failed during cli_ftw() on %s\n",
142
logg("!Memory allocation failed during cli_ftw()\n");
146
conn_reply_errno(scandata->conn, msg, "lstat() failed:");
147
logg("^lstat() failed on: %s\n", msg);
150
case warning_skipped_dir:
151
logg("^Directory recursion limit reached, skipping %s\n",
154
case warning_skipped_link:
155
logg("$Skipping symlink: %s\n", msg);
157
case warning_skipped_special:
158
if (msg == scandata->toplevel_path)
159
conn_reply(scandata->conn, msg, "Not supported file type", "ERROR");
160
logg("*Not supported file type: %s\n", msg);
162
case visit_directory_toplev:
168
/* check whether the file is excluded */
170
if(procdev && sb && (sb->st_dev == procdev)) {
176
if(sb && sb->st_size == 0) { /* empty file */
177
if (msg == scandata->toplevel_path)
178
conn_reply_single(scandata->conn, filename, "Empty file");
183
if (type == TYPE_MULTISCAN) {
184
client_conn_t *client_conn = (client_conn_t *) calloc(1, sizeof(struct client_conn_tag));
186
client_conn->scanfd = -1;
187
client_conn->sd = scandata->odesc;
188
client_conn->filename = filename;
189
client_conn->cmdtype = COMMAND_MULTISCANFILE;
190
client_conn->term = scandata->conn->term;
191
client_conn->options = scandata->options;
192
client_conn->opts = scandata->opts;
193
client_conn->group = scandata->group;
194
if(cl_engine_addref(scandata->engine)) {
195
logg("!cl_engine_addref() failed\n");
200
client_conn->engine = scandata->engine;
201
pthread_mutex_lock(&reload_mutex);
202
client_conn->engine_timestamp = reloaded_time;
203
pthread_mutex_unlock(&reload_mutex);
204
if(!thrmgr_group_dispatch(scandata->thr_pool, scandata->group, client_conn, 1)) {
205
logg("!thread dispatch failed\n");
206
cl_engine_free(scandata->engine);
213
logg("!Can't allocate memory for client_conn\n");
221
if (access(filename, R_OK)) {
222
if (conn_reply(scandata->conn, filename, "Access denied.", "ERROR") == -1) {
226
logg("*Access denied: %s\n", filename);
232
thrmgr_setactivetask(filename, NULL);
233
context.filename = filename;
235
ret = cl_scanfile_callback(filename, virpp, &scandata->scanned, scandata->engine, scandata->options, &context);
236
thrmgr_setactivetask(NULL, NULL);
238
if (scandata->options & CL_SCAN_ALLMATCHES) {
239
virpp = (const char **)*virpp; /* temp hack for scanall mode until api augmentation */
240
if (virpp) virname = virpp[0];
243
if (thrmgr_group_need_terminate(scandata->conn->group)) {
245
if ((scandata->options & CL_SCAN_ALLMATCHES) && (virpp != &virname))
247
logg("*Client disconnected while scanjob was active\n");
248
return ret == CL_ETIMEOUT ? ret : CL_BREAK;
251
if ((ret == CL_VIRUS) && (virname == NULL)) {
252
logg("*%s: reported CL_VIRUS but no virname returned!\n", filename);
256
if (ret == CL_VIRUS) {
257
scandata->infected++;
258
if (conn_reply_virus(scandata->conn, filename, virname) == -1) {
260
if((scandata->options & CL_SCAN_ALLMATCHES) && (virpp != &virname))
264
if (scandata->options & CL_SCAN_ALLMATCHES && virpp[1] != NULL) {
266
while (NULL != virpp[i])
267
if (conn_reply_virus(scandata->conn, filename, virpp[i++]) == -1) {
269
if (virpp != &virname)
274
if(context.virsize && optget(scandata->opts, "ExtendedDetectionInfo")->enabled)
275
logg("~%s: %s(%s:%llu) FOUND\n", filename, virname, context.virhash, context.virsize);
277
logg("~%s: %s FOUND\n", filename, virname);
278
virusaction(filename, virname, scandata->opts);
279
if (scandata->options & CL_SCAN_ALLMATCHES && virpp[1] != NULL) {
281
while (NULL != virpp[i])
282
logg("~%s: %s FOUND\n", filename, virpp[i++]);
284
} else if (ret != CL_CLEAN) {
286
if (conn_reply(scandata->conn, filename, cl_strerror(ret), "ERROR") == -1) {
287
if((scandata->options & CL_SCAN_ALLMATCHES) && (virpp != &virname))
292
logg("~%s: %s ERROR\n", filename, cl_strerror(ret));
294
logg("~%s: OK\n", filename);
298
if((scandata->options & CL_SCAN_ALLMATCHES) && (virpp != &virname))
300
if(ret == CL_EMEM) /* stop scanning */
303
if (type == TYPE_SCAN) {
308
/* keep scanning always */
312
int scan_pathchk(const char *path, struct cli_ftw_cbdata *data)
314
struct scan_cb_data *scandata = data->data;
315
const struct optstruct *opt;
318
if((opt = optget(scandata->opts, "ExcludePath"))->enabled) {
320
if(match_regex(path, opt->strarg) == 1) {
321
if(scandata->type != TYPE_MULTISCAN)
322
conn_reply_single(scandata->conn, path, "Excluded");
325
opt = (const struct optstruct *) opt->nextarg;
329
if(!optget(scandata->opts, "CrossFilesystems")->enabled) {
330
if(CLAMSTAT(path, &statbuf) == 0) {
331
if(statbuf.st_dev != scandata->dev) {
332
if(scandata->type != TYPE_MULTISCAN)
333
conn_reply_single(scandata->conn, path, "Excluded (another filesystem)");
342
int scanfd(const client_conn_t *conn, unsigned long int *scanned,
343
const struct cl_engine *engine,
344
unsigned int options, const struct optstruct *opts, int odesc, int stream)
346
int ret, fd = conn->scanfd;
349
struct cb_context context;
351
const char*reply_fdstr;
354
struct sockaddr_in sa;
355
socklen_t salen = sizeof(sa);
356
if(getpeername(conn->sd, (struct sockaddr *)&sa, &salen) || salen > sizeof(sa) || sa.sin_family != AF_INET)
357
strncpy(fdstr, "instream(local)", sizeof(fdstr));
359
snprintf(fdstr, sizeof(fdstr), "instream(%s@%u)", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
360
reply_fdstr = "stream";
362
snprintf(fdstr, sizeof(fdstr), "fd[%d]", fd);
365
if(FSTAT(fd, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) {
366
logg("%s: Not a regular file. ERROR\n", fdstr);
367
if (conn_reply(conn, reply_fdstr, "Not a regular file", "ERROR") == -1)
372
thrmgr_setactivetask(fdstr, NULL);
373
context.filename = fdstr;
375
ret = cl_scandesc_callback(fd, &virname, scanned, engine, options, &context);
376
thrmgr_setactivetask(NULL, NULL);
378
if (thrmgr_group_need_terminate(conn->group)) {
379
logg("*Client disconnected while scanjob was active\n");
380
return ret == CL_ETIMEOUT ? ret : CL_BREAK;
383
if(ret == CL_VIRUS) {
384
if (conn_reply_virus(conn, reply_fdstr, virname) == -1)
386
if(context.virsize && optget(opts, "ExtendedDetectionInfo")->enabled)
387
logg("%s: %s(%s:%llu) FOUND\n", fdstr, virname, context.virhash, context.virsize);
389
logg("%s: %s FOUND\n", fdstr, virname);
390
virusaction(reply_fdstr, virname, opts);
391
} else if(ret != CL_CLEAN) {
392
if (conn_reply(conn, reply_fdstr, cl_strerror(ret), "ERROR") == -1)
394
logg("%s: %s ERROR\n", fdstr, cl_strerror(ret));
396
if (conn_reply_single(conn, reply_fdstr, "OK") == CL_ETIMEOUT)
399
logg("%s: OK\n", fdstr);
404
int scanstream(int odesc, unsigned long int *scanned, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts, char term)
406
int ret, sockfd, acceptd;
407
int tmpd, bread, retval, firsttimeout, timeout, btread;
408
unsigned int port = 0, portscan, min_port, max_port;
409
unsigned long int quota = 0, maxsize = 0;
414
struct cb_context context;
415
struct sockaddr_in server;
416
struct sockaddr_in peer;
421
min_port = optget(opts, "StreamMinPort")->numarg;
422
max_port = optget(opts, "StreamMaxPort")->numarg;
424
/* search for a free port to bind to */
425
port = cli_rndnum(max_port - min_port);
427
for (portscan = 0; portscan < 1000; portscan++) {
428
port = (port - 1) % (max_port - min_port + 1);
430
memset((char *) &server, 0, sizeof(server));
431
server.sin_family = AF_INET;
432
server.sin_port = htons(min_port + port);
433
server.sin_addr.s_addr = htonl(INADDR_ANY);
435
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
438
if(bind(sockfd, (struct sockaddr *) &server, (socklen_t)sizeof(struct sockaddr_in)) == -1)
447
timeout = optget(opts, "ReadTimeout")->numarg;
448
firsttimeout = optget(opts, "CommandReadTimeout")->numarg;
451
logg("!ScanStream: Can't find any free port.\n");
452
mdprintf(odesc, "Can't find any free port. ERROR%c", term);
455
if (listen(sockfd, 1) == -1) {
456
logg("!ScanStream: listen() error on socket. Error returned is %s.\n", strerror(errno));
460
if(mdprintf(odesc, "PORT %u%c", port, term) <= 0) {
461
logg("!ScanStream: error transmitting port.\n");
467
retval = poll_fd(sockfd, firsttimeout, 0);
468
if (!retval || retval == -1) {
469
const char *reason = !retval ? "timeout" : "poll";
470
mdprintf(odesc, "Accept %s. ERROR%c", reason, term);
471
logg("!ScanStream %u: accept %s.\n", port, reason);
476
addrlen = sizeof(peer);
477
if((acceptd = accept(sockfd, (struct sockaddr *) &peer, (socklen_t *)&addrlen)) == -1) {
479
mdprintf(odesc, "accept() ERROR%c", term);
480
logg("!ScanStream %u: accept() failed.\n", port);
485
inet_ntop(peer.sin_family, &peer.sin_addr, peer_addr, sizeof(peer_addr));
486
logg("*Accepted connection from %s on port %u, fd %d\n", peer_addr, port, acceptd);
488
if(cli_gentempfd(optget(opts, "TemporaryDirectory")->strarg, &tmpname, &tmpd)) {
491
closesocket(acceptd);
492
mdprintf(odesc, "cli_gentempfd() failed. ERROR%c", term);
493
logg("!ScanStream(%s@%u): Can't create temporary file.\n", peer_addr, port);
497
quota = maxsize = optget(opts, "StreamMaxLength")->numarg;
499
while((retval = poll_fd(acceptd, timeout, 0)) == 1) {
500
/* only read up to max */
501
btread = (maxsize && (quota < sizeof(buff))) ? quota : sizeof(buff);
503
logg("^ScanStream(%s@%u): Size limit reached (max: %lu)\n", peer_addr, port, maxsize);
504
break; /* Scan what we have */
506
bread = recv(acceptd, buff, btread, 0);
512
if(writen(tmpd, buff, bread) != bread) {
515
closesocket(acceptd);
516
mdprintf(odesc, "Temporary file -> write ERROR%c", term);
517
logg("!ScanStream(%s@%u): Can't write to temporary file.\n", peer_addr, port);
519
if(!optget(opts, "LeaveTemporaryFiles")->enabled)
527
case 0: /* timeout */
528
mdprintf(odesc, "read timeout ERROR%c", term);
529
logg("!ScanStream(%s@%u): read timeout.\n", peer_addr, port);
532
mdprintf(odesc, "read poll ERROR%c", term);
533
logg("!ScanStream(%s@%u): read poll failed.\n", peer_addr, port);
538
lseek(tmpd, 0, SEEK_SET);
539
thrmgr_setactivetask(peer_addr, NULL);
540
context.filename = peer_addr;
542
ret = cl_scandesc_callback(tmpd, &virname, scanned, engine, options, &context);
543
thrmgr_setactivetask(NULL, NULL);
548
if(!optget(opts, "LeaveTemporaryFiles")->enabled)
552
closesocket(acceptd);
555
if(ret == CL_VIRUS) {
556
if(context.virsize && optget(opts, "ExtendedDetectionInfo")->enabled) {
557
mdprintf(odesc, "stream: %s(%s:%llu) FOUND%c", virname, context.virhash, context.virsize, term);
558
logg("stream(%s@%u): %s(%s:%llu) FOUND\n", peer_addr, port, virname, context.virhash, context.virsize);
560
mdprintf(odesc, "stream: %s FOUND%c", virname, term);
561
logg("stream(%s@%u): %s FOUND\n", peer_addr, port, virname);
563
virusaction("stream", virname, opts);
564
} else if(ret != CL_CLEAN) {
566
mdprintf(odesc, "stream: %s ERROR%c", cl_strerror(ret), term);
567
logg("stream(%s@%u): %s ERROR\n", peer_addr, port, cl_strerror(ret));
570
mdprintf(odesc, "stream: OK%c", term);
572
logg("stream(%s@%u): OK\n", peer_addr, port);