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

« back to all changes in this revision

Viewing changes to clamscan/manager.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
 
2
 *  Copyright (C) 2002 - 2007 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
17
16
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18
17
 *  MA 02110-1301, USA.
19
18
 *
 
19
 *  Wed Mar  5 03:45:31 CET 2003: included --move code from Damien Curtain
20
20
 */
21
21
 
22
22
#if HAVE_CONFIG_H
29
29
#include <ctype.h>
30
30
#include <sys/stat.h>
31
31
#include <sys/types.h>
32
 
#ifdef HAVE_PWD_H
33
 
#include <pwd.h>
34
 
#endif
35
 
#include <dirent.h>
36
 
#ifndef _WIN32
37
32
#include <sys/wait.h>
38
33
#include <utime.h>
39
 
#include <sys/time.h>
40
 
#include <sys/resource.h>
41
 
#endif
 
34
#include <grp.h>
42
35
#include <fcntl.h>
43
 
#ifdef  HAVE_UNISTD_H
44
36
#include <unistd.h>
45
 
#endif
46
37
#include <sys/types.h>
47
38
#include <signal.h>
48
39
#include <errno.h>
49
 
#include <target.h>
50
40
 
51
41
#include "manager.h"
 
42
#include "others.h"
 
43
#include "treewalk.h"
52
44
#include "global.h"
53
45
 
54
 
#include "shared/optparser.h"
55
 
#include "shared/actions.h"
 
46
#include "shared/options.h"
 
47
#include "shared/memory.h"
56
48
#include "shared/output.h"
57
49
#include "shared/misc.h"
58
50
 
60
52
#include "libclamav/others.h"
61
53
#include "libclamav/matcher-ac.h"
62
54
#include "libclamav/str.h"
63
 
#include "libclamav/readdb.h"
64
 
#include "libclamav/cltypes.h"
65
55
 
66
56
#ifdef C_LINUX
67
57
dev_t procdev;
68
58
#endif
69
59
 
70
 
#ifdef _WIN32
71
 
/* FIXME: If possible, handle users correctly */
72
 
static int checkaccess(const char *path, const char *username, int mode)
73
 
{
74
 
    return !_access(path, mode);
75
 
}
76
 
#else
77
 
static int checkaccess(const char *path, const char *username, int mode)
78
 
{
79
 
        struct passwd *user;
80
 
        int ret = 0, status;
81
 
 
82
 
    if(!geteuid()) {
83
 
 
84
 
        if((user = getpwnam(username)) == NULL) {
85
 
            return -1;
86
 
        }
87
 
 
88
 
        switch(fork()) {
89
 
            case -1:
90
 
                return -2;
91
 
 
92
 
            case 0:
93
 
                if(setgid(user->pw_gid)) {
94
 
                    fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int) user->pw_gid);
95
 
                    exit(0);
96
 
                }
97
 
 
98
 
                if(setuid(user->pw_uid)) {
99
 
                    fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int) user->pw_uid);
100
 
                    exit(0);
101
 
                }
102
 
 
103
 
                if(access(path, mode))
104
 
                    exit(0);
105
 
                else
106
 
                    exit(1);
107
 
 
108
 
            default:
109
 
                wait(&status);
110
 
                if(WIFEXITED(status) && WEXITSTATUS(status) == 1)
111
 
                    ret = 1;
112
 
        }
113
 
 
114
 
    } else {
115
 
        if(!access(path, mode))
116
 
            ret = 1;
117
 
    }
118
 
 
119
 
    return ret;
120
 
}
121
 
#endif
122
 
 
123
 
static void scanfile(const char *filename, struct cl_engine *engine, const struct optstruct *opts, unsigned int options)
124
 
{
125
 
        int ret = 0, fd, included, printclean = 1;
126
 
        const struct optstruct *opt;
127
 
        const char *virname;
128
 
        struct stat sb;
129
 
 
130
 
    if((opt = optget(opts, "exclude"))->enabled) {
131
 
        while(opt) {
132
 
            if(match_regex(filename, opt->strarg) == 1) {
133
 
                if(!printinfected)
134
 
                    logg("~%s: Excluded\n", filename);
135
 
                return;
136
 
            }
137
 
            opt = opt->nextarg;
138
 
        }
139
 
    }
140
 
 
141
 
    if((opt = optget(opts, "include"))->enabled) {
142
 
        included = 0;
143
 
        while(opt) {
144
 
            if(match_regex(filename, opt->strarg) == 1) {
145
 
                included = 1;
146
 
                break;
147
 
            }
148
 
            opt = opt->nextarg;
149
 
        }
150
 
        if(!included) {
151
 
            if(!printinfected)
152
 
                logg("~%s: Excluded\n", filename);
153
 
            return;
154
 
        }
155
 
    }
156
 
 
157
 
    /* argh, don't scan /proc files */
158
 
    if(stat(filename, &sb) != -1) {
159
 
#ifdef C_LINUX
160
 
        if(procdev && sb.st_dev == procdev) {
161
 
            if(!printinfected)
162
 
                logg("~%s: Excluded (/proc)\n", filename);
163
 
                return;
164
 
        }
165
 
#endif    
166
 
        if(!sb.st_size) {
167
 
            if(!printinfected)
168
 
                logg("~%s: Empty file\n", filename);
169
 
            return;
170
 
        }
171
 
        info.rblocks += sb.st_size / CL_COUNT_PRECISION;
172
 
    }
173
 
 
174
 
#ifndef _WIN32
175
 
    if(geteuid())
176
 
        if(checkaccess(filename, NULL, R_OK) != 1) {
177
 
            if(!printinfected)
178
 
                logg("~%s: Access denied\n", filename);
179
 
            info.errors++;
180
 
            return;
181
 
        }
182
 
#endif
183
 
 
184
 
    logg("*Scanning %s\n", filename);
185
 
 
186
 
    if((fd = safe_open(filename, O_RDONLY|O_BINARY)) == -1) {
187
 
        logg("^Can't open file %s: %s\n", filename, strerror(errno));
188
 
        info.errors++;
189
 
        return;
190
 
    }
191
 
 
192
 
    if((ret = cl_scandesc(fd, &virname, &info.blocks, engine, options)) == CL_VIRUS) {
193
 
        logg("~%s: %s FOUND\n", filename, virname);
194
 
        info.files++;
195
 
        info.ifiles++;
196
 
 
197
 
        if(bell)
198
 
            fprintf(stderr, "\007");
199
 
 
200
 
    } else if(ret == CL_CLEAN) {
201
 
        if(!printinfected && printclean)
202
 
            mprintf("~%s: OK\n", filename);
203
 
        info.files++;
204
 
    } else {
205
 
        if(!printinfected)
206
 
            logg("~%s: %s ERROR\n", filename, cl_strerror(ret));
207
 
        info.errors++;
208
 
    }
209
 
 
210
 
    close(fd);
211
 
 
212
 
    if(ret == CL_VIRUS && action)
213
 
        action(filename);
214
 
}
215
 
 
216
 
static void scandirs(const char *dirname, struct cl_engine *engine, const struct optstruct *opts, unsigned int options, unsigned int depth, dev_t dev)
217
 
