~ubuntu-branches/ubuntu/feisty/clamav/feisty

« back to all changes in this revision

Viewing changes to clamd/session.c

  • Committer: Bazaar Package Importer
  • Author(s): Kees Cook
  • Date: 2007-02-20 10:33:44 UTC
  • mto: This revision was merged to the branch mainline in revision 16.
  • Revision ID: james.westby@ubuntu.com-20070220103344-zgcu2psnx9d98fpa
Tags: upstream-0.90
ImportĀ upstreamĀ versionĀ 0.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
 *  Copyright (C) 2007-2009 Sourcefire, Inc.
3
 
 *
4
 
 *  Authors: Tomasz Kojm, Tƶrƶk Edvin
 
2
 *  Copyright (C) 2002 - 2005 Tomasz Kojm <tkojm@clamav.net>
5
3
 *
6
4
 *  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.
 
5
 *  it under the terms of the GNU General Public License as published by
 
6
 *  the Free Software Foundation; either version 2 of the License, or
 
7
 *  (at your option) any later version.
9
8
 *
10
9
 *  This program is distributed in the hope that it will be useful,
11
10
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18
17
 *  MA 02110-1301, USA.
19
18
 */
20
19
 
 
20
#ifdef  _MSC_VER
 
21
#include <winsock.h>
 
22
#endif
 
23
 
21
24
#if HAVE_CONFIG_H
22
25
#include "clamav-config.h"
23
26
#endif
29
32
#include <unistd.h>
30
33
#endif
31
34
#include <sys/types.h>
 
35
#ifndef C_WINDOWS
32
36
#include <dirent.h>
33
 
#ifndef _WIN32
34
37
#include <sys/socket.h>
35
 
#ifdef HAVE_SYS_SELECT_H
36
 
#include <sys/select.h>
37
 
#endif
38
 
#ifdef HAVE_FD_PASSING
39
 
#ifdef HAVE_SYS_UIO_H
40
 
#include <sys/uio.h>
41
 
#endif
42
 
#endif
43
 
 
44
38
#include <sys/time.h>
45
39
#endif
46
40
#include <pthread.h>
47
41
#include <time.h>
 
42
#include <signal.h>
48
43
#include <errno.h>
49
44
#include <stddef.h>
50
 
#include <limits.h>
51
45
 
52
46
#include "libclamav/clamav.h"
53
47
#include "libclamav/str.h"
54
 
#include "libclamav/others.h"
55
48
 
56
 
#include "shared/optparser.h"
 
49
#include "shared/cfgparser.h"
 
50
#include "shared/memory.h"
57
51
#include "shared/output.h"
58
52
#include "shared/misc.h"
59
53
 
60
54
#include "others.h"
61
55
#include "scanner.h"
62
56
#include "server.h"
 
57
#include "clamuko.h"
63
58
#include "session.h"
64
59
#include "thrmgr.h"
65
 
 
66
 
#ifndef HAVE_FDPASSING
67
 
#define FEATURE_FDPASSING 0
68
 
#else
69
 
#define FEATURE_FDPASSING 1
70
 
#endif
71
 
 
72
 
static struct {
73
 
    const char *cmd;
74
 
    const size_t len;
75
 
    enum commands cmdtype;
76
 
    int need_arg;
77
 
    int support_old;
78
 