{
218
 
        DIR *dd;
219
 
        struct dirent *dent;
220
 
        struct stat sb;
221
 
        char *fname;
222
 
        int included;
223
 
        const struct optstruct *opt;
224
 
        unsigned int dirlnk, filelnk;
225
 
 
226
 
 
227
 
    if((opt = optget(opts, "exclude-dir"))->enabled) {
228
 
        while(opt) {
229
 
            if(match_regex(dirname, opt->strarg) == 1) {
230
 
                if(!printinfected)
231
 
                    logg("~%s: Excluded\n", dirname);
232
 
                return;
233
 
            }
234
 
            opt = opt->nextarg;
235
 
        }
236
 
    }
237
 
 
238
 
    if((opt = optget(opts, "include-dir"))->enabled) {
239
 
        included = 0;
240
 
        while(opt) {
241
 
            if(match_regex(dirname, opt->strarg) == 1) {
242
 
                included = 1;
243
 
                break;
244
 
            }
245
 
            opt = opt->nextarg;
246
 
        }
247
 
        if(!included) {
248
 
            if(!printinfected)
249
 
                logg("~%s: Excluded\n", dirname);
250
 
            return;
251
 
        }
252
 
    }
253
 
 
254
 
    if(depth > (unsigned int) optget(opts, "max-dir-recursion")->numarg)
255
 
        return;
256
 
 
257
 
    dirlnk = optget(opts, "follow-dir-symlinks")->numarg;
258
 
    filelnk = optget(opts, "follow-file-symlinks")->numarg;
259
 
 
260
 
    if((dd = opendir(dirname)) != NULL) {
261
 
        info.dirs++;
262
 
        depth++;
263
 
        while((dent = readdir(dd))) {
264
 
            if(dent->d_ino)
265
 
            {
266
 
                if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) {
267
 
                    /* build the full name */
268
 
                    fname = malloc(strlen(dirname) + strlen(dent->d_name) + 2);
269
 
                    if(!strcmp(dirname, PATHSEP))
270
 
                        sprintf(fname, PATHSEP"%s", dent->d_name);
271
 
                    else
272
 
                        sprintf(fname, "%s"PATHSEP"%s", dirname, dent->d_name);
273
 
 
274
 
                    /* stat the file */
275
 
                    if(lstat(fname, &sb) != -1) {
276
 
                        if(!optget(opts, "cross-fs")->enabled) {
277
 
                            if(sb.st_dev != dev) {
278
 
                                if(!printinfected)
279
 
                                    logg("~%s: Excluded\n", fname);
280
 
                                free(fname);
281
 
                                continue;
282
 
                            }
283
 
                        }
284
 
                        if(S_ISLNK(sb.st_mode)) {
285
 
                            if(dirlnk != 2 && filelnk != 2) {
286
 
                                if(!printinfected)
287
 
                                    logg("%s: Symbolic link\n", fname);
288
 
                            } else if(stat(fname, &sb) != -1) {
289
 
                                if(S_ISREG(sb.st_mode) && filelnk == 2) {
290
 
                                    scanfile(fname, engine, opts, options);
291
 
                                } else if(S_ISDIR(sb.st_mode) && dirlnk == 2) {
292
 
                                    if(recursion)
293
 
                                        scandirs(fname, engine, opts, options, depth, dev);
294
 
                                } else {
295
 
                                    if(!printinfected)
296
 
                                        logg("%s: Symbolic link\n", fname);
297
 
                                }
298
 
                            }
299
 
                        } else if(S_ISREG(sb.st_mode)) {
300
 
                            scanfile(fname, engine, opts, options);
301
 
                        } else if(S_ISDIR(sb.st_mode) && recursion) {
302
 
                            scandirs(fname, engine, opts, options, depth, dev);
303
 
                        }
304
 
                    }
305
 
                    free(fname);
306
 
                }
307
 
            }
308
 
        }
309
 
        closedir(dd);
310
 
    } else {
311
 
        if(!printinfected)
312
 
            logg("~%s: Can't open directory.\n", dirname);
313
 
        info.errors++;
314
 
    }
315
 
}
316
 
 
317
 
static int scanstdin(const struct cl_engine *engine, const struct optstruct *opts, int options)
 
60
static int scandirs(const char *dirname, struct cl_engine *engine, const struct passwd *user, const struct optstruct *opt, const struct cl_limits *limits, int options)
 
61
{
 
62
    return treewalk(dirname, engine, user, opt, limits, options, 1);
 
63
}
 
64
 
 
65
static int scanstdin(const struct cl_engine *engine, const struct cl_limits *limits, int options)
318
66
{
319
67
        int ret;
320
 
        unsigned int fsize = 0;
321
68
        const char *virname, *tmpdir;
322
69
        char *file, buff[FILEBUFF];
323
 
        size_t bread;
324
70
        FILE *fs;
325
71
 
326
 
    if(optget(opts, "tempdir")->enabled) {
327
 
        tmpdir = optget(opts, "tempdir")->strarg;
328
 
    } else
329
 
        /* check write access */
330
 
        tmpdir = cli_gettmpdir();
 
72
 
 
73
    /* check write access */
 
74
    tmpdir = getenv("TMPDIR");
 
75
 
 
76
    if(tmpdir == NULL)
 
77
#ifdef P_tmpdir
 
78
        tmpdir = P_tmpdir;
 
79
#else
 
80
        tmpdir = "/tmp";
 
81
#endif
331
82
 
332
83
    if(checkaccess(tmpdir, CLAMAVUSER, W_OK) != 1) {
333
84
        logg("!Can't write to temporary directory\n");
334
 
        return 2;
 
85
        return 64;
335
86
    }
336
87
 
337
88
    file = cli_gentemp(tmpdir);
338
89
 
339
90
    if(!(fs = fopen(file, "wb"))) {
340
91
        logg("!Can't open %s for writing\n", file);
341
 
        free(file);
342
 
        return 2;
343
 
    }
344
 
 
345
 
    while((bread = fread(buff, 1, FILEBUFF, stdin))) {
346
 
        fsize += bread;
347
 
        if(fwrite(buff, 1, bread, fs) < bread) {
348
 
            logg("!Can't write to %s\n", file);
349
 
            free(file);
350
 
            fclose(fs);
351
 
            return 2;
352
 
        }
353
 
    }
 
92
        return 63;
 
93
    }
 
94
 
 
95
    while((ret = fread(buff, 1, FILEBUFF, stdin)))
 
96
        fwrite(buff, 1, ret, fs);
 
97
 
354
98
    fclose(fs);
355
99
 
356
100
    logg("*Checking %s\n", file);
357
101
    info.files++;
358
 
    info.rblocks += fsize / CL_COUNT_PRECISION;
359
102
 
360
 
    if((ret = cl_scanfile(file, &virname, &info.blocks, engine, options)) == CL_VIRUS) {
 
103
    if((ret = cl_scanfile(file, &virname, &info.blocks, engine, limits, options)) == CL_VIRUS) {
361
104
        logg("stdin: %s FOUND\n", virname);
362
105
        info.ifiles++;
363
106
 
367
110
    } else if(ret == CL_CLEAN) {
368
111
        if(!printinfected)
369
112
            mprintf("stdin: OK\n");
370
 
    } else {
 
113
    } else
371
114
        if(!printinfected)
372
 
            logg("stdin: %s ERROR\n", cl_strerror(ret));
373
 
        info.errors++;
374
 
    }
 
115
            logg("stdin: %s\n", cl_strerror(ret));
375
116
 
376
117
    unlink(file);
377
118
    free(file);
378
119
    return ret;
379
120
}
380
121
 
381
 
int scanmanager(const struct optstruct *opts)
 