    int enabled;
79
 
} commands[] = {
80
 
    {CMD1,  sizeof(CMD1)-1,     COMMAND_SCAN,       1,  1, 0},
81
 
    {CMD3,  sizeof(CMD3)-1,     COMMAND_SHUTDOWN,   0,  1, 0},
82
 
    {CMD4,  sizeof(CMD4)-1,     COMMAND_RELOAD,     0,  1, 0},
83
 
    {CMD5,  sizeof(CMD5)-1,     COMMAND_PING,       0,  1, 0},
84
 
    {CMD6,  sizeof(CMD6)-1,     COMMAND_CONTSCAN,   1,  1, 0},
85
 
    /* must be before VERSION, because they share common prefix! */
86
 
    {CMD18, sizeof(CMD18)-1,    COMMAND_COMMANDS,   0,  0, 1},
87
 
    {CMD7,  sizeof(CMD7)-1,     COMMAND_VERSION,    0,  1, 1},
88
 
    {CMD8,  sizeof(CMD8)-1,     COMMAND_STREAM,     0,  1, 1},
89
 
    {CMD10, sizeof(CMD10)-1,    COMMAND_END,        0,  0, 1},
90
 
    {CMD11, sizeof(CMD11)-1,    COMMAND_SHUTDOWN,   0,  1, 1},
91
 
    {CMD13, sizeof(CMD13)-1,    COMMAND_MULTISCAN,  1,  1, 1},
92
 
    {CMD14, sizeof(CMD14)-1,    COMMAND_FILDES,     0,  1, FEATURE_FDPASSING},
93
 
    {CMD15, sizeof(CMD15)-1,    COMMAND_STATS,      0,  0, 1},
94
 
    {CMD16, sizeof(CMD16)-1,    COMMAND_IDSESSION,  0,  0, 1},
95
 
    {CMD17, sizeof(CMD17)-1,    COMMAND_INSTREAM,   0,  0, 1},
96
 
    {CMD19, sizeof(CMD19)-1,    COMMAND_DETSTATSCLEAR,  0, 1, 1},
97
 
    {CMD20, sizeof(CMD20)-1,    COMMAND_DETSTATS,   0, 1, 1}
 
60
#include "shared.h"
 
61
 
 
62
static pthread_mutex_t ctime_mutex = PTHREAD_MUTEX_INITIALIZER;
 
63
extern int progexit;
 
64
 
 
65
struct multi_tag {
 
66
    int sd;
 
67
    unsigned int options;
 
68
    const struct cfgstruct *copt;
 
69
    char *fname;
 
70
    const struct cl_engine *engine;
 
71
    const struct cl_limits *limits;
98
72
};
99
73
 
100
 
enum commands parse_command(const char *cmd, const char **argument, int oldstyle)
101
 
{
102
 
    size_t i;
103
 
    *argument = NULL;
104
 
    for (i=0; i < sizeof(commands)/sizeof(commands[0]); i++) {
105
 
        const size_t len = commands[i].len;
106
 
        if (!strncmp(cmd, commands[i].cmd, len)) {
107
 
            const char *arg = cmd + len;
108
 
            if (commands[i].need_arg) {
109
 
                if (!*arg) {/* missing argument */
110
 
                    logg("$Command %s missing argument!\n", commands[i].cmd);
111
 
                    return COMMAND_UNKNOWN;
112
 
                }
113
 
                *argument = arg+1;
114
 
            } else {
115
 
                if (*arg) {/* extra stuff after command */
116
 
                    logg("$Command %s has trailing garbage!\n", commands[i].cmd);
117
 
                    return COMMAND_UNKNOWN;
118
 
                }
119
 
                *argument = NULL;
120
 
            }
121
 
            if (oldstyle && !commands[i].support_old) {
122
 
                logg("$Command sent as old-style when not supported: %s\n", commands[i].cmd);
123
 
                return COMMAND_UNKNOWN;
124
 
            }
125
 
            return commands[i].cmdtype;
126
 
        }
127
 
    }
128
 
    return COMMAND_UNKNOWN;
129
 
}
130
 
 
131
 
int conn_reply_single(const client_conn_t *conn, const char *path, const char *status)
132
 
{
133
 
    if (conn->id) {
134
 
        if (path)
135
 
            return mdprintf(conn->sd, "%u: %s: %s%c", conn->id, path, status, conn->term);
136
 
        return mdprintf(conn->sd, "%u: %s%c", conn->id, status, conn->term);
137
 
    }
138
 
    if (path)
139
 
        return mdprintf(conn->sd, "%s: %s%c", path, status, conn->term);
140
 
    return mdprintf(conn->sd, "%s%c", status, conn->term);
141
 
}
142
 
 
143
 
int conn_reply(const client_conn_t *conn, const char *path,
144
 
               const char *msg, const char *status)
145
 
{
146
 
    if (conn->id) {
147
 
        if (path)
148
 
            return mdprintf(conn->sd, "%u: %s: %s %s%c", conn->id, path, msg,
149
 
                            status, conn->term);
150
 
        return mdprintf(conn->sd, "%u: %s %s%c", conn->id, msg, status,
151
 
                        conn->term);
152
 
    }
153
 
    if (path)
154
 
        return mdprintf(conn->sd, "%s: %s %s%c", path, msg, status, conn->term);
155
 
    return mdprintf(conn->sd, "%s %s%c", msg, status, conn->term);
156
 
}
157
 
 
158
 
int conn_reply_virus(const client_conn_t *conn, const char *file,
159
 
               const char *virname)
160
 
{
161
 
    if (conn->id) {
162
 
        return mdprintf(conn->sd, "%u: %s: %s FOUND%c", conn->id, file, virname,
163
 
            conn->term);
164
 
    }
165
 
    return mdprintf(conn->sd, "%s: %s FOUND%c", file, virname, conn->term);
166
 
}
167
 
 
168
 
int conn_reply_error(const client_conn_t *conn, const char *msg)
169
 
{
170
 
    return conn_reply(conn, NULL, msg, "ERROR");
171
 
}
172
 
 
173
 
#define BUFFSIZE 1024
174
 
int conn_reply_errno(const client_conn_t *conn, const char *path,
175
 
                     const char *msg)
176
 
{
177
 
    char err[BUFFSIZE + sizeof(". ERROR")];
178
 
    cli_strerror(errno, err, BUFFSIZE-1);
179
 
    strcat(err, ". ERROR");
180
 
    return conn_reply(conn, path, msg, err);
181
 
}
182
 
 
183
 
/* returns
184
 
 *  -1 on fatal error (shutdown)
185
 
 *  0 on ok
186
 
 *  >0 errors encountered
187
 
 */
188
 
int command(client_conn_t *conn, int *virus)
189
 
{
190
 
    int desc = conn->sd;
191
 
    struct cl_engine *engine = conn->engine;
192
 
    unsigned int options = conn->options;
193
 
    const struct optstruct *opts = conn->opts;
194
 
    int type = -1; /* TODO: make this enum */
195
 
    int maxdirrec;
196
 
    int ret = 0;
197
 
    int flags = CLI_FTW_STD;
198
 
 
199
 
    struct scan_cb_data scandata;
200
 
    struct cli_ftw_cbdata data;
201
 
    unsigned ok, error, total;
202
 
    struct stat sb;
203
 
    jobgroup_t *group = NULL;
204
 
 
205
 
    if (thrmgr_group_need_terminate(conn->group)) {
206
 
        logg("$Client disconnected while command was active\n");
207
 
        if (conn->scanfd != -1)
208
 
            close(conn->scanfd);
209
 
        return 1;
210
 
    }
211
 
    thrmgr_setactiveengine(engine);
212
 
 
213
 
    data.data = &scandata;
214
 
    memset(&scandata, 0, sizeof(scandata));
215
 
    scandata.id = conn->id;
216
 
    scandata.group = conn->group;
217
 
    scandata.odesc = desc;
218
 
    scandata.conn = conn;
219
 
    scandata.options = options;
220
 
    scandata.engine = engine;
221
 
    scandata.opts = opts;
222
 
    scandata.thr_pool = conn->thrpool;
223
 
    scandata.toplevel_path = conn->filename;
224
 
 
225
 
    switch (conn->cmdtype) {
226
 
        case COMMAND_SCAN:
227
 
            thrmgr_setactivetask(NULL, "SCAN");
228
 
            type = TYPE_SCAN;
229
 
            break;
230
 
        case COMMAND_CONTSCAN:
231
 
            thrmgr_setactivetask(NULL, "CONTSCAN");
232
 
            type = TYPE_CONTSCAN;
233
 
            break;
234
 
        case COMMAND_MULTISCAN: {
235
 
            int multiscan, max, alive;
 
74
static void multiscanfile(void *arg)
 
75
{
 
76
        struct multi_tag *tag = (struct multi_tag *) arg;
 
77
        const char *virname;
 
78
#ifndef C_WINDOWS
 
79
        sigset_t sigset;
 
80
#endif
 
81
        int ret;
 
82
 
 
83
 
 
84
#ifndef C_WINDOWS
 
85
    /* ignore all signals */
 
86
    sigfillset(&sigset);
 
87
    pthread_sigmask(SIG_SETMASK, &sigset, NULL);
 
88
#endif
 
89
 
 
90
    ret = cl_scanfile(tag->fname, &virname, NULL, tag->engine, tag->limits, tag->options);
 
91
 
 
92
    if(ret == CL_VIRUS) {
 
93
        mdprintf(tag->sd, "%s: %s FOUND\n", tag->fname, virname);
 
94
        logg("%s: %s FOUND\n", tag->fname, virname);
 
95
        virusaction(tag->fname, virname, tag->copt);
 
96
    } else if(ret != CL_CLEAN) {
 
97
        mdprintf(tag->sd, "%s: %s ERROR\n", tag->fname, cl_strerror(ret));
 
98
        logg("%s: %s ERROR\n", tag->fname, cl_strerror(ret));
 
99
    } else if(logok) {
 
100
        logg("%s: OK\n", tag->fname);
 
101
    }
 
102
 
 
103
    free(tag->fname);
 
104
    free(tag);
 
105
    return;
 
106
}
 
107
 
 
108
static int multiscan(const char *dirname, const struct cl_engine *engine, const struct cl_limits *limits, unsigned int options, const struct cfgstruct *copt, int odesc, unsigned int *reclev, threadpool_t *multi_pool)
 
109
{
 
110
        DIR *dd;
 
111
        struct dirent *dent;
 
112
#if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
 
113
        union {
 
114
            struct dirent d;
 
115
            char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
 
116
        } result;
 
117
#endif
 
118
        struct stat statbuf;
 
119
        char *fname;
 
120
        int scanret = 0;
 
121
        unsigned int maxdirrec = 0;
 
122
        struct multi_tag *scandata;
 
123
 
 
124
 
 
125
    maxdirrec = cfgopt(copt, "MaxDirectoryRecursion")->numarg;
 
126
    if(maxdirrec) {
 
127
        if(*reclev > maxdirrec) {
 
128
            logg("*multiscan: Directory recursion limit exceeded at %s\n", dirname);
 
129
            return 0;
 
130
        }
 
131
        (*reclev)++;
 
132
    }
 
133
 
 
134
    if((dd = opendir(dirname)) != NULL) {
 
135
#ifdef HAVE_READDIR_R_3
 
136
        while(!readdir_r(dd, &result.d, &dent) && dent) {
 
137
#elif defined(HAVE_READDIR_R_2)
 
138
        while((dent = (struct dirent *) readdir_r(dd, &result.d))) {
 
139
#else
 
140
        while((dent = readdir(dd))) {
 
141
#endif
 
142
            if (!is_fd_connected(odesc)) {
 
143
                logg("multiscan: Client disconnected\n");
 
144
                closedir(dd);
 
145
                return -1;
 
146
            }
 
147
 
 
148
            if(progexit) {
 
149
                closedir(dd);
 
150
                return -1;
 
151
            }
 
152
 
 
153
#if     (!defined(C_INTERIX)) && (!defined(C_WINDOWS)) && (!defined(C_CYGWIN))
 
154
            if(dent->d_ino)
 
155
#endif
 
156
            {
 
157
                if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) {
 
158
                    /* build the full name */
 
159
                    fname = (char *) mcalloc(strlen(dirname) + strlen(dent->d_name) + 2, sizeof(char));
 
160
                    if(!fname) {
 
161
                        logg("!multiscan: Can't allocate memory for fname\n");
 
162
                        closedir(dd);
 
163
                        return -1;
 
164
                    }
 
165
                    sprintf(fname, "%s/%s", dirname, dent->d_name);
 
166
 
 
167
                    /* stat the file */
 
168
                    if(lstat(fname, &statbuf) != -1) {
 
169
                        if((S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) || (S_ISLNK(statbuf.st_mode) && (checksymlink(fname) == 1) && cfgopt(copt, "FollowDirectorySymlinks")->enabled)) {
 
170
                            if(multiscan(fname, engine, limits, options, copt, odesc, reclev, multi_pool) == -1) {
 
171
                                free(fname);
 
172
                                closedir(dd);
 
173
                                return -1;
 
174
                            }
 
175
                            free(fname);
 
176
                        } else {
 
177
                            if(S_ISREG(statbuf.st_mode) || (S_ISLNK(statbuf.st_mode) && (checksymlink(fname) == 2) && cfgopt(copt, "FollowFileSymlinks")->enabled)) {
 
178
 
 
179
#ifdef C_LINUX
 
180
                                if(procdev && (statbuf.st_dev == procdev))
 
181
                                    scanret = CL_CLEAN;
 
182
                                else
 
183
#endif
 
184
                                {
 
185
                                    scandata = (struct multi_tag *) mmalloc(sizeof(struct multi_tag));
 
186
                                    if(!scandata) {
 
187
                                        logg("!multiscan: Can't allocate memory for scandata\n");
 
188
                                        free(fname);
 
189
                                        closedir(dd);
 
190
                                        return -1;
 
191
                                    }
 
192
                                    scandata->sd = odesc;
 
193
                                    scandata->options = options;
 
194
                                    scandata->copt = copt;
 
195
                                    scandata->fname = fname;
 
196
                                    scandata->engine = engine;
 
197
                                    scandata->limits = limits;
 
198
                                    if(!thrmgr_dispatch(multi_pool, scandata)) {
 
199
                                        logg("!multiscan: thread dispatch failed for multi_pool (file %s)\n", fname);
 
200
                                        mdprintf(odesc, "ERROR: Can't scan file %s\n", fname);
 
201
                                        free(fname);
 
202
                                        free(scandata);
 
203
                                        closedir(dd);
 
204
                                        return -1;
 
205
                                    }
 
206
 
 
207
                                    while(!multi_pool->thr_idle) /* non-critical */
 
208
#ifdef C_WINDOWS
 
209
                                        Sleep(1);
 
210
#else
 
211
                                        usleep(200);
 
212
#endif
 
213
                                }
 
214
                            }
 
215
                        }
 
216
                    } else {
 
217
                        free(fname);
 
218
                    }
 
219
                }
 
220
            }
 
221
        }
 
222
        closedir(dd);
 
223
    } else {
 
224
        return -2;
 
225
    }
 
226
 
 
227
    (*reclev)--;
 
228
    return 0;
 
229
}
 
230
 
 
231
int command(int desc, const struct cl_engine *engine, const struct cl_limits *limits, unsigned int options, const struct cfgstruct *copt, int timeout)
 
232
{
 
233
        char buff[1025];
 
234
        int bread, opt;
 
235
 
 
236
 
 
237
    bread = readsock(desc, buff, sizeof(buff)-1, '\n', timeout, 0, 1);
 
238
    if(bread == -2) /* timeout */
 
239
        return -2;
 
240
    if(bread == 0) /* Connection closed */
 
241
        return -1;
 
242
    if(bread < 0) {
 
243
        mdprintf(desc, "ERROR\n");
 
244
        logg("!Command: readsock() failed.\n");
 
245
        return -1;
 
246
    }
 
247
 
 
248
    buff[bread] = 0;
 
249
    cli_chomp(buff);
 
250
 
 
251
    if(!strncmp(buff, CMD1, strlen(CMD1))) { /* SCAN */
 
252
        if(scan(buff + strlen(CMD1) + 1, NULL, engine, limits, options, copt, desc, 0) == -2)
 
253
            if(cfgopt(copt, "ExitOnOOM")->enabled)
 
254
                return COMMAND_SHUTDOWN;
 
255
 
 
256
    } else if(!strncmp(buff, CMD2, strlen(CMD2))) { /* RAWSCAN */
 
257
        opt = options & ~CL_SCAN_ARCHIVE;
 
258
        if(scan(buff + strlen(CMD2) + 1, NULL, engine, NULL, opt, copt, desc, 0) == -2)
 
259
            if(cfgopt(copt, "ExitOnOOM")->enabled)
 
260
                return COMMAND_SHUTDOWN;
 
261
 
 
262
    } else if(!strncmp(buff, CMD3, strlen(CMD3))) { /* QUIT */
 
263
        return COMMAND_SHUTDOWN;
 
264
 
 
265
    } else if(!strncmp(buff, CMD4, strlen(CMD4))) { /* RELOAD */
 
266
        mdprintf(desc, "RELOADING\n");
 
267
        return COMMAND_RELOAD;
 
268
 
 
269
    } else if(!strncmp(buff, CMD5, strlen(CMD5))) { /* PING */
 
270
        mdprintf(desc, "PONG\n");
 
271
 
 
272
    } else if(!strncmp(buff, CMD6, strlen(CMD6))) { /* CONTSCAN */
 
273
        if(scan(buff + strlen(CMD6) + 1, NULL, engine, limits, options, copt, desc, 1) == -2)
 
274
            if(cfgopt(copt, "ExitOnOOM")->enabled)
 
275
                return COMMAND_SHUTDOWN;
 
276
 
 
277
    } else if(!strncmp(buff, CMD7, strlen(CMD7))) { /* VERSION */
 
278
            const char *dbdir = cfgopt(copt, "DatabaseDirectory")->strarg;
 
279
            char *path;
 
280
            struct cl_cvd *daily;
 
281
            struct stat foo;
 
282
 
 
283
 
 
284
        if(!(path = mmalloc(strlen(dbdir) + 30))) {
 
285
            mdprintf(desc, "Memory allocation error - SHUTDOWN forced\n");
 
286
            return COMMAND_SHUTDOWN;
 
287
        }
 
288
 
 
289
        sprintf(path, "%s/daily.cvd", dbdir);
 
290
        if(stat(path, &foo) == -1)
 
291
            sprintf(path, "%s/daily.inc/daily.info", dbdir);
 
292
 
 
293
        if((daily = cl_cvdhead(path))) {
 
294
                time_t t = (time_t) daily->stime;
 
295
 
 
296
            pthread_mutex_lock(&ctime_mutex);
 
297
            mdprintf(desc, "ClamAV "VERSION"/%d/%s", daily->version, ctime(&t));
 
298
            pthread_mutex_unlock(&ctime_mutex);
 
299
            cl_cvdfree(daily);
 
300
        } else {
 
301
            mdprintf(desc, "ClamAV "VERSION"\n");
 
302
        }
 
303
 
 
304
        free(path);
 
305
 
 
306
    } else if(!strncmp(buff, CMD8, strlen(CMD8))) { /* STREAM */
 
307
        if(scanstream(desc, NULL, engine, limits, options, copt) == CL_EMEM)
 
308
            if(cfgopt(copt, "ExitOnOOM")->enabled)
 
309
                return COMMAND_SHUTDOWN;
 
310
 
 
311
    } else if(!strncmp(buff, CMD9, strlen(CMD9))) { /* SESSION */
 
312
        return COMMAND_SESSION;
 
313
 
 
314
    } else if(!strncmp(buff, CMD10, strlen(CMD10))) { /* END */
 
315
        return COMMAND_END;
 
316
 
 
317
    } else if(!strncmp(buff, CMD11, strlen(CMD11))) { /* SHUTDOWN */
 
318
        return COMMAND_SHUTDOWN;
 
319
 
 
320
    } else if(!strncmp(buff, CMD12, strlen(CMD12))) { /* FD */
 
321
            int fd = atoi(buff + strlen(CMD12) + 1);
 
322
 
 
323
        scanfd(fd, NULL, engine, limits, options, copt, desc);
 
324
        close(fd); /* FIXME: should we close it here? */
 
325
 
 
326
    } else if(!strncmp(buff, CMD13, strlen(CMD13))) { /* MULTISCAN */
 
327
            threadpool_t *multi_pool;
 
328
            int idletimeout = cfgopt(copt, "IdleTimeout")->numarg;
 
329
            int max_threads = cfgopt(copt, "MaxThreads")->numarg;
 
330
            int ret;
 
331
            unsigned int reclev = 0;
 
332
            const char *path = buff + strlen(CMD13) + 1;
 
333
            const char *virname;
236
334
            struct stat sb;
237
335
 
238
 
            /* use MULTISCAN only for directories (bb #1869) */
239
 
            if (stat(conn->filename, &sb) == 0 &&
240
 
                !S_ISDIR(sb.st_mode)) {
241
 
                thrmgr_setactivetask(NULL, "CONTSCAN");
242
 
                type = TYPE_CONTSCAN;
243
 
                break;
244
 
            }
245
 
 
246
 
            pthread_mutex_lock(&conn->thrpool->pool_mutex);
247
 
            multiscan = conn->thrpool->thr_multiscan;
248
 
            max = conn->thrpool->thr_max;
249
 
            if (multiscan+1 < max)
250
 
                conn->thrpool->thr_multiscan = multiscan+1;
251
 
            else {
252
 
                alive = conn->thrpool->thr_alive;
253
 
                ret = -1;
254
 
            }
255
 
            pthread_mutex_unlock(&conn->thrpool->pool_mutex);
256
 
            if (ret) {
257
 
                /* multiscan has 1 control thread, so there needs to be at least
258
 
                   1 threads that is a non-multiscan controlthread to scan and
259
 
                   make progress. */
260
 
                logg("^Not enough threads for multiscan. Max: %d, Alive: %d, Multiscan: %d+1\n",
261
 
                     max, alive, multiscan);
262
 
                conn_reply(conn, conn->filename, "Not enough threads for multiscan. Increase MaxThreads.", "ERROR");
263
 
                return 1;
264
 
            }
265
 
            flags &= ~CLI_FTW_NEED_STAT;
266
 
            thrmgr_setactivetask(NULL, "MULTISCAN");
267
 
            type = TYPE_MULTISCAN;
268
 
            scandata.group = group = thrmgr_group_new();
269
 
            if (!group)
270
 
                return CL_EMEM;
271
 
            break;
272
 
            }
273
 
        case COMMAND_MULTISCANFILE:
274
 
            thrmgr_setactivetask(NULL, "MULTISCANFILE");
275
 
            scandata.group = NULL;
276
 
            scandata.type = TYPE_SCAN;
277
 
            scandata.thr_pool = NULL;
278
 
            /* TODO: check ret value */
279
 
            ret = scan_callback(NULL, conn->filename, conn->filename, visit_file, &data);           /* callback freed it */
280
 
            conn->filename = NULL;
281
 
            *virus = scandata.infected;
282
 
            if (ret == CL_BREAK) {
283
 
                thrmgr_group_terminate(conn->group);
284
 
                return CL_BREAK;
285
 
            }
286
 
            return scandata.errors > 0 ? scandata.errors : 0;
287
 
        case COMMAND_FILDES:
288
 
            thrmgr_setactivetask(NULL, "FILDES");
289
 
#ifdef HAVE_FD_PASSING
290
 
            if (conn->scanfd == -1)
291
 
                conn_reply_error(conn, "FILDES: didn't receive file descriptor.");
292
 
            else {
293
 
                ret = scanfd(conn->scanfd, conn, NULL, engine, options, opts, desc, 0);
294
 
                if (ret == CL_VIRUS) {
295
 
                    *virus = 1;
296
 
                } else if (ret == CL_EMEM) {
297
 
                    if(optget(opts, "ExitOnOOM")->enabled)
298
 
                        ret = -1;
299
 
                } else if (ret == CL_ETIMEOUT) {
300
 
                        thrmgr_group_terminate(conn->group);
301
 
                        ret = 1;
302
 
                } else
303
 
                    ret = 0;
304
 
            }
305
 
            logg("$Closed fd %d\n", conn->scanfd);
306
 
            close(conn->scanfd);
307
 
            return ret;
308
 
#else
309
 
            conn_reply_error(conn, "FILDES support not compiled in.");
310
 
            close(conn->scanfd);
311
 
            return 0;
312
 
#endif
313
 
        case COMMAND_STATS:
314
 
            thrmgr_setactivetask(NULL, "STATS");
315
 
            if (conn->group)
316
 
                mdprintf(desc, "%u: ", conn->id);
317
 
            thrmgr_printstats(desc, conn->term);
318
 
            return 0;
319
 
        case COMMAND_STREAM:
320
 
            thrmgr_setactivetask(NULL, "STREAM");
321
 
            ret = scanstream(desc, NULL, engine, options, opts, conn->term);
322
 
            if (ret == CL_VIRUS)
323
 
                *virus = 1;
324
 
            if (ret == CL_EMEM) {
325
 
                if(optget(opts, "ExitOnOOM")->enabled)
326
 
                    return -1;
327
 
            }
328
 
            return 0;
329
 
        case COMMAND_INSTREAMSCAN:
330
 
            thrmgr_setactivetask(NULL, "INSTREAM");
331
 
            ret = scanfd(conn->scanfd, conn, NULL, engine, options, opts, desc, 1);
332
 
            if (ret == CL_VIRUS) {
333
 
                *virus = 1;
334
 
            } else if (ret == CL_EMEM) {
335
 
                if(optget(opts, "ExitOnOOM")->enabled)
336
 
                    ret = -1;
337
 
            } else if (ret == CL_ETIMEOUT) {
338
 
                thrmgr_group_terminate(conn->group);
339
 
                ret = 1;
340
 
            } else
341
 
                ret = 0;
342
 
            if (ftruncate(conn->scanfd, 0) == -1) {
343
 
                /* not serious, we're going to close it and unlink it anyway */
344
 
                logg("*ftruncate failed: %d\n", errno);
345
 
            }
346
 
            close(conn->scanfd);
347
 
            conn->scanfd = -1;
348
 
            cli_unlink(conn->filename);
349
 
            return ret;
350
 
        default:
351
 
            logg("!Invalid command distpached: %d\n", conn->cmdtype);
352
 
            return 1;
353
 
    }
354
 
 
355
 
    scandata.type = type;
356
 
    maxdirrec = optget(opts, "MaxDirectoryRecursion")->numarg;
357
 
    if (optget(opts, "FollowDirectorySymlinks")->enabled)
358
 
        flags |= CLI_FTW_FOLLOW_DIR_SYMLINK;
359
 
    if (optget(opts, "FollowFileSymlinks")->enabled)
360
 
        flags |= CLI_FTW_FOLLOW_FILE_SYMLINK;
361
 
 
362
 
    if(!optget(opts, "CrossFilesystems")->enabled)
363
 
        if(stat(conn->filename, &sb) == 0)
364
 
            scandata.dev = sb.st_dev;
365
 
 
366
 
    ret = cli_ftw(conn->filename, flags,  maxdirrec ? maxdirrec : INT_MAX, scan_callback, &data, scan_pathchk);
367
 
    if (ret == CL_EMEM)
368
 
        if(optget(opts, "ExitOnOOM")->enabled)
 
336
        if(stat(path, &sb) == -1) {
 
337
            mdprintf(desc, "Can't stat file %s\n", path);
369
338
            return -1;
370
 
    if (scandata.group && conn->cmdtype == COMMAND_MULTISCAN) {
371
 
        thrmgr_group_waitforall(group, &ok, &error, &total);
372
 
        pthread_mutex_lock(&conn->thrpool->pool_mutex);
373
 
        conn->thrpool->thr_multiscan--;
374
 
        pthread_mutex_unlock(&conn->thrpool->pool_mutex);
 
339
        }
 
340
 
 
341
        if(S_ISDIR(sb.st_mode)) {
 
342
            if((multi_pool = thrmgr_new(max_threads, idletimeout, multiscanfile)) == NULL) {
 
343
                logg("!thrmgr_new failed for multi_pool\n");
 
344
                mdprintf(desc, "ERROR: thrmgr_new failed for multi_pool\n");
 
345
                return -1;
 
346
            }
 
347
 
 
348
            ret = multiscan(path, engine, limits, options, copt, desc, &reclev, multi_pool);
 
349
            thrmgr_destroy(multi_pool);
 
350
 
 
351
            if(ret < 0)
 
352
                return -1;
 
353
 
 
354
        } else {
 
355
            ret = cl_scanfile(path, &virname, NULL, engine, limits, options);
 
356
 
 
357
            if(ret == CL_VIRUS) {
 
358
                mdprintf(desc, "%s: %s FOUND\n", path, virname);
 
359
                logg("%s: %s FOUND\n", path, virname);
 
360
                virusaction(path, virname, copt);
 
361
            } else if(ret != CL_CLEAN) {
 
362
                mdprintf(desc, "%s: %s ERROR\n", path, cl_strerror(ret));
 
363
                logg("%s: %s ERROR\n", path, cl_strerror(ret));
 
364
            } else {
 
365
                mdprintf(desc, "%s: OK\n", path); 
 
366
                if(logok)
 
367
                    logg("%s: OK\n", path);
 
368
            }
 
369
        }
 
370
 
375
371
    } else {
376
 
        error = scandata.errors;
377
 
        total = scandata.total;
378
 
        ok = total - error - scandata.infected;
379
 
    }
380
 
 
381
 
    if (ok + error == total && (error != total)) {
382
 
        if (conn_reply_single(conn, conn->filename, "OK") == -1)
383
 
            ret = CL_ETIMEOUT;
384
 
    }
385
 
    *virus = total - (ok + error);
386
 
 
387
 
    if (ret == CL_ETIMEOUT)
388
 
        thrmgr_group_terminate(conn->group);
389
 
    return error;
390
 
}
391
 
 
392
 
static int dispatch_command(client_conn_t *conn, enum commands cmd, const char *argument)
393
 
{
394
 
    int ret = 0;
395
 
    int bulk;
396
 
    client_conn_t *dup_conn = (client_conn_t *) malloc(sizeof(struct client_conn_tag));
397
 
 
398
 
    if(!dup_conn) {
399
 
        logg("!Can't allocate memory for client_conn\n");
400
 
        return -1;
401
 
    }
402
 
    memcpy(dup_conn, conn, sizeof(*conn));
403
 
    dup_conn->cmdtype = cmd;
404
 
    if(cl_engine_addref(dup_conn->engine)) {
405
 
        logg("!cl_engine_addref() failed\n");
406
 
        free(dup_conn);
407
 
        return -1;
408
 
    }
409
 
    dup_conn->scanfd = -1;
410
 
    bulk = 1;
411
 
    switch (cmd) {
412
 
        case COMMAND_FILDES:
413
 
            if (conn->scanfd == -1) {
414
 
                conn_reply_error(dup_conn, "No file descriptor received.");
415
 
                ret = 1;
416
 
            }
417
 
            dup_conn->scanfd = conn->scanfd;
418
 
            /* consume FD */
419
 
            conn->scanfd = -1;
420
 
            break;
421
 
        case COMMAND_SCAN:
422
 
        case COMMAND_CONTSCAN:
423
 
        case COMMAND_MULTISCAN:
424
 
            dup_conn->filename = strdup(argument);
425
 
            if (!dup_conn->filename) {
426
 
                logg("!Failed to allocate memory for filename\n");
427
 
                ret = -1;
428
 
            }
429
 
            break;
430
 
        case COMMAND_INSTREAMSCAN:
431
 
            dup_conn->scanfd = conn->scanfd;
432
 
            conn->scanfd = -1;
433
 
            break;
434
 
        case COMMAND_STREAM:
435
 
        case COMMAND_STATS:
436
 
            /* not a scan command, don't queue to bulk */
437
 
            bulk = 0;
438
 
            /* just dispatch the command */
439
 
            break;
440
 
        default:
441
 
            logg("!Invalid command dispatch: %d\n", cmd);
442
 
            ret = -2;
443
 
            break;
444
 
    }
445
 
    if (!dup_conn->group)
446
 
        bulk = 0;
447
 
    if(!ret && !thrmgr_group_dispatch(dup_conn->thrpool, dup_conn->group, dup_conn, bulk)) {
448
 
        logg("!thread dispatch failed\n");
449
 
        ret = -2;
450
 
    }
451
 
    if (ret) {
452
 
        cl_engine_free(dup_conn->engine);
453
 
        free(dup_conn);
454
 
    }
455
 
    return ret;
456
 
}
457
 
 
458
 
static int print_ver(int desc, char term, const struct cl_engine *engine)
459
 
{
460
 
    uint32_t ver;
461
 
 
462
 
    ver = cl_engine_get_num(engine, CL_ENGINE_DB_VERSION, NULL);
463
 
    if(ver) {
464
 
        char timestr[32];
465
 
        const char *tstr;
466
 
        time_t t;
467
 
        t = cl_engine_get_num(engine, CL_ENGINE_DB_TIME, NULL);
468
 
        tstr = cli_ctime(&t, timestr, sizeof(timestr));
469
 
        /* cut trailing \n */
470
 
        timestr[strlen(tstr)-1] = '\0';
471
 
        return mdprintf(desc, "ClamAV %s/%u/%s%c", get_version(), (unsigned int) ver, tstr, term);
472
 
    }
473
 
    return mdprintf(desc, "ClamAV %s%c", get_version(), term);
474
 
}
475
 
 
476
 
static void print_commands(int desc, char term, const struct cl_engine *engine)
477
 
{
478
 
    unsigned i, n;
479
 
    const char *engine_ver = cl_retver();
480
 
    const char *clamd_ver = get_version();
481
 
    if (strcmp(engine_ver, clamd_ver)) {
482
 
        mdprintf(desc, "ENGINE VERSION MISMATCH: %s != %s. ERROR%c",
483
 
                 engine_ver, clamd_ver, term);
484
 
        return;
485
 
    }
486
 
    print_ver(desc, '|', engine);
487
 
    mdprintf(desc, " COMMANDS:");
488
 
    n = sizeof(commands)/sizeof(commands[0]);
489
 
    for (i=0;i<n;i++) {
490
 
        mdprintf(desc, " %s", commands[i].cmd);
491
 
    }
492
 
    mdprintf(desc, "%c", term);
493
 
}
494
 
 
495
 
/* returns:
496
 
 *  <0 for error
497
 
 *     -1 out of memory
498
 
 *     -2 other
499
 
 *   0 for async dispatched
500
 
 *   1 for command completed (connection can be closed)
501
 
 */
502
 
int execute_or_dispatch_command(client_conn_t *conn, enum commands cmd, const char *argument)
503
 
{
504
 
    int desc = conn->sd;
505
 
    char term = conn->term;
506
 
    const struct cl_engine *engine = conn->engine;
507
 
    /* execute commands that can be executed quickly on the recvloop thread,
508
 
     * these must:
509
 
     *  - not involve any operation that can block for a long time, such as disk
510
 
     *  I/O
511
 
     *  - send of atomic message is allowed.
512
 
     * Dispatch other commands */
513
 
    if (conn->group) {
514
 
        switch (cmd) {
515
 
            case COMMAND_FILDES:
516
 
            case COMMAND_SCAN:
517
 
            case COMMAND_END:
518
 
            case COMMAND_INSTREAM:
519
 
            case COMMAND_INSTREAMSCAN:
520
 
            case COMMAND_VERSION:
521
 
            case COMMAND_PING:
522
 
            case COMMAND_STATS:
523
 
            case COMMAND_COMMANDS:
524
 
                /* These commands are accepted inside IDSESSION */
525
 
                break;
526
 
            default:
527
 
                /* these commands are not recognized inside an IDSESSION */
528
 
                conn_reply_error(conn, "Command invalid inside IDSESSION.");
529
 
                logg("$SESSION: command is not valid inside IDSESSION: %d\n", cmd);
530
 
                conn->group = NULL;
531
 
                return 1;
532
 
        }
533
 
    }
534
 
 
535
 
    switch (cmd) {
536
 
        case COMMAND_SHUTDOWN:
537
 
            pthread_mutex_lock(&exit_mutex);
538
 
            progexit = 1;
539
 
            pthread_mutex_unlock(&exit_mutex);
540
 
            return 1;
541
 
        case COMMAND_RELOAD:
542
 
            pthread_mutex_lock(&reload_mutex);
543
 
            reload = 1;
544
 
            pthread_mutex_unlock(&reload_mutex);
545
 
            mdprintf(desc, "RELOADING%c", term);
546
 
            /* we set reload flag, and we'll reload before closing the
547
 
             * connection */
548
 
            return 1;
549
 
        case COMMAND_PING:
550
 
            if (conn->group)
551
 
                mdprintf(desc, "%u: PONG%c", conn->id, term);
552
 
            else
553
 
                mdprintf(desc, "PONG%c", term);
554
 
            return conn->group ? 0 : 1;
555
 
        case COMMAND_VERSION:
556
 
            {
557
 
                if (conn->group)
558
 
                    mdprintf(desc, "%u: ", conn->id);
559
 
                print_ver(desc, conn->term, engine);
560
 
                return conn->group ? 0 : 1;
561
 
            }
562
 
        case COMMAND_COMMANDS:
563
 
            {
564
 
                if (conn->group)
565
 
                    mdprintf(desc, "%u: ", conn->id);
566
 
                print_commands(desc, conn->term, engine);
567
 
                return conn->group ? 0 : 1;
568
 
            }
569
 
        case COMMAND_DETSTATSCLEAR:
570
 
            {
571
 
                detstats_clear();
572
 
                return 1;
573
 
            }
574
 
        case COMMAND_DETSTATS:
575
 
            {
576
 
                detstats_print(desc, conn->term);
577
 
                return 1;
578
 
            }
579
 
        case COMMAND_INSTREAM:
580
 
            {
581
 
                int rc = cli_gentempfd(optget(conn->opts, "TemporaryDirectory")->strarg, &conn->filename, &conn->scanfd);
582
 
                if (rc != CL_SUCCESS)
583
 
                    return rc;
584
 
                conn->quota = optget(conn->opts, "StreamMaxLength")->numarg;
585
 
                conn->mode = MODE_STREAM;
586
 
                return 0;
587
 
            }
588
 
        case COMMAND_STREAM:
589
 
        case COMMAND_MULTISCAN:
590
 
        case COMMAND_CONTSCAN:
591
 
        case COMMAND_STATS:
592
 
        case COMMAND_FILDES:
593
 
        case COMMAND_SCAN:
594
 
        case COMMAND_INSTREAMSCAN:
595
 
            return dispatch_command(conn, cmd, argument);
596
 
        case COMMAND_IDSESSION:
597
 
            conn->group = thrmgr_group_new();
598
 
            if (!conn->group)
599
 
                return CL_EMEM;
600
 
            return 0;
601
 
        case COMMAND_END:
602
 
            if (!conn->group) {
603
 
                /* end without idsession? */
604
 
                conn_reply_single(conn, NULL, "UNKNOWN COMMAND");
605
 
                return 1;
606
 
            }
607
 
            /* need to close connection  if we were last in group */
608
 
            return 1;
609
 
        /*case COMMAND_UNKNOWN:*/
610
 
        default:
611
 
            conn_reply_single(conn, NULL, "UNKNOWN COMMAND");
612
 
            return 1;
613
 
    }
 
372
        mdprintf(desc, "UNKNOWN COMMAND\n");
 
373
    }
 
374
 
 
375
    return 0; /* no error and no 'special' command executed */
614
376
}