122
int scanmanager(const struct optstruct *opt)
382
123
{
383
 
        int ret = 0, i;
384
 
        unsigned int options = 0, dboptions = 0, dirlnk = 1, filelnk = 1;
385
 
        struct cl_engine *engine;
 
124
        mode_t fmode;
 
125
        int ret = 0, compression = 0, fmodeint, i, x;
 
126
        unsigned int options = 0, dboptions = 0;
 
127
        struct cl_engine *engine = NULL;
 
128
        struct cl_limits limits;
 
129
        struct passwd *user = NULL;
386
130
        struct stat sb;
387
 
        char *file, cwd[1024], *pua_cats = NULL;
388
 
        const char *filename;
389
 
        const struct optstruct *opt;
390
 
#ifndef _WIN32
391
 
        struct rlimit rlim;
 
131
        char *fullpath = NULL, cwd[1024];
 
132
 
 
133
 
 
134
/* njh@bandsman.co.uk: BeOS */
 
135
#if !defined(C_CYGWIN) && !defined(C_OS2) && !defined(C_BEOS)
 
136
    if(!geteuid()) {
 
137
        if((user = getpwnam(CLAMAVUSER)) == NULL) {
 
138
            logg("!Can't get information about user "CLAMAVUSER"\n");
 
139
            exit(60); /* this is critical problem, so we just exit here */
 
140
        }
 
141
    }
392
142
#endif
393
143
 
394
 
    dirlnk = optget(opts, "follow-dir-symlinks")->numarg;
395
 
    if(dirlnk > 2) {
396
 
        logg("!--follow-dir-symlinks: Invalid argument\n");
397
 
        return 2;
398
 
    }
399
 
 
400
 
    filelnk = optget(opts, "follow-file-symlinks")->numarg;
401
 
    if(filelnk > 2) {
402
 
        logg("!--follow-file-symlinks: Invalid argument\n");
403
 
        return 2;
404
 
    }
405
 
 
406
 
    if(optget(opts, "phishing-sigs")->enabled)
 
144
    if(opt_check(opt, "unzip") || opt_check(opt, "unrar") || opt_check(opt, "arj") ||
 
145
       opt_check(opt, "unzoo") || opt_check(opt, "jar") || opt_check(opt, "lha") ||
 
146
       opt_check(opt, "tar") || opt_check(opt, "tgz") || opt_check(opt, "deb"))
 
147
            compression = 1;
 
148
 
 
149
 
 
150
    if(opt_check(opt, "ncore"))
 
151
        dboptions |= CL_DB_NCORE;
 
152
 
 
153
    if(!opt_check(opt, "no-phishing-sigs"))
407
154
        dboptions |= CL_DB_PHISHING;
408
155
 
409
 
    if(optget(opts, "official-db-only")->enabled)
410
 
        dboptions |= CL_DB_OFFICIAL_ONLY;
411
 
 
412
 
    if(optget(opts,"phishing-scan-urls")->enabled)
 
156
#ifdef CL_EXPERIMENTAL
 
157
    if(!opt_check(opt,"no-phishing-scan-urls"))
413
158
        dboptions |= CL_DB_PHISHING_URLS;
414
 
 
415
 
    if(optget(opts,"bytecode")->enabled)
416
 
        dboptions |= CL_DB_BYTECODE;
417
 
 
418
 
    if((ret = cl_init(CL_INIT_DEFAULT))) {
419
 
        logg("!Can't initialize libclamav: %s\n", cl_strerror(ret));
420
 
        return 2;
421
 
    }
422
 
 
423
 
    if(!(engine = cl_engine_new())) {
424
 
        logg("!Can't initialize antivirus engine\n");
425
 
        return 2;
426
 
    }
427
 
 
428
 
    if(optget(opts, "detect-pua")->enabled) {
429
 
        dboptions |= CL_DB_PUA;
430
 
        if((opt = optget(opts, "exclude-pua"))->enabled) {
431
 
            dboptions |= CL_DB_PUA_EXCLUDE;
432
 
            i = 0;
433
 
            while(opt) {
434
 
                if(!(pua_cats = realloc(pua_cats, i + strlen(opt->strarg) + 3))) {
435
 
                    logg("!Can't allocate memory for pua_cats\n");
436
 
                    cl_engine_free(engine);
437
 
                    return 2;
438
 
                }
439
 
                sprintf(pua_cats + i, ".%s", opt->strarg);
440
 
                i += strlen(opt->strarg) + 1;
441
 
                pua_cats[i] = 0;
442
 
                opt = opt->nextarg;
443
 
            }
444
 
            pua_cats[i] = '.';
445
 
            pua_cats[i + 1] = 0;
446
 
        }
447
 
 
448
 
        if((opt = optget(opts, "include-pua"))->enabled) {
449
 
            if(pua_cats) {
450
 
                logg("!--exclude-pua and --include-pua cannot be used at the same time\n");
451
 
                cl_engine_free(engine);
452
 
                free(pua_cats);
453
 
                return 2;
454
 
            }
455
 
            dboptions |= CL_DB_PUA_INCLUDE;
456
 
            i = 0;
457
 
            while(opt) {
458
 
                if(!(pua_cats = realloc(pua_cats, i + strlen(opt->strarg) + 3))) {
459
 
                    logg("!Can't allocate memory for pua_cats\n");
460
 
                    return 2;
461
 
                }
462
 
                sprintf(pua_cats + i, ".%s", opt->strarg);
463
 
                i += strlen(opt->strarg) + 1;
464
 
                pua_cats[i] = 0;
465
 
                opt = opt->nextarg;
466
 
            }
467
 
            pua_cats[i] = '.';
468
 
            pua_cats[i + 1] = 0;
469
 
        }
470
 
 
471
 
        if(pua_cats) {
472
 
            if((ret = cl_engine_set_str(engine, CL_ENGINE_PUA_CATEGORIES, pua_cats))) {
473
 
                logg("!cli_engine_set_str(CL_ENGINE_PUA_CATEGORIES) failed: %s\n", cl_strerror(ret));
474
 
                free(pua_cats);
475
 
                cl_engine_free(engine);
476
 
                return 2;
477
 
            }
478
 
            free(pua_cats);
479
 
        }
480
 
    }
481
 
 
482
 
    if(optget(opts, "dev-ac-only")->enabled)
483
 
        cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1);
484
 
 
485
 
    if(optget(opts, "dev-ac-depth")->enabled)
486
 
        cl_engine_set_num(engine, CL_ENGINE_AC_MAXDEPTH, optget(opts, "dev-ac-depth")->numarg);
487
 
 
488
 
    if(optget(opts, "leave-temps")->enabled)
489
 
        cl_engine_set_num(engine, CL_ENGINE_KEEPTMP, 1);
490
 
 
491
 
    if(optget(opts, "bytecode-trust-all")->enabled)
492
 
        cl_engine_set_num(engine, CL_ENGINE_BYTECODE_SECURITY, CL_BYTECODE_TRUST_ALL);
493
 
    if((opt = optget(opts,"bytecode-timeout"))->enabled)
494
 
        cl_engine_set_num(engine, CL_ENGINE_BYTECODE_TIMEOUT, opt->numarg);
495
 
    if((opt = optget(opts,"bytecode-mode"))->enabled) {
496
 
        enum bytecode_mode mode;
497
 
        if (!strcmp(opt->strarg, "ForceJIT"))
498
 
            mode = CL_BYTECODE_MODE_JIT;
499
 
        else if(!strcmp(opt->strarg, "ForceInterpreter"))
500
 
            mode = CL_BYTECODE_MODE_INTERPRETER;
501
 
        else if(!strcmp(opt->strarg, "Test"))
502
 
            mode = CL_BYTECODE_MODE_TEST;
503
 
        else
504
 
            mode = CL_BYTECODE_MODE_AUTO;
505
 
        cl_engine_set_num(engine, CL_ENGINE_BYTECODE_MODE, mode);
506
 
    }
507
 
 
508
 
    if((opt = optget(opts, "tempdir"))->enabled) {
509
 
        if((ret = cl_engine_set_str(engine, CL_ENGINE_TMPDIR, opt->strarg))) {
510
 
            logg("!cli_engine_set_str(CL_ENGINE_TMPDIR) failed: %s\n", cl_strerror(ret));
511
 
            cl_engine_free(engine);
512
 
            return 2;
513
 
        }
514
 
    }
515
 
 
516
 
    if((opt = optget(opts, "database"))->active) {
517
 
        while(opt) {
518
 
            if((ret = cl_load(opt->strarg, engine, &info.sigs, dboptions))) {
519
 
                logg("!%s\n", cl_strerror(ret));
520
 
                cl_engine_free(engine);
521
 
                return 2;
522
 
            }
523
 
            opt = opt->nextarg;
524
 
        }
 
159
    if(!opt_check(opt,"no-phishing-restrictedscan")) {
 
160
        /* not scanning all domains, check only URLs with domains from .pdb */
 
161
        options |= CL_SCAN_PHISHING_DOMAINLIST;
 
162
    }
 
163
    if(opt_check(opt,"phishing-ssl")) {
 
164
        options |= CL_SCAN_PHISHING_BLOCKSSL;
 
165
    }
 
166
    if(opt_check(opt,"phishing-cloak")) {
 
167
        options |= CL_SCAN_PHISHING_BLOCKCLOAK;
 
168
    }
 
169
#endif
 
170
 
 
171
    if(opt_check(opt, "dev-ac-only")) {
 
172
        dboptions |= CL_DB_ACONLY;
 
173
 
 
174
        if(opt_check(opt, "dev-ac-depth"))
 
175
            cli_ac_setdepth(atoi(opt_arg(opt, "dev-ac-depth")));
 
176
    }
 
177
 
 
178
    if(opt_check(opt, "database")) {
 
179
        if((ret = cl_load(opt_arg(opt, "database"), &engine, &info.sigs, dboptions))) {
 
180
            logg("!%s\n", cl_strerror(ret));
 
181
            return 50;
 
182
        }
 
183
 
525
184
    } else {
526
185
            char *dbdir = freshdbdir();
527
186
 
528
 
        if((ret = cl_load(dbdir, engine, &info.sigs, dboptions))) {
 
187
        if((ret = cl_load(dbdir, &engine, &info.sigs, dboptions))) {
529
188
            logg("!%s\n", cl_strerror(ret));
530
189
            free(dbdir);
531
 
            cl_engine_free(engine);
532
 
            return 2;
 
190
            return 50;
533
191
        }
534
192
        free(dbdir);
535
193
    }
536
194
 
537
 
    if((ret = cl_engine_compile(engine)) != 0) {
 
195
    if(!engine) {
 
196
        logg("!Can't initialize the virus database\n");
 
197
        return 50;
 
198
    }
 
199
 
 
200
    if((ret = cl_build(engine)) != 0) {
538
201
        logg("!Database initialization error: %s\n", cl_strerror(ret));;
539
 
        cl_engine_free(engine);
540
 
        return 2;
 
202
        return 50;
541
203
    }
542
204
 
543
205
    /* set limits */
544
 
 
545
 
    if((opt = optget(opts, "max-scansize"))->active) {
546
 
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANSIZE, opt->numarg))) {
547
 
            logg("!cli_engine_set_num(CL_ENGINE_MAX_SCANSIZE) failed: %s\n", cl_strerror(ret));
548
 
            cl_engine_free(engine);
549
 
            return 2;
550
 
        }
551
 
    }
552
 
 
553
 
    if((opt = optget(opts, "max-filesize"))->active) {
554
 
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_FILESIZE, opt->numarg))) {
555
 
            logg("!cli_engine_set_num(CL_ENGINE_MAX_FILESIZE) failed: %s\n", cl_strerror(ret));
556
 
            cl_engine_free(engine);
557
 
            return 2;
558
 
        }
559
 
    }
560
 
 
561
 
#ifndef _WIN32
562
 
    if(getrlimit(RLIMIT_FSIZE, &rlim) == 0) {
563
 
        if(rlim.rlim_cur < (rlim_t) cl_engine_get_num(engine, CL_ENGINE_MAX_FILESIZE, NULL))
564
 
            logg("^System limit for file size is lower than engine->maxfilesize\n");
565
 
        if(rlim.rlim_cur < (rlim_t) cl_engine_get_num(engine, CL_ENGINE_MAX_SCANSIZE, NULL))
566
 
            logg("^System limit for file size is lower than engine->maxscansize\n");
567
 
    } else {
568
 
        logg("^Cannot obtain resource limits for file size\n");
569
 
    }
570
 
#endif
571
 
 
572
 
    if((opt = optget(opts, "max-files"))->active) {
573
 
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_FILES, opt->numarg))) {
574
 
            logg("!cli_engine_set_num(CL_ENGINE_MAX_FILES) failed: %s\n", cl_strerror(ret));
575
 
            cl_engine_free(engine);
576
 
            return 2;
577
 
        }
578
 
    }
579
 
 
580
 
    if((opt = optget(opts, "max-recursion"))->active) {
581
 
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_RECURSION, opt->numarg))) {
582
 
            logg("!cli_engine_set_num(CL_ENGINE_MAX_RECURSION) failed: %s\n", cl_strerror(ret));
583
 
            cl_engine_free(engine);
584
 
            return 2;
585
 
        }
586
 
    }
587
 
 
588
 
    /* set scan options */
589
 
    if(optget(opts,"phishing-ssl")->enabled)
590
 
        options |= CL_SCAN_PHISHING_BLOCKSSL;
591
 
 
592
 
    if(optget(opts,"phishing-cloak")->enabled)
593
 
        options |= CL_SCAN_PHISHING_BLOCKCLOAK;
594
 
 
595
 
    if(optget(opts,"heuristic-scan-precedence")->enabled)
596
 
        options |= CL_SCAN_HEURISTIC_PRECEDENCE;
597
 
 
598
 
    if(optget(opts, "scan-archive")->enabled)
 
206
    memset(&limits, 0, sizeof(struct cl_limits));
 
207
 
 
208
    if(opt_check(opt, "max-space")) {
 
209
        char *cpy, *ptr;
 
210
        ptr = opt_arg(opt, "max-space");
 
211
        if(tolower(ptr[strlen(ptr) - 1]) == 'm') {
 
212
            cpy = mcalloc(strlen(ptr), sizeof(char));
 
213
            strncpy(cpy, ptr, strlen(ptr) - 1);
 
214
            limits.maxfilesize = atoi(cpy) * 1024 * 1024;
 
215
            free(cpy);
 
216
        } else
 
217
            limits.maxfilesize = atoi(ptr) * 1024;
 
218
    } else
 
219
        limits.maxfilesize = 10485760;
 
220
 
 
221
    if(opt_check(opt, "max-files"))
 
222
        limits.maxfiles = atoi(opt_arg(opt, "max-files"));
 
223
    else
 
224
        limits.maxfiles = 500;
 
225
 
 
226
    if(opt_check(opt, "max-recursion"))
 
227
        limits.maxreclevel = atoi(opt_arg(opt, "max-recursion"));
 
228
    else
 
229
        limits.maxreclevel = 8;
 
230
 
 
231
    if(opt_check(opt, "max-mail-recursion"))
 
232
        limits.maxmailrec = atoi(opt_arg(opt, "max-mail-recursion"));
 
233
    else
 
234
        limits.maxmailrec = 64;
 
235
 
 
236
    if(opt_check(opt, "max-ratio"))
 
237
        limits.maxratio = atoi(opt_arg(opt, "max-ratio"));
 
238
    else
 
239
        limits.maxratio = 250;
 
240
 
 
241
    /* set options */
 
242
 
 
243
    if(opt_check(opt, "disable-archive") || opt_check(opt, "no-archive"))
 
244
        options &= ~CL_SCAN_ARCHIVE;
 
245
    else
599
246
        options |= CL_SCAN_ARCHIVE;
600
247
 
601
 
    if(optget(opts, "detect-broken")->enabled)
 
248
    if(opt_check(opt, "detect-broken"))
602
249
        options |= CL_SCAN_BLOCKBROKEN;
603
250
 
604
 
    if(optget(opts, "block-encrypted")->enabled)
 
251
    if(opt_check(opt, "block-encrypted"))
605
252
        options |= CL_SCAN_BLOCKENCRYPTED;
606
253
 
607
 
    if(optget(opts, "scan-pe")->enabled)
 
254
    if(opt_check(opt, "block-max"))
 
255
        options |= CL_SCAN_BLOCKMAX;
 
256
 
 
257
    if(opt_check(opt, "no-pe"))
 
258
        options &= ~CL_SCAN_PE;
 
259
    else
608
260
        options |= CL_SCAN_PE;
609
261
 
610
 
    if(optget(opts, "scan-elf")->enabled)
 
262
    if(opt_check(opt, "no-elf"))
 
263
        options &= ~CL_SCAN_ELF;
 
264
    else
611
265
        options |= CL_SCAN_ELF;
612
266
 
613
 
    if(optget(opts, "scan-ole2")->enabled)
 
267
    if(opt_check(opt, "no-ole2"))
 
268
        options &= ~CL_SCAN_OLE2;
 
269
    else
614
270
        options |= CL_SCAN_OLE2;
615
271
 
616
 
    if(optget(opts, "scan-pdf")->enabled)
617
 
        options |= CL_SCAN_PDF;
618
 
 
619
 
    if(optget(opts, "scan-html")->enabled)
 
272
    if(opt_check(opt, "no-html"))
 
273
        options &= ~CL_SCAN_HTML;
 
274
    else
620
275
        options |= CL_SCAN_HTML;
621
276
 
622
 
    if(optget(opts, "scan-mail")->enabled)
 
277
    if(opt_check(opt, "no-mail")) {
 
278
        options &= ~CL_SCAN_MAIL;
 
279
    } else {
623
280
        options |= CL_SCAN_MAIL;
624
281
 
625
 
    if(optget(opts, "algorithmic-detection")->enabled)
 
282
        if(opt_check(opt, "mail-follow-urls"))
 
283
            options |= CL_SCAN_MAILURL;
 
284
    }
 
285
 
 
286
    if(opt_check(opt, "no-algorithmic"))
 
287
        options &= ~CL_SCAN_ALGORITHMIC;
 
288
    else
626
289
        options |= CL_SCAN_ALGORITHMIC;
627
290
 
628
 
#ifdef HAVE__INTERNAL__SHA_COLLECT
629
 
    if(optget(opts, "dev-collect-hashes")->enabled)
630
 
        options |= CL_SCAN_INTERNAL_COLLECT_SHA;
631
 
#endif
632
 
 
633
 
    if(optget(opts, "detect-structured")->enabled) {
634
 
        options |= CL_SCAN_STRUCTURED;
635
 
 
636
 
        if((opt = optget(opts, "structured-ssn-format"))->enabled) {
637
 
            switch(opt->numarg) {
638
 
                case 0:
639
 
                    options |= CL_SCAN_STRUCTURED_SSN_NORMAL;
640
 
                    break;
641
 
                case 1:
642
 
                    options |= CL_SCAN_STRUCTURED_SSN_STRIPPED;
643
 
                    break;
644
 
                case 2:
645
 
                    options |= (CL_SCAN_STRUCTURED_SSN_NORMAL | CL_SCAN_STRUCTURED_SSN_STRIPPED);
646
 
                    break;
647
 
                default:
648
 
                    logg("!Invalid argument for --structured-ssn-format\n");
649
 
                    return 2;
650
 
            }
651
 
        } else {
652
 
            options |= CL_SCAN_STRUCTURED_SSN_NORMAL;
653
 
        }
654
 
 
655
 
        if((opt = optget(opts, "structured-ssn-count"))->active) {
656
 
            if((ret = cl_engine_set_num(engine, CL_ENGINE_MIN_SSN_COUNT, opt->numarg))) {
657
 
                logg("!cli_engine_set_num(CL_ENGINE_MIN_SSN_COUNT) failed: %s\n", cl_strerror(ret));
658
 
                cl_engine_free(engine);
659
 
                return 2;
660
 
            }
661
 
        }
662
 
 
663
 
        if((opt = optget(opts, "structured-cc-count"))->active) {
664
 
            if((ret = cl_engine_set_num(engine, CL_ENGINE_MIN_CC_COUNT, opt->numarg))) {
665
 
                logg("!cli_engine_set_num(CL_ENGINE_MIN_CC_COUNT) failed: %s\n", cl_strerror(ret));
666
 
                cl_engine_free(engine);
667
 
                return 2;
668
 
            }
669
 
        }
670
 
 
671
 
    } else {
672
 
        options &= ~CL_SCAN_STRUCTURED;
673
 
    }
674
 
 
675
291
#ifdef C_LINUX
676
292
    procdev = (dev_t) 0;
677
293
    if(stat("/proc", &sb) != -1 && !sb.st_size)
679
295
#endif
680
296
 
681
297
    /* check filetype */
682
 
    if(!opts->filename && !optget(opts, "file-list")->enabled) {
 
298
    if(opt->filename == NULL || strlen(opt->filename) == 0) {
 
299
 
683
300
        /* we need full path for some reasons (eg. archive handling) */
684
301
        if(!getcwd(cwd, sizeof(cwd))) {
685
302
            logg("!Can't get absolute pathname of current working directory\n");
686
 
            ret = 2;
687
 
        } else {
688
 
            stat(cwd, &sb);
689
 
            scandirs(cwd, engine, opts, options, 1, sb.st_dev);
690
 
        }
 
303
            ret = 57;
 
304
        } else
 
305
            ret = scandirs(cwd, engine, user, opt, &limits, options);
691
306
 
692
 
    } else if(opts->filename && !optget(opts, "file-list")->enabled && !strcmp(opts->filename[0], "-")) { /* read data from stdin */
693
 
        ret = scanstdin(engine, opts, options);
 
307
    } else if(!strcmp(opt->filename, "-")) { /* read data from stdin */
 
308
        ret = scanstdin(engine, &limits, options);
694
309
 
695
310
    } else {
696
 
        if(opts->filename && optget(opts, "file-list")->enabled)
697
 
            logg("^Only scanning files from --file-list (files passed at cmdline are ignored)\n");
698
 
 
699
 
        while((filename = filelist(opts, &ret)) && (file = strdup(filename))) {
700
 
            if(lstat(file, &sb) == -1) {
701
 
                logg("^%s: Can't access file\n", file);
702
 
                perror(file);
703
 
                ret = 2;
 
311
        char *thefilename;
 
312
        for (x = 0; (thefilename = cli_strtok(opt->filename, x, "\t")) != NULL; x++) {
 
313
            if((fmodeint = fileinfo(thefilename, 2)) == -1) {
 
314
                logg("^Can't access file %s\n", thefilename);
 
315
                perror(thefilename);
 
316
                ret = 56;
704
317
            } else {
705
 
                for(i = strlen(file) - 1; i > 0; i--) {
706
 
                    if(file[i] == *PATHSEP)
707
 
                        file[i] = 0;
 
318
                int slash = 1;
 
319
                for(i = strlen(thefilename) - 1; i > 0 && slash; i--) {
 
320
                    if(thefilename[i] == '/')
 
321
                        thefilename[i] = 0;
708
322
                    else
709
 
                        break;
 
323
                        slash = 0;
710
324
                }
711
325
 
712
 
                if(S_ISLNK(sb.st_mode)) {
713
 
                    if(dirlnk == 0 && filelnk == 0) {
714
 
                        if(!printinfected)
715
 
                            logg("%s: Symbolic link\n", file);
716
 
                    } else if(stat(file, &sb) != -1) {
717
 
                        if(S_ISREG(sb.st_mode) && filelnk) {
718
 
                            scanfile(file, engine, opts, options);
719
 
                        } else if(S_ISDIR(sb.st_mode) && dirlnk) {
720
 
                            scandirs(file, engine, opts, options, 1, sb.st_dev);
721
 
                        } else {
722
 
                            if(!printinfected)
723
 
                                logg("%s: Symbolic link\n", file);
724
 
                        }
 
326
                fmode = (mode_t) fmodeint;
 
327
 
 
328
                if(compression && (thefilename[0] != '/' && thefilename[0] != '\\' && thefilename[1] != ':')) {
 
329
                    /* we need to complete the path */
 
330
                    if(!getcwd(cwd, sizeof(cwd))) {
 
331
                        logg("!Can't get absolute pathname of current working directory\n");
 
332
                        return 57;
 
333
                    } else {
 
334
                        fullpath = mcalloc(512, sizeof(char));
 
335
#ifdef NO_SNPRINTF
 
336
                        sprintf(fullpath, "%s/%s", cwd, thefilename);
 
337
#else
 
338
                        snprintf(fullpath, 512, "%s/%s", cwd, thefilename);
 
339
#endif
 
340
                        logg("*Full path: %s\n", fullpath);
725
341
                    }
726
 
                } else if(S_ISREG(sb.st_mode)) {
727
 
                    scanfile(file, engine, opts, options);
728
 
                } else if(S_ISDIR(sb.st_mode)) {
729
 
                    scandirs(file, engine, opts, options, 1, sb.st_dev);
730
 
                } else {
731
 
                    logg("^%s: Not supported file type\n", file);
732
 
                    ret = 2;
 
342
                } else
 
343
                    fullpath = thefilename;
 
344
 
 
345
                switch(fmode & S_IFMT) {
 
346
                    case S_IFREG:
 
347
                        ret = scanfile(fullpath, engine, user, opt, &limits, options);
 
348
                        break;
 
349
 
 
350
                    case S_IFDIR:
 
351
                        ret = scandirs(fullpath, engine, user, opt, &limits, options);
 
352
                        break;
 
353
 
 
354
                    default:
 
355
                        logg("!Not supported file type (%s)\n", thefilename);
 
356
                        ret = 52;
 
357
                }
 
358
 
 
359
                if(compression && (thefilename[0] != '/' && thefilename[0] != '\\' && thefilename[1] != ':')) {
 
360
                    free(fullpath);
 
361
                    fullpath = NULL;
733
362
                }
734
363
            }
735
 
            free(file);
 
364
            free(thefilename);
736
365
        }
737
366
    }
738
367
 
739
368
    /* free the engine */
740
 
    cl_engine_free(engine);
 
369
    cl_free(engine);
741
370
 
742
 
    /* overwrite return code - infection takes priority */
 
371
    /* overwrite return code */
743
372
    if(info.ifiles)
744
373
        ret = 1;
745
 
    else if(info.errors)
746
 
        ret = 2;
747
 
 
 
374
    else if(ret < 50) /* hopefully no error detected */ 
 
375
        ret = 0; /* just make sure it's 0 */
 
376
 
 
377
    return ret;
 
378
}
 
379
 
 
380
/*
 
381
 * -1 -> can't fork
 
382
 * -2 -> can't execute
 
383
 * -3 -> external signal
 
384
 * 0 -> OK
 
385
 */
 
386
static int clamav_unpack(const char *prog, char **args, const char *tmpdir, const struct passwd *user, const struct optstruct *opt)
 
387
{
 
388
        pid_t pid;
 
389
        int status, wret, fdevnull;
 
390
        unsigned int maxfiles, maxspace;
 
391
        struct s_du n;
 
392
 
 
393
 
 
394
    if(opt_check(opt, "max-files"))
 
395
        maxfiles = atoi(opt_arg(opt, "max-files"));
 
396
    else
 
397
        maxfiles = 0;
 
398
 
 
399
    if(opt_check(opt, "max-space")) {
 
400
            char *cpy, *ptr;
 
401
        ptr = opt_arg(opt, "max-space");
 
402
        if(tolower(ptr[strlen(ptr) - 1]) == 'm') { /* megabytes */
 
403
            cpy = mcalloc(strlen(ptr), sizeof(char));
 
404
            strncpy(cpy, ptr, strlen(ptr) - 1);
 
405
            maxspace = atoi(cpy) * 1024;
 
406
            free(cpy);
 
407
        } else /* default - kilobytes */
 
408
            maxspace = atoi(ptr);
 
409
    } else
 
410
        maxspace = 0;
 
411
 
 
412
 
 
413
    switch(pid = fork()) {
 
414
        case -1:
 
415
            return -1;
 
416
        case 0:
 
417
#ifndef C_CYGWIN
 
418
            if(!geteuid() && user) {
 
419
 
 
420
#ifdef HAVE_SETGROUPS
 
421
                if(setgroups(1, &user->pw_gid)) {
 
422
                    fprintf(stderr, "ERROR: setgroups() failed\n");
 
423
                    exit(1);
 
424
                }
 
425
#endif
 
426
 
 
427
                if(setgid(user->pw_gid)) {
 
428
                    fprintf(stderr, "ERROR: setgid(%d) failed\n", (int) user->pw_gid);
 
429
                    exit(1);
 
430
                }
 
431
 
 
432
                if(setuid(user->pw_uid)) {
 
433
                    fprintf(stderr, "ERROR: setuid(%d) failed\n", (int) user->pw_uid);
 
434
                    exit(1);
 
435
                }
 
436
            }
 
437
#endif
 
438
            chdir(tmpdir);
 
439
 
 
440
            if(printinfected) {
 
441
                fdevnull = open("/dev/null", O_WRONLY);
 
442
                if(fdevnull == -1) {
 
443
                    logg("Non fatal error: cannot open /dev/null. Continuing with full output\n");
 
444
                    printinfected = 0;
 
445
                } else {
 
446
                    dup2(fdevnull,1);
 
447
                    dup2(fdevnull,2);
 
448
                }
 
449
            }
 
450
 
 
451
            if(strchr(prog, '/')) /* we have full path */
 
452
                execv(prog, args);
 
453
            else
 
454
                execvp(prog, args);
 
455
            perror("execv(p)");
 
456
            abort();
 
457
            break;
 
458
        default:
 
459
 
 
460
            if(maxfiles || maxspace) {
 
461
                while(!(wret = waitpid(pid, &status, WNOHANG))) {
 
462
                    memset(&n, 0, sizeof(struct s_du));
 
463
 
 
464
                    if(!du(tmpdir, &n))
 
465
                        if((maxfiles && n.files > maxfiles) || (maxspace && n.space > maxspace)) {
 
466
                            logg("*n.files: %d, n.space: %d\n", n.files, n.space);
 
467
                            kill(pid, 9); /* stop it immediately */
 
468
                        }
 
469
                }
 
470
            } else
 
471
                waitpid(pid, &status, 0);
 
472
 
 
473
 
 
474
            if(WIFSIGNALED(status)) {
 
475
                switch(WTERMSIG(status)) {
 
476
 
 
477
                    case 9:
 
478
                        logg("\nUnpacker process %d stopped due to exceeded limits\n", pid);
 
479
                        return 0;
 
480
                    case 6: /* abort */
 
481
                        logg("^Can't run %s\n", prog);
 
482
                        return -2;
 
483
                    default:
 
484
                        logg("^\nUnpacker stopped with external signal %d\n", WTERMSIG(status));
 
485
                        return -3;
 
486
                }
 
487
            } else if(WIFEXITED(status))
 
488
                return 0;
 
489
    }
 
490
 
 
491
    return 0;
 
492
}
 
493
 
 
494
static void move_infected(const char *filename, const struct optstruct *opt)
 
495
{
 
496
        char *movedir, *movefilename, *tmp, numext[4 + 1];
 
497
        struct stat fstat, mfstat;
 
498
        int n, len, movefilename_size;
 
499
        int moveflag = opt_check(opt, "move");
 
500
        struct utimbuf ubuf;
 
501
 
 
502
 
 
503
    if((moveflag && !(movedir = opt_arg(opt, "move"))) ||
 
504
        (!moveflag && !(movedir = opt_arg(opt, "copy")))) {
 
505
        /* Should never reach here */
 
506
        logg("!opt_arg() returned NULL\n", filename);
 
507
        info.notmoved++;
 
508
        return;
 
509
    }
 
510
 
 
511
    if(access(movedir, W_OK|X_OK) == -1) {
 
512
        logg("!Can't %s file '%s': cannot write to '%s': %s\n", (moveflag) ? "move" : "copy", filename, movedir, strerror(errno));
 
513
        info.notmoved++;
 
514
        return;
 
515
    }
 
516
 
 
517
    if(!(tmp = strrchr(filename, '/')))
 
518
        tmp = (const char *) filename;
 
519
 
 
520
    movefilename_size = sizeof(char) * (strlen(movedir) + strlen(tmp) + sizeof(numext) + 2);
 
521
 
 
522
    if(!(movefilename = mmalloc(movefilename_size))) {
 
523
        logg("!mmalloc() failed\n");
 
524
        exit(71);
 
525
    }
 
526
 
 
527
    if(!(cli_strrcpy(movefilename, movedir))) {
 
528
        logg("!cli_strrcpy() returned NULL\n");
 
529
        info.notmoved++;
 
530
        free(movefilename);
 
531
        return;
 
532
    }
 
533
 
 
534
    strcat(movefilename, "/");
 
535
 
 
536
    if(!(strcat(movefilename, tmp))) {
 
537
        logg("!strcat() returned NULL\n");
 
538
        info.notmoved++;
 
539
        free(movefilename);
 
540
        return;
 
541
    }
 
542
 
 
543
    stat(filename, &fstat);
 
544
 
 
545
    if(!stat(movefilename, &mfstat)) {
 
546
        if(fstat.st_ino == mfstat.st_ino) { /* It's the same file*/
 
547
            logg("File excluded '%s'\n", filename);
 
548
            info.notmoved++;
 
549
            free(movefilename);
 
550
            return;
 
551
        } else {
 
552
            /* file exists - try to append an ordinal number to the
 
553
             * quranatined file in an attempt not to overwrite existing
 
554
             * files in quarantine  
 
555
             */
 
556
            len = strlen(movefilename);
 
557
            n = 0;                                              
 
558
            do {
 
559
                /* reset the movefilename to it's initial value by
 
560
                 * truncating to the original filename length
 
561
                 */
 
562
                movefilename[len] = 0;
 
563
                /* append .XXX */
 
564
                sprintf(numext, ".%03d", n++);
 
565
                strcat(movefilename, numext);                   
 
566
            } while(!stat(movefilename, &mfstat) && (n < 1000));
 
567
       }
 
568
    }
 
569
 
 
570
    if(!moveflag || rename(filename, movefilename) == -1) {
 
571
        if(filecopy(filename, movefilename) == -1) {
 
572
            logg("!Can't %s '%s' to '%s': %s\n", (moveflag) ? "move" : "copy", filename, movefilename, strerror(errno));
 
573
            info.notmoved++;
 
574
            free(movefilename);
 
575
            return;
 
576
        }
 
577
 
 
578
        chmod(movefilename, fstat.st_mode);
 
579
#ifndef C_OS2
 
580
        chown(movefilename, fstat.st_uid, fstat.st_gid);
 
581
#endif
 
582
 
 
583
        ubuf.actime = fstat.st_atime;
 
584
        ubuf.modtime = fstat.st_mtime;
 
585
        utime(movefilename, &ubuf);
 
586
 
 
587
        if(moveflag && unlink(filename)) {
 
588
            logg("!Can't unlink '%s': %s\n", filename, strerror(errno));
 
589
            info.notremoved++;            
 
590
            free(movefilename);
 
591
            return;
 
592
        }
 
593
    }
 
594
 
 
595
    logg("%s: %s to '%s'\n", filename, (moveflag) ? "moved" : "copied", movefilename);
 
596
 
 
597
    free(movefilename);
 
598
}
 
599
 
 
600
static int checkfile(const char *filename, const struct cl_engine *engine, const struct cl_limits *limits, int options, short printclean)
 
601
{
 
602
        int fd, ret;
 
603
        const char *virname;
 
604
 
 
605
 
 
606
    logg("*Scanning %s\n", filename);
 
607
 
 
608
    if((fd = open(filename, O_RDONLY)) == -1) {
 
609
        logg("^Can't open file %s\n", filename);
 
610
        return 54;
 
611
    }
 
612
 
 
613
    if((ret = cl_scandesc(fd, &virname, &info.blocks, engine, limits, options)) == CL_VIRUS) {
 
614
        logg("%s: %s FOUND\n", filename, virname);
 
615
        info.ifiles++;
 
616
 
 
617
        if(bell)
 
618
            fprintf(stderr, "\007");
 
619
 
 
620
    } else if(ret == CL_CLEAN) {
 
621
        if(!printinfected && printclean)
 
622
            mprintf("%s: OK\n", filename);
 
623
    } else
 
624
        if(!printinfected)
 
625
            logg("%s: %s\n", filename, cl_strerror(ret));
 
626
 
 
627
    close(fd);
 
628
    return ret;
 
629
}
 
630
 
 
631
static int scancompressed(const char *filename, struct cl_engine *engine, const struct passwd *user, const struct optstruct *opt, const struct cl_limits *limits, int options)
 
632
{
 
633
        int ret = 0;
 
634
        char *tmpdir, *gendir, *userprg;
 
635
        struct stat statbuf;
 
636
 
 
637
 
 
638
    stat(filename, &statbuf);
 
639
 
 
640
    if(!S_ISREG(statbuf.st_mode)) {
 
641
        logg("^Suspect archive %s (not a regular file)\n", filename);
 
642
        return 0; /* hmm ? */
 
643
    }
 
644
 
 
645
    /* check write access */
 
646
 
 
647
    tmpdir = getenv("TMPDIR");
 
648
 
 
649
    if(tmpdir == NULL)
 
650
#ifdef P_tmpdir
 
651
        tmpdir = P_tmpdir;
 
652
#else
 
653
        tmpdir = "/tmp";
 
654
#endif
 
655
 
 
656
    if(checkaccess(tmpdir, CLAMAVUSER, W_OK) != 1) {
 
657
        logg("!Can't write to the temporary directory\n");
 
658
        exit(64);
 
659
    }
 
660
 
 
661
    /* generate the temporary directory */
 
662
 
 
663
    gendir = cli_gentemp(tmpdir);
 
664
    if(mkdir(gendir, 0700)) {
 
665
        logg("!Can't create the temporary directory %s\n", gendir);
 
666
        exit(63); /* critical */
 
667
    }
 
668
 
 
669
#ifndef C_OS2
 
670
    if(user)
 
671
        chown(gendir, user->pw_uid, user->pw_gid);
 
672
#endif
 
673
 
 
674
    /* unpack file  - as unprivileged user */
 
675
    if(cli_strbcasestr(filename, ".zip")) {
 
676
        char *args[] = { "unzip", "-P", "clam", "-o", NULL, NULL };
 
677
        /* Sun's SUNWspro C compiler doesn't allow direct initialisation
 
678
         * with a variable
 
679
         */
 
680
        args[4] = (char *) filename;
 
681
 
 
682
        if((userprg = opt_arg(opt, "unzip")))
 
683
            ret = clamav_unpack(userprg, args, gendir, user, opt);
 
684
        else
 
685
            ret = clamav_unpack("unzip", args, gendir, user, opt);
 
686
 
 
687
    } else if(cli_strbcasestr(filename, ".rar")) { 
 
688
        char *args[] = { "unrar", "x", "-p-", "-y", NULL, NULL };
 
689
        args[4] = (char *) filename;
 
690
        if((userprg = opt_arg(opt, "unrar")))
 
691
            ret = clamav_unpack(userprg, args, gendir, user, opt);
 
692
        else
 
693
            ret = clamav_unpack("unrar", args, gendir, user, opt);
 
694
 
 
695
    } else if(cli_strbcasestr(filename, ".arj")) { 
 
696
        char *args[] = { "arj", "x","-y", NULL, NULL };
 
697
        args[3] = (char *) filename;
 
698
        if((userprg = opt_arg(opt, "arj")))
 
699
            ret = clamav_unpack(userprg, args, gendir, user, opt);
 
700
        else
 
701
            ret = clamav_unpack("arj", args, gendir, user, opt);
 
702
 
 
703
    } else if(cli_strbcasestr(filename, ".zoo")) { 
 
704
        char *args[] = { "unzoo", "-x","-j","./", NULL, NULL };
 
705
        args[4] = (char *) filename;
 
706
        if((userprg = opt_arg(opt, "unzoo")))
 
707
            ret = clamav_unpack(userprg, args, gendir, user, opt);
 
708
        else
 
709
            ret = clamav_unpack("unzoo", args, gendir, user, opt);
 
710
 
 
711
    } else if(cli_strbcasestr(filename, ".jar")) { 
 
712
        char *args[] = { "unzip", "-P", "clam", "-o", NULL, NULL };
 
713
        args[4] = (char *) filename;
 
714
        if((userprg = opt_arg(opt, "jar")))
 
715
            ret = clamav_unpack(userprg, args, gendir, user, opt);
 
716
        else
 
717
            ret = clamav_unpack("unzip", args, gendir, user, opt);
 
718
 
 
719
    } else if(cli_strbcasestr(filename, ".lzh")) { 
 
720
        char *args[] = { "lha", "xf", NULL, NULL };
 
721
        args[2] = (char *) filename;
 
722
        if((userprg = opt_arg(opt, "lha")))
 
723
            ret = clamav_unpack(userprg, args, gendir, user, opt);
 
724
        else
 
725
            ret = clamav_unpack("lha", args, gendir, user, opt);
 
726
 
 
727
    } else if(cli_strbcasestr(filename, ".tar")) { 
 
728
        char *args[] = { "tar", "-xpvf", NULL, NULL };
 
729
        args[2] = (char *) filename;
 
730
        if((userprg = opt_arg(opt, "tar")))
 
731
            ret = clamav_unpack(userprg, args, gendir, user, opt);
 
732
        else
 
733
            ret = clamav_unpack("tar", args, gendir, user, opt);
 
734
 
 
735
    } else if(cli_strbcasestr(filename, ".deb")) { 
 
736
        char *args[] = { "ar", "x", NULL, NULL };
 
737
        args[2] = (char *) filename;
 
738
        if((userprg = opt_arg(opt, "deb")))
 
739
            ret = clamav_unpack(userprg, args, gendir, user, opt);
 
740
        else
 
741
            ret = clamav_unpack("ar", args, gendir, user, opt);
 
742
 
 
743
    } else if((cli_strbcasestr(filename, ".tar.gz") || cli_strbcasestr(filename, ".tgz"))) {
 
744
        char *args[] = { "tar", "-zxpvf", NULL, NULL };
 
745
        args[2] = (char *) filename;
 
746
        if((userprg = opt_arg(opt, "tgz")))
 
747
            ret = clamav_unpack(userprg, args, gendir, user, opt);
 
748
        else
 
749
            ret = clamav_unpack("tar", args, gendir, user, opt);
 
750
    }
 
751
 
 
752
    /* fix permissions of extracted files */
 
753
    fixperms(gendir);
 
754
 
 
755
    if(!ret) { /* execute successful */
 
756
            short oldrec = recursion;
 
757
 
 
758
        recursion = 1;
 
759
        ret = treewalk(gendir, engine, user, opt, limits, options, 1);
 
760
        recursion = oldrec;
 
761
    }
 
762
 
 
763
    /* remove the directory  - as clamav */
 
764
    if(!opt_check(opt, "leave-temps"))
 
765
        clamav_rmdirs(gendir);
 
766
 
 
767
    /* free gendir - it's not necessary now */
 
768
    free(gendir);
 
769
 
 
770
    switch(ret) {
 
771
        case -1:
 
772
            logg("!Can't fork()\n");
 
773
            exit(61); /* this is critical problem, so we just exit here */
 
774
        case -2:
 
775
            logg("^Can't execute some unpacker. Check paths and permissions on the temporary directory\n");
 
776
            /* This is no longer a critical error (since 0.24). We scan
 
777
             * raw archive.
 
778
             */
 
779
            if((ret = checkfile(filename, engine, limits, 0, 0)) == CL_VIRUS) {
 
780
                if(opt_check(opt, "remove")) {
 
781
                    if(unlink(filename)) {
 
782
                        logg("^%s: Can't remove\n", filename);
 
783
                        info.notremoved++;
 
784
                    } else {
 
785
                        logg("%s: Removed\n", filename);
 
786
                    }
 
787
                } else if (opt_check(opt, "move") || opt_check(opt, "copy"))
 
788
                    move_infected(filename, opt);
 
789
            }
 
790
            return ret;
 
791
        case -3:
 
792
            return 0;
 
793
        case 0:
 
794
            /* no viruses found in archive, we scan just in case a raw file
 
795
             */
 
796
            if((ret = checkfile(filename, engine, limits, 0, 1)) == CL_VIRUS) {
 
797
                if(opt_check(opt, "remove")) {
 
798
                    if(unlink(filename)) {
 
799
                        logg("^%s: Can't remove\n", filename);
 
800
                        info.notremoved++;
 
801
                    } else {
 
802
                        logg("%s: Removed\n", filename);
 
803
                    }
 
804
                } else if (opt_check(opt, "move") || opt_check(opt, "copy"))
 
805
                    move_infected(filename, opt);
 
806
            }
 
807
            return ret;
 
808
        case 1:
 
809
            logg("%s: Infected.Archive FOUND\n", filename);
 
810
 
 
811
            if(bell)
 
812
                fprintf(stderr, "\007");
 
813
 
 
814
            if(opt_check(opt, "remove")) {
 
815
                if(unlink(filename)) {
 
816
                    logg("^%s: Can't remove\n", filename);
 
817
                    info.notremoved++;
 
818
                } else {
 
819
                    logg("%s: Removed\n", filename);
 
820
                }
 
821
            } else if (opt_check(opt, "move") || opt_check(opt, "copy"))
 
822
                move_infected(filename, opt);
 
823
 
 
824
            return 1;
 
825
        default:
 
826
            logg("^Strange value (%d) returned in scancompressed()\n", ret);
 
827
            return 0;
 
828
    }
 
829
}
 
830
 
 
831
static int scandenied(const char *filename, struct cl_engine *engine, const struct passwd *user, const struct optstruct *opt, const struct cl_limits *limits, int options)
 
832
{
 
833
        char *tmpdir, *gendir, *tmpfile, *pt;
 
834
        struct stat statbuf;
 
835
        int ret;
 
836
 
 
837
    stat(filename, &statbuf);
 
838
    if(!S_ISREG(statbuf.st_mode)) {
 
839
        logg("^Suspect archive %s (not a regular file)\n", filename);
 
840
        return 0;
 
841
    }
 
842
 
 
843
    /* check write access */
 
844
 
 
845
    tmpdir = getenv("TMPDIR");
 
846
 
 
847
    if(tmpdir == NULL)
 
848
#ifdef P_tmpdir
 
849
        tmpdir = P_tmpdir;
 
850
#else
 
851
        tmpdir = "/tmp";
 
852
#endif
 
853
 
 
854
 
 
855
    if(checkaccess(tmpdir, CLAMAVUSER, W_OK) != 1) {
 
856
        logg("!Can't write to the temporary directory %s\n", tmpdir);
 
857
        exit(64);
 
858
    }
 
859
 
 
860
    /* generate the temporary directory */
 
861
    gendir = cli_gentemp(tmpdir);
 
862
    if(mkdir(gendir, 0700)) {
 
863
        logg("^Can't create the temporary directory %s\n", gendir);
 
864
        exit(63); /* critical */
 
865
    }
 
866
 
 
867
    tmpfile = (char *) mcalloc(strlen(gendir) + strlen(filename) + 10, sizeof(char));
 
868
    pt = strrchr(filename, '/');
 
869
    if(!pt)
 
870
        pt = (char *) filename;
 
871
    else
 
872
        pt += 1;
 
873
 
 
874
    sprintf(tmpfile, "%s/%s", gendir, pt);
 
875
 
 
876
    if(filecopy(filename, tmpfile) == -1) {
 
877
        logg("!I/O error\n");
 
878
        perror("copyfile()");
 
879
        exit(58);
 
880
    }
 
881
 
 
882
    fixperms(gendir);
 
883
 
 
884
#ifndef C_OS2
 
885
    if(user) {
 
886
        chown(gendir, user->pw_uid, user->pw_gid);
 
887
        chown(tmpfile, user->pw_uid, user->pw_gid);
 
888
    }
 
889
#endif
 
890
 
 
891
    if((ret = treewalk(gendir, engine, user, opt, limits, options, 1)) == 1) {
 
892
        logg("(Real infected archive: %s)\n", filename);
 
893
 
 
894
        if(opt_check(opt, "remove")) {
 
895
            if(unlink(filename)) {
 
896
                logg("^%s: Can't remove\n", filename);
 
897
                info.notremoved++;
 
898
            } else {
 
899
                logg("%s: Removed\n", filename);
 
900
            }
 
901
        } else if (opt_check(opt, "move") || opt_check(opt, "copy"))
 
902
            move_infected(filename, opt);
 
903
    }
 
904
 
 
905
    /* remove the directory  - as clamav */
 
906
    clamav_rmdirs(gendir);
 
907
 
 
908
    free(gendir);
 
909
    free(tmpfile);
 
910
 
 
911
    return ret;
 
912
}
 
913
 
 
914
int scanfile(const char *filename, struct cl_engine *engine, const struct passwd *user, const struct optstruct *opt, const struct cl_limits *limits, unsigned int options)
 
915
{
 
916
        int ret, included, printclean = 1;
 
917
        const struct optnode *optnode;
 
918
        char *argument;
 
919
#ifdef C_LINUX
 
920
        struct stat sb;
 
921
 
 
922
    /* argh, don't scan /proc files */
 
923
    if(procdev)
 
924
        if(stat(filename, &sb) != -1)
 
925
            if(sb.st_dev == procdev) {
 
926
                if(!printinfected)
 
927
                    logg("%s: Excluded (/proc)\n", filename);
 
928
                return 0;
 
929
            }
 
930
#endif    
 
931
 
 
932
    if(opt_check(opt, "exclude")) {
 
933
        argument = opt_firstarg(opt, "exclude", &optnode);
 
934
        while(argument) {
 
935
            if(match_regex(filename, argument) == 1) {
 
936
                if(!printinfected)
 
937
                    logg("%s: Excluded\n", filename);
 
938
                return 0;
 
939
            }
 
940
            argument = opt_nextarg(&optnode, "exclude");
 
941
        }
 
942
    }
 
943
 
 
944
   if(opt_check(opt, "include")) {
 
945
        included = 0;
 
946
        argument = opt_firstarg(opt, "include", &optnode);
 
947
        while(argument && !included) {
 
948
            if(match_regex(filename, argument) == 1) {
 
949
                included = 1;
 
950
                break;
 
951
            }
 
952
            argument = opt_nextarg(&optnode, "include");
 
953
        }
 
954
 
 
955
        if(!included) {
 
956
            if(!printinfected)
 
957
                logg("%s: Excluded\n", filename);
 
958
            return 0;
 
959
        }
 
960
    }
 
961
 
 
962
    if(fileinfo(filename, 1) == 0) {
 
963
        if(!printinfected)
 
964
            logg("%s: Empty file\n", filename);
 
965
        return 0;
 
966
    }
 
967
 
 
968
    if(geteuid())
 
969
        if(checkaccess(filename, NULL, R_OK) != 1) {
 
970
            if(!printinfected)
 
971
                logg("%s: Access denied\n", filename);
 
972
            return 0;
 
973
        }
 
974
 
 
975
    info.files++;
 
976
 
 
977
    /* 
 
978
     * check the extension  - this is a special case, normally we don't need to
 
979
     * do this (libclamav detects archive by its magic string), but here we
 
980
     * want to know the exit code from internal unpacker and try to use
 
981
     * external (if provided) when internal cannot extract data.
 
982
     */
 
983
 
 
984
    if((cli_strbcasestr(filename, ".zip") || cli_strbcasestr(filename, ".rar")) && (options & CL_SCAN_ARCHIVE)) {
 
985
        /* try to use internal archivers */
 
986
        if((ret = checkfile(filename, engine, limits, options, 1)) == CL_VIRUS) {
 
987
            if(opt_check(opt, "remove")) {
 
988
                if(unlink(filename)) {
 
989
                    logg("^%s: Can't remove\n", filename);
 
990
                    info.notremoved++;
 
991
                } else {
 
992
                    logg("%s: Removed\n", filename);
 
993
                }
 
994
            } else if (opt_check(opt, "move") || opt_check(opt, "copy"))
 
995
                move_infected(filename, opt);
 
996
 
 
997
            return 1;
 
998
 
 
999
        } else if(ret == CL_CLEAN) {
 
1000
            return 0;
 
1001
        } else if(ret == 54) {
 
1002
            return ret;
 
1003
        }
 
1004
 
 
1005
        /* in other case try to continue with external archivers */
 
1006
        options &= ~CL_SCAN_ARCHIVE; /* and disable decompression for the checkfile() below */
 
1007
        printclean = 0;
 
1008
    }
 
1009
 
 
1010
    if((cli_strbcasestr(filename, ".zip") && opt_check(opt, "unzip"))
 
1011
    || (cli_strbcasestr(filename, ".rar") && opt_check(opt, "unrar"))
 
1012
    || (cli_strbcasestr(filename, ".arj") && opt_check(opt, "arj"))
 
1013
    || (cli_strbcasestr(filename, ".zoo") && opt_check(opt, "unzoo"))
 
1014
    || (cli_strbcasestr(filename, ".jar") && opt_check(opt, "jar"))
 
1015
    || (cli_strbcasestr(filename, ".lzh") && opt_check(opt, "lha"))
 
1016
    || (cli_strbcasestr(filename, ".tar") && opt_check(opt, "tar"))
 
1017
    || (cli_strbcasestr(filename, ".deb") && opt_check(opt, "deb"))
 
1018
    || ((cli_strbcasestr(filename, ".tar.gz") || cli_strbcasestr(filename, ".tgz")) 
 
1019
         && (opt_check(opt, "tgz") || opt_check(opt, "deb"))) ) {
 
1020
 
 
1021
        /* check permissions */
 
1022
        switch(checkaccess(filename, CLAMAVUSER, R_OK)) {
 
1023
            case -1:
 
1024
                logg("^Can't get information about user "CLAMAVUSER"\n");
 
1025
                exit(60); /* this is a critical problem so we just exit here */
 
1026
            case -2:
 
1027
                logg("^Can't fork\n");
 
1028
                exit(61);
 
1029
            case 0: /* read access denied */
 
1030
                if(geteuid()) {
 
1031
                    if(!printinfected)
 
1032
                        logg("^%s: Access denied to archive\n", filename);
 
1033
                } else {
 
1034
 
 
1035
                    if(limits && limits->maxfilesize)
 
1036
                        if((unsigned int) fileinfo(filename, 1) / 1024 > limits->maxfilesize) {
 
1037
                            if(!printinfected)
 
1038
                                logg("^%s: Archive too big\n", filename);
 
1039
                            return 0;
 
1040
                        }
 
1041
 
 
1042
                    return(scandenied(filename, engine, user, opt, limits, options));
 
1043
                }
 
1044
                return 0;
 
1045
            case 1:
 
1046
                return(scancompressed(filename, engine, user, opt, limits, options));
 
1047
        }
 
1048
    }
 
1049
 
 
1050
    if((ret = checkfile(filename, engine, limits, options, printclean)) == CL_VIRUS) {
 
1051
        if(opt_check(opt, "remove")) {
 
1052
            if(unlink(filename)) {
 
1053
                logg("^%s: Can't remove\n", filename);
 
1054
                info.notremoved++;
 
1055
            } else {
 
1056
                logg("%s: Removed\n", filename);
 
1057
            }
 
1058
        } else if (opt_check(opt, "move") || opt_check(opt, "copy"))
 
1059
            move_infected(filename, opt);
 
1060
    }
748
1061
    return ret;
749
1062
}