~ubuntu-branches/ubuntu/raring/clamav/raring

« back to all changes in this revision

Viewing changes to clamscan/manager.c

  • Committer: Bazaar Package Importer
  • Author(s): Stephen Gran
  • Date: 2008-09-05 17:25:34 UTC
  • mfrom: (0.35.1 lenny)
  • Revision ID: james.westby@ubuntu.com-20080905172534-yi3f8fkye1o7u1r3
* New upstream version (closes: #497662, #497773)
  - lots of new options for clamd.conf
  - fixes CVEs CVE-2008-3912, CVE-2008-3913, CVE-2008-3914, and
    CVE-2008-1389
* No longer supports --unzip option, so typo is gone (closes: #496276)
* Translations:
  - sv (thanks Martin Bagge <brother@bsnet.se>) (closes: #491760)

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
#include <sys/wait.h>
35
35
#include <utime.h>
36
36
#endif
37
 
#ifdef HAVE_GRP_H
38
 
#include <grp.h>
39
 
#endif
40
 
#ifdef HAVE_PWD_H
41
 
#include <pwd.h>
 
37
#ifndef C_WINDOWS
 
38
#include <dirent.h>
 
39
#include <sys/time.h>
 
40
#include <sys/resource.h>
42
41
#endif
43
42
#include <fcntl.h>
44
43
#ifdef  HAVE_UNISTD_H
50
49
 
51
50
#include "manager.h"
52
51
#include "others.h"
53
 
#include "treewalk.h"
54
52
#include "global.h"
55
53
 
56
54
#include "shared/options.h"
61
59
#include "libclamav/others.h"
62
60
#include "libclamav/matcher-ac.h"
63
61
#include "libclamav/str.h"
 
62
#include "libclamav/readdb.h"
64
63
 
65
64
#ifdef C_LINUX
66
65
dev_t procdev;
75
74
#define O_BINARY    0
76
75
#endif
77
76
 
78
 
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)
79
 
{
80
 
    return treewalk(dirname, engine, user, opt, limits, options, 1);
 
77
static void move_infected(const char *filename, const struct optstruct *opt);
 
78
 
 
79
static int scanfile(const char *filename, struct cl_engine *engine, const struct optstruct *opt, const struct cl_limits *limits, unsigned int options)
 
80
{
 
81
        int ret = 0, fd, included, printclean = 1;
 
82
        const struct optnode *optnode;
 
83
        char *argument;
 
84
        const char *virname;
 
85
#ifdef C_LINUX
 
86
        struct stat sb;
 
87
 
 
88
    /* argh, don't scan /proc files */
 
89
    if(procdev)
 
90
        if(stat(filename, &sb) != -1)
 
91
            if(sb.st_dev == procdev) {
 
92
                if(!printinfected)
 
93
                    logg("~%s: Excluded (/proc)\n", filename);
 
94
                return 0;
 
95
            }
 
96
#endif    
 
97
 
 
98
    if(opt_check(opt, "exclude")) {
 
99
        argument = opt_firstarg(opt, "exclude", &optnode);
 
100
        while(argument) {
 
101
            if(match_regex(filename, argument) == 1) {
 
102
                if(!printinfected)
 
103
                    logg("~%s: Excluded\n", filename);
 
104
                return 0;
 
105
            }
 
106
            argument = opt_nextarg(&optnode, "exclude");
 
107
        }
 
108
    }
 
109
 
 
110
   if(opt_check(opt, "include")) {
 
111
        included = 0;
 
112
        argument = opt_firstarg(opt, "include", &optnode);
 
113
        while(argument && !included) {
 
114
            if(match_regex(filename, argument) == 1) {
 
115
                included = 1;
 
116
                break;
 
117
            }
 
118
            argument = opt_nextarg(&optnode, "include");
 
119
        }
 
120
 
 
121
        if(!included) {
 
122
            if(!printinfected)
 
123
                logg("~%s: Excluded\n", filename);
 
124
            return 0;
 
125
        }
 
126
    }
 
127
 
 
128
    if(fileinfo(filename, 1) == 0) {
 
129
        if(!printinfected)
 
130
            logg("~%s: Empty file\n", filename);
 
131
        return 0;
 
132
    }
 
133
 
 
134
#ifndef C_WINDOWS
 
135
    if(geteuid())
 
136
        if(checkaccess(filename, NULL, R_OK) != 1) {
 
137
            if(!printinfected)
 
138
                logg("~%s: Access denied\n", filename);
 
139
            return 0;
 
140
        }
 
141
#endif
 
142
 
 
143
    logg("*Scanning %s\n", filename);
 
144
 
 
145
    if((fd = open(filename, O_RDONLY|O_BINARY)) == -1) {
 
146
        logg("^Can't open file %s\n", filename);
 
147
        return 54;
 
148
    }
 
149
 
 
150
    info.files++;
 
151
 
 
152
    if((ret = cl_scandesc(fd, &virname, &info.blocks, engine, limits, options)) == CL_VIRUS) {
 
153
        logg("~%s: %s FOUND\n", filename, virname);
 
154
        info.ifiles++;
 
155
 
 
156
        if(bell)
 
157
            fprintf(stderr, "\007");
 
158
 
 
159
    } else if(ret == CL_CLEAN) {
 
160
        if(!printinfected && printclean)
 
161
            mprintf("~%s: OK\n", filename);
 
162
    } else
 
163
        if(!printinfected)
 
164
            logg("~%s: %s\n", filename, cl_strerror(ret));
 
165
 
 
166
    close(fd);
 
167
 
 
168
    if(ret == CL_VIRUS) {
 
169
        if(opt_check(opt, "remove")) {
 
170
            if(unlink(filename)) {
 
171
                logg("^%s: Can't remove\n", filename);
 
172
                info.notremoved++;
 
173
            } else {
 
174
                logg("~%s: Removed\n", filename);
 
175
            }
 
176
        } else if(opt_check(opt, "move") || opt_check(opt, "copy"))
 
177
            move_infected(filename, opt);
 
178
    }
 
179
 
 
180
    return ret;
 
181
}
 
182
 
 
183
static int scandirs(const char *dirname, struct cl_engine *engine, const struct optstruct *opt, const struct cl_limits *limits, unsigned int options, unsigned int depth)
 
184
{
 
185
        DIR *dd;
 
186
        struct dirent *dent;
 
187
        struct stat statbuf;
 
188
        char *fname;
 
189
        int scanret = 0, included;
 
190
        unsigned int maxdepth;
 
191
        const struct optnode *optnode;
 
192
        char *argument;
 
193
 
 
194
 
 
195
    if(opt_check(opt, "exclude-dir")) {
 
196
        argument = opt_firstarg(opt, "exclude-dir", &optnode);
 
197
        while(argument) {
 
198
            if(match_regex(dirname, argument) == 1) {
 
199
                if(!printinfected)
 
200
                    logg("~%s: Excluded\n", dirname);
 
201
                return 0;
 
202
            }
 
203
            argument = opt_nextarg(&optnode, "exclude-dir");
 
204
        }
 
205
    }
 
206
 
 
207
   if(opt_check(opt, "include-dir")) {
 
208
        included = 0;
 
209
        argument = opt_firstarg(opt, "include-dir", &optnode);
 
210
        while(argument && !included) {
 
211
            if(match_regex(dirname, argument) == 1) {
 
212
                included = 1;
 
213
                break;
 
214
            }
 
215
            argument = opt_nextarg(&optnode, "include-dir");
 
216
        }
 
217
 
 
218
        if(!included) {
 
219
            if(!printinfected)
 
220
                logg("~%s: Excluded\n", dirname);
 
221
            return 0;
 
222
        }
 
223
    }
 
224
 
 
225
    if(opt_check(opt, "max-dir-recursion"))
 
226
        maxdepth = atoi(opt_arg(opt, "max-dir-recursion"));
 
227
    else
 
228
        maxdepth = 15;
 
229
 
 
230
    if(depth > maxdepth)
 
231
        return 0;
 
232
 
 
233
    info.dirs++;
 
234
    depth++;
 
235
 
 
236
    if((dd = opendir(dirname)) != NULL) {
 
237
        while((dent = readdir(dd))) {
 
238
#if !defined(C_INTERIX) && !defined(C_WINDOWS)
 
239
            if(dent->d_ino)
 
240
#endif
 
241
            {
 
242
                if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) {
 
243
                    /* build the full name */
 
244
                    fname = malloc(strlen(dirname) + strlen(dent->d_name) + 2);
 
245
                    sprintf(fname, "%s/%s", dirname, dent->d_name);
 
246
 
 
247
                    /* stat the file */
 
248
                    if(lstat(fname, &statbuf) != -1) {
 
249
                        if(S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode) && recursion) {
 
250
                            if(scandirs(fname, engine, opt, limits, options, depth) == 1)
 
251
                                scanret++;
 
252
                        } else {
 
253
                            if(S_ISREG(statbuf.st_mode))
 
254
                                scanret += scanfile(fname, engine, opt, limits, options);
 
255
                        }
 
256
                    }
 
257
                    free(fname);
 
258
                }
 
259
 
 
260
            }
 
261
        }
 
262
    } else {
 
263
        if(!printinfected)
 
264
            logg("~%s: Can't open directory.\n", dirname);
 
265
        return 53;
 
266
    }
 
267
 
 
268
    closedir(dd);
 
269
 
 
270
    if(scanret)
 
271
        return 1;
 
272
    else
 
273
        return 0;
 
274
 
81
275
}
82
276
 
83
277
static int scanstdin(const struct cl_engine *engine, const struct cl_limits *limits, int options)
85
279
        int ret;
86
280
        const char *virname, *tmpdir;
87
281
        char *file, buff[FILEBUFF];
 
282
        size_t bread;
88
283
        FILE *fs;
89
284
 
90
285
 
111
306
        return 63;
112
307
    }
113
308
 
114
 
    while((ret = fread(buff, 1, FILEBUFF, stdin)))
115
 
        if(fwrite(buff, 1, ret, fs) < ret) {
 
309
    while((bread = fread(buff, 1, FILEBUFF, stdin)))
 
310
        if(fwrite(buff, 1, bread, fs) < bread) {
116
311
            logg("!Can't write to %s\n", file);
117
312
            free(file);
118
313
            return 58;
145
340
int scanmanager(const struct optstruct *opt)
146
341
{
147
342
        mode_t fmode;
148
 
        int ret = 0, extunpacker = 0, fmodeint, i, x;
 
343
        int ret = 0, fmodeint, i, x;
149
344
        unsigned int options = 0, dboptions = 0;
150
345
        struct cl_engine *engine = NULL;
151
346
        struct cl_limits limits;
152
 
        struct passwd *user = NULL;
153
347
        struct stat sb;
154
 
        char *fullpath = NULL, cwd[1024];
155
 
 
156
 
 
157
 
    if(opt_check(opt, "unzip") || opt_check(opt, "unrar") || opt_check(opt, "arj") ||
158
 
       opt_check(opt, "unzoo") || opt_check(opt, "jar") || opt_check(opt, "lha") ||
159
 
       opt_check(opt, "tar") || opt_check(opt, "tgz") || opt_check(opt, "deb"))
160
 
            extunpacker = 1;
161
 
 
162
 
/* njh@bandsman.co.uk: BeOS */
163
 
#if !defined(C_CYGWIN) && !defined(C_OS2) && !defined(C_BEOS) && !defined(C_WINDOWS)
164
 
    if(extunpacker && !geteuid()) {
165
 
        if((user = getpwnam(CLAMAVUSER)) == NULL) {
166
 
            logg("!Can't get information about user "CLAMAVUSER" (required to run external unpackers)\n");
167
 
            exit(60); /* this is critical problem, so we just exit here */
168
 
        }
169
 
    }
 
348
        char *file, cwd[1024], *pua_cats = NULL, *argument;
 
349
        const struct optnode *optnode;
 
350
#ifndef C_WINDOWS
 
351
        struct rlimit rlim;
170
352
#endif
171
353
 
 
354
 
172
355
    if(!opt_check(opt, "no-phishing-sigs"))
173
356
        dboptions |= CL_DB_PHISHING;
174
357
 
180
363
    if(opt_check(opt,"phishing-cloak")) {
181
364
        options |= CL_SCAN_PHISHING_BLOCKCLOAK;
182
365
    }
 
366
    if(opt_check(opt,"heuristic-scan-precedence")) {
 
367
        options |= CL_SCAN_HEURISTIC_PRECEDENCE;
 
368
    }
183
369
 
184
370
    if(opt_check(opt, "dev-ac-only"))
185
371
        dboptions |= CL_DB_ACONLY;
187
373
    if(opt_check(opt, "dev-ac-depth"))
188
374
        cli_ac_setdepth(AC_DEFAULT_MIN_DEPTH, atoi(opt_arg(opt, "dev-ac-depth")));
189
375
 
190
 
    if(opt_check(opt, "detect-pua"))
 
376
    if(opt_check(opt, "detect-pua")) {
191
377
        dboptions |= CL_DB_PUA;
192
378
 
 
379
        if(opt_check(opt, "exclude-pua")) {
 
380
            dboptions |= CL_DB_PUA_EXCLUDE;
 
381
            argument = opt_firstarg(opt, "exclude-pua", &optnode);
 
382
            i = 0;
 
383
            while(argument) {
 
384
                if(!(pua_cats = realloc(pua_cats, i + strlen(argument) + 3))) {
 
385
                    logg("!Can't allocate memory for pua_cats\n");
 
386
                    return 70;
 
387
                }
 
388
                sprintf(pua_cats + i, ".%s", argument);
 
389
                i += strlen(argument) + 1;
 
390
                pua_cats[i] = 0;
 
391
                argument = opt_nextarg(&optnode, "exclude-pua");
 
392
            }
 
393
            pua_cats[i] = '.';
 
394
            pua_cats[i + 1] = 0;
 
395
        }
 
396
 
 
397
        if(opt_check(opt, "include-pua")) {
 
398
            if(pua_cats) {
 
399
                logg("!--exclude-pua and --include-pua cannot be used at the same time\n");
 
400
                free(pua_cats);
 
401
                return 40;
 
402
            }
 
403
            dboptions |= CL_DB_PUA_INCLUDE;
 
404
            argument = opt_firstarg(opt, "include-pua", &optnode);
 
405
            i = 0;
 
406
            while(argument) {
 
407
                if(!(pua_cats = realloc(pua_cats, i + strlen(argument) + 3))) {
 
408
                    logg("!Can't allocate memory for pua_cats\n");
 
409
                    return 70;
 
410
                }
 
411
                sprintf(pua_cats + i, ".%s", argument);
 
412
                i += strlen(argument) + 1;
 
413
                pua_cats[i] = 0;
 
414
                argument = opt_nextarg(&optnode, "include-pua");
 
415
            }
 
416
            pua_cats[i] = '.';
 
417
            pua_cats[i + 1] = 0;
 
418
        }
 
419
 
 
420
        if(pua_cats) {
 
421
            /* FIXME with the new API */
 
422
            if((ret = cli_initengine(&engine, dboptions))) {
 
423
                logg("!cli_initengine() failed: %s\n", cl_strerror(ret));
 
424
                free(pua_cats);
 
425
                return 50;
 
426
            }
 
427
            engine->pua_cats = pua_cats;
 
428
        }
 
429
    }
 
430
 
193
431
    if(opt_check(opt, "database")) {
194
432
        if((ret = cl_load(opt_arg(opt, "database"), &engine, &info.sigs, dboptions))) {
195
433
            logg("!%s\n", cl_strerror(ret));
248
486
    } else
249
487
        limits.maxfilesize = 26214400;
250
488
 
 
489
#ifndef C_WINDOWS
 
490
    if(getrlimit(RLIMIT_FSIZE, &rlim) == 0) {
 
491
        if((rlim.rlim_max < limits.maxfilesize) || (rlim.rlim_max < limits.maxscansize))
 
492
            logg("^System limit for file size is lower than maxfilesize or maxscansize\n");
 
493
    } else {
 
494
        logg("^Cannot obtain resource limits for file size\n");
 
495
    }
 
496
#endif
 
497
 
251
498
    if(opt_check(opt, "max-files"))
252
499
        limits.maxfiles = atoi(opt_arg(opt, "max-files"));
253
500
    else
310
557
    else
311
558
        options |= CL_SCAN_ALGORITHMIC;
312
559
 
 
560
    if(opt_check(opt, "detect-structured")) {
 
561
        options |= CL_SCAN_STRUCTURED;
 
562
 
 
563
        if(opt_check(opt, "structured-ssn-format")) {
 
564
            switch(atoi(opt_arg(opt, "structured-ssn-format"))) {
 
565
                case 0:
 
566
                    options |= CL_SCAN_STRUCTURED_SSN_NORMAL;
 
567
                    break;
 
568
                case 1:
 
569
                    options |= CL_SCAN_STRUCTURED_SSN_STRIPPED;
 
570
                    break;
 
571
                case 2:
 
572
                    options |= (CL_SCAN_STRUCTURED_SSN_NORMAL | CL_SCAN_STRUCTURED_SSN_STRIPPED);
 
573
                    break;
 
574
                default:
 
575
                    logg("!Invalid argument for --structured-ssn-format\n");
 
576
                    return 40;
 
577
            }
 
578
        } else {
 
579
            options |= CL_SCAN_STRUCTURED_SSN_NORMAL;
 
580
        }
 
581
 
 
582
        if(opt_check(opt, "structured-ssn-count"))
 
583
            limits.min_ssn_count = atoi(opt_arg(opt, "structured-ssn-count"));
 
584
        else
 
585
            limits.min_ssn_count = 3;
 
586
 
 
587
        if(opt_check(opt, "structured-cc-count"))
 
588
            limits.min_cc_count = atoi(opt_arg(opt, "structured-cc-count"));
 
589
        else
 
590
            limits.min_cc_count = 3;
 
591
 
 
592
    } else
 
593
        options &= ~CL_SCAN_STRUCTURED;
 
594
 
 
595
 
313
596
#ifdef C_LINUX
314
597
    procdev = (dev_t) 0;
315
598
    if(stat("/proc", &sb) != -1 && !sb.st_size)
324
607
            logg("!Can't get absolute pathname of current working directory\n");
325
608
            ret = 57;
326
609
        } else
327
 
            ret = scandirs(cwd, engine, user, opt, &limits, options);
 
610
            ret = scandirs(cwd, engine, opt, &limits, options, 1);
328
611
 
329
612
    } else if(!strcmp(opt->filename, "-")) { /* read data from stdin */
330
613
        ret = scanstdin(engine, &limits, options);
331
614
 
332
615
    } else {
333
 
        char *thefilename;
334
 
        for (x = 0; (thefilename = cli_strtok(opt->filename, x, "\t")) != NULL; x++) {
335
 
            if((fmodeint = fileinfo(thefilename, 2)) == -1) {
336
 
                logg("^Can't access file %s\n", thefilename);
337
 
                perror(thefilename);
 
616
        for (x = 0; (file = cli_strtok(opt->filename, x, "\t")) != NULL; x++) {
 
617
            if((fmodeint = fileinfo(file, 2)) == -1) {
 
618
                logg("^Can't access file %s\n", file);
 
619
                perror(file);
338
620
                ret = 56;
339
621
            } else {
340
622
                int slash = 1;
341
 
                for(i = strlen(thefilename) - 1; i > 0 && slash; i--) {
342
 
                    if(thefilename[i] == '/')
343
 
                        thefilename[i] = 0;
 
623
                for(i = strlen(file) - 1; i > 0 && slash; i--) {
 
624
                    if(file[i] == '/')
 
625
                        file[i] = 0;
344
626
                    else
345
627
                        slash = 0;
346
628
                }
347
629
 
348
630
                fmode = (mode_t) fmodeint;
349
631
 
350
 
                if(extunpacker && (thefilename[0] != '/' && thefilename[0] != '\\' && thefilename[1] != ':')) {
351
 
                    /* we need to complete the path */
352
 
                    if(!getcwd(cwd, sizeof(cwd))) {
353
 
                        logg("!Can't get absolute pathname of current working directory\n");
354
 
                        return 57;
355
 
                    } else {
356
 
                        fullpath = malloc(512);
357
 
#ifdef NO_SNPRINTF
358
 
                        sprintf(fullpath, "%s/%s", cwd, thefilename);
359
 
#else
360
 
                        snprintf(fullpath, 512, "%s/%s", cwd, thefilename);
361
 
#endif
362
 
                        logg("*Full path: %s\n", fullpath);
363
 
                    }
364
 
                } else
365
 
                    fullpath = thefilename;
366
 
 
367
632
                switch(fmode & S_IFMT) {
368
633
                    case S_IFREG:
369
 
                        ret = scanfile(fullpath, engine, user, opt, &limits, options);
 
634
                        ret = scanfile(file, engine, opt, &limits, options);
370
635
                        break;
371
636
 
372
637
                    case S_IFDIR:
373
 
                        ret = scandirs(fullpath, engine, user, opt, &limits, options);
 
638
                        ret = scandirs(file, engine, opt, &limits, options, 1);
374
639
                        break;
375
640
 
376
641
                    default:
377
 
                        logg("!Not supported file type (%s)\n", thefilename);
 
642
                        logg("!Not supported file type (%s)\n", file);
378
643
                        ret = 52;
379
644
                }
380
 
 
381
 
                if(extunpacker && (thefilename[0] != '/' && thefilename[0] != '\\' && thefilename[1] != ':')) {
382
 
                    free(fullpath);
383
 
                    fullpath = NULL;
384
 
                }
385
645
            }
386
 
            free(thefilename);
 
646
            free(file);
387
647
        }
388
648
    }
389
649
 
399
659
    return ret;
400
660
}
401
661
 
402
 
/*
403
 
 * -1 -> can't fork
404
 
 * -2 -> can't execute
405
 
 * -3 -> external signal
406
 
 * 0 -> OK
407
 
 */
408
 
 
409
 
#ifdef C_WINDOWS
410
 
static int clamav_unpack(const char *prog, const char **args, const char *tmpdir, const struct passwd *user, const struct optstruct *opt)
411
 
{
412
 
    /* TODO: use spamvp(P_WAIT, prog, args); */
413
 
    cli_errmsg("clamav_unpack is not supported under Windows yet\n");
414
 
    return -1;
415
 
}
416
 
#else
417
 
static int clamav_unpack(const char *prog, const char **args, const char *tmpdir, const struct passwd *user, const struct optstruct *opt)
418
 
{
419
 
        pid_t pid;
420
 
        int status, wret, fdevnull;
421
 
        unsigned int maxfiles, maxscansize;
422
 
        struct s_du n;
423
 
 
424
 
 
425
 
    if(opt_check(opt, "max-files"))
426
 
        maxfiles = atoi(opt_arg(opt, "max-files"));
427
 
    else
428
 
        maxfiles = 10000;
429
 
 
430
 
    if(opt_check(opt, "max-scansize")) {
431
 
            char *cpy, *ptr;
432
 
        ptr = opt_arg(opt, "max-scansize");
433
 
        if(tolower(ptr[strlen(ptr) - 1]) == 'm') { /* megabytes */
434
 
            cpy = calloc(strlen(ptr), 1);
435
 
            strncpy(cpy, ptr, strlen(ptr) - 1);
436
 
            cpy[strlen(ptr)-1]='\0';
437
 
            maxscansize = atoi(cpy) * 1024;
438
 
            free(cpy);
439
 
        } else /* default - kilobytes */
440
 
            maxscansize = atoi(ptr);
441
 
    } else
442
 
        maxscansize = 104857600;
443
 
 
444
 
    switch(pid = fork()) {
445
 
        case -1:
446
 
            return -1;
447
 
        case 0:
448
 
#ifndef C_CYGWIN
449
 
            if(!geteuid() && user) {
450
 
 
451
 
#ifdef HAVE_SETGROUPS
452
 
                if(setgroups(1, &user->pw_gid)) {
453
 
                    fprintf(stderr, "ERROR: setgroups() failed\n");
454
 
                    exit(1);
455
 
                }
456
 
#endif
457
 
 
458
 
                if(setgid(user->pw_gid)) {
459
 
                    fprintf(stderr, "ERROR: setgid(%d) failed\n", (int) user->pw_gid);
460
 
                    exit(1);
461
 
                }
462
 
 
463
 
                if(setuid(user->pw_uid)) {
464
 
                    fprintf(stderr, "ERROR: setuid(%d) failed\n", (int) user->pw_uid);
465
 
                    exit(1);
466
 
                }
467
 
            }
468
 
#endif
469
 
            if(chdir(tmpdir) == -1) {
470
 
                fprintf(stderr, "ERROR: chdir(%s) failed\n", tmpdir);
471
 
                exit(1);
472
 
            }
473
 
 
474
 
            if(printinfected) {
475
 
                fdevnull = open("/dev/null", O_WRONLY);
476
 
                if(fdevnull == -1) {
477
 
                    logg("Non fatal error: cannot open /dev/null. Continuing with full output\n");
478
 
                    printinfected = 0;
479
 
                } else {
480
 
                    dup2(fdevnull,1);
481
 
                    dup2(fdevnull,2);
482
 
                }
483
 
            }
484
 
 
485
 
            if(strchr(prog, '/')) /* we have full path */
486
 
                execv(prog, args);
487
 
            else
488
 
                execvp(prog, args);
489
 
            perror("execv(p)");
490
 
            abort();
491
 
            break;
492
 
        default:
493
 
 
494
 
            if(maxscansize || maxfiles) {
495
 
                while(!(wret = waitpid(pid, &status, WNOHANG))) {
496
 
                    memset(&n, 0, sizeof(struct s_du));
497
 
 
498
 
                    if(!du(tmpdir, &n))
499
 
                        if((maxfiles && n.files > maxfiles) || (maxscansize && n.space > maxscansize)) {
500
 
                            logg("*n.files: %u, n.space: %lu\n", n.files, n.space);
501
 
                            kill(pid, 9); /* stop it immediately */
502
 
                        }
503
 
                }
504
 
            } else
505
 
                waitpid(pid, &status, 0);
506
 
 
507
 
 
508
 
            if(WIFSIGNALED(status)) {
509
 
                switch(WTERMSIG(status)) {
510
 
 
511
 
                    case 9:
512
 
                        logg("\nUnpacker process %d stopped due to exceeded limits\n", pid);
513
 
                        return 0;
514
 
                    case 6: /* abort */
515
 
                        logg("^Can't run %s\n", prog);
516
 
                        return -2;
517
 
                    default:
518
 
                        logg("^\nUnpacker stopped with external signal %d\n", WTERMSIG(status));
519
 
                        return -3;
520
 
                }
521
 
            } else if(WIFEXITED(status))
522
 
                return 0;
523
 
    }
524
 
 
525
 
    return 0;
526
 
}
527
 
#endif
528
 
 
529
662
static void move_infected(const char *filename, const struct optstruct *opt)
530
663
{
531
664
        char *movedir, *movefilename, numext[4 + 1];
613
746
 
614
747
        chmod(movefilename, ofstat.st_mode);
615
748
#ifndef C_OS2
616
 
        chown(movefilename, ofstat.st_uid, ofstat.st_gid);
 
749
        if(chown(movefilename, ofstat.st_uid, ofstat.st_gid) == -1) {
 
750
                logg("!Can't chown '%s': %s\n", movefilename, strerror(errno));
 
751
        }
617
752
#endif
618
753
 
619
754
        ubuf.actime = ofstat.st_atime;
633
768
    free(movefilename);
634
769
}
635
770
 
636
 
static int checkfile(const char *filename, const struct cl_engine *engine, const struct cl_limits *limits, int options, short printclean)
637
 
{
638
 
        int fd, ret;
639
 
        const char *virname;
640
 
 
641
 
 
642
 
    logg("*Scanning %s\n", filename);
643
 
 
644
 
    if((fd = open(filename, O_RDONLY|O_BINARY)) == -1) {
645
 
        logg("^Can't open file %s\n", filename);
646
 
        return 54;
647
 
    }
648
 
 
649
 
    if((ret = cl_scandesc(fd, &virname, &info.blocks, engine, limits, options)) == CL_VIRUS) {
650
 
        logg("~%s: %s FOUND\n", filename, virname);
651
 
        info.ifiles++;
652
 
 
653
 
        if(bell)
654
 
            fprintf(stderr, "\007");
655
 
 
656
 
    } else if(ret == CL_CLEAN) {
657
 
        if(!printinfected && printclean)
658
 
            mprintf("~%s: OK\n", filename);
659
 
    } else
660
 
        if(!printinfected)
661
 
            logg("~%s: %s\n", filename, cl_strerror(ret));
662
 
 
663
 
    close(fd);
664
 
    return ret;
665
 
}
666
 
 
667
 
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)
668
 
{
669
 
        int ret = 0;
670
 
        char *gendir, *userprg;
671
 
        const char *tmpdir;
672
 
        struct stat statbuf;
673
 
 
674
 
 
675
 
    stat(filename, &statbuf);
676
 
 
677
 
    if(!S_ISREG(statbuf.st_mode)) {
678
 
        logg("^Suspect archive %s (not a regular file)\n", filename);
679
 
        return 0; /* hmm ? */
680
 
    }
681
 
 
682
 
    /* check write access */
683
 
 
684
 
    tmpdir = getenv("TMPDIR");
685
 
 
686
 
    if(tmpdir == NULL)
687
 
#ifdef P_tmpdir
688
 
        tmpdir = P_tmpdir;
689
 
#else
690
 
        tmpdir = "/tmp";
691
 
#endif
692
 
 
693
 
    if(checkaccess(tmpdir, CLAMAVUSER, W_OK) != 1) {
694
 
        logg("!Can't write to the temporary directory\n");
695
 
        exit(64);
696
 
    }
697
 
 
698
 
    /* generate the temporary directory */
699
 
 
700
 
    gendir = cli_gentemp(tmpdir);
701
 
    if(mkdir(gendir, 0700)) {
702
 
        logg("!Can't create the temporary directory %s\n", gendir);
703
 
        exit(63); /* critical */
704
 
    }
705
 
 
706
 
#if !defined(C_OS2) && !defined(C_WINDOWS)
707
 
    /* FIXME: do the correct native windows way */
708
 
    if(user)
709
 
        chown(gendir, user->pw_uid, user->pw_gid);
710
 
#endif
711
 
 
712
 
    /* unpack file  - as unprivileged user */
713
 
    if(cli_strbcasestr(filename, ".zip")) {
714
 
        const char *args[] = { "unzip", "-P", "clam", "-o", NULL, NULL };
715
 
        /* Sun's SUNWspro C compiler doesn't allow direct initialisation
716
 
         * with a variable
717
 
         */
718
 
        args[4] = filename;
719
 
 
720
 
        if((userprg = opt_arg(opt, "unzip")))
721
 
            ret = clamav_unpack(userprg, args, gendir, user, opt);
722
 
        else
723
 
            ret = clamav_unpack("unzip", args, gendir, user, opt);
724
 
 
725
 
    } else if(cli_strbcasestr(filename, ".rar")) { 
726
 
        const char *args[] = { "unrar", "x", "-p-", "-y", NULL, NULL };
727
 
        args[4] = filename;
728
 
        if((userprg = opt_arg(opt, "unrar")))
729
 
            ret = clamav_unpack(userprg, args, gendir, user, opt);
730
 
        else
731
 
            ret = clamav_unpack("unrar", args, gendir, user, opt);
732
 
 
733
 
    } else if(cli_strbcasestr(filename, ".arj")) { 
734
 
        const char *args[] = { "arj", "x","-y", NULL, NULL };
735
 
        args[3] = filename;
736
 
        if((userprg = opt_arg(opt, "arj")))
737
 
            ret = clamav_unpack(userprg, args, gendir, user, opt);
738
 
        else
739
 
            ret = clamav_unpack("arj", args, gendir, user, opt);
740
 
 
741
 
    } else if(cli_strbcasestr(filename, ".zoo")) { 
742
 
        const char *args[] = { "unzoo", "-x","-j","./", NULL, NULL };
743
 
        args[4] = filename;
744
 
        if((userprg = opt_arg(opt, "unzoo")))
745
 
            ret = clamav_unpack(userprg, args, gendir, user, opt);
746
 
        else
747
 
            ret = clamav_unpack("unzoo", args, gendir, user, opt);
748
 
 
749
 
    } else if(cli_strbcasestr(filename, ".jar")) { 
750
 
        const char *args[] = { "unzip", "-P", "clam", "-o", NULL, NULL };
751
 
        args[4] = filename;
752
 
        if((userprg = opt_arg(opt, "jar")))
753
 
            ret = clamav_unpack(userprg, args, gendir, user, opt);
754
 
        else
755
 
            ret = clamav_unpack("unzip", args, gendir, user, opt);
756
 
 
757
 
    } else if(cli_strbcasestr(filename, ".lzh")) { 
758
 
        const char *args[] = { "lha", "xf", NULL, NULL };
759
 
        args[2] = filename;
760
 
        if((userprg = opt_arg(opt, "lha")))
761
 
            ret = clamav_unpack(userprg, args, gendir, user, opt);
762
 
        else
763
 
            ret = clamav_unpack("lha", args, gendir, user, opt);
764
 
 
765
 
    } else if(cli_strbcasestr(filename, ".tar")) { 
766
 
        const char *args[] = { "tar", "-xpvf", NULL, NULL };
767
 
        args[2] = filename;
768
 
        if((userprg = opt_arg(opt, "tar")))
769
 
            ret = clamav_unpack(userprg, args, gendir, user, opt);
770
 
        else
771
 
            ret = clamav_unpack("tar", args, gendir, user, opt);
772
 
 
773
 
    } else if(cli_strbcasestr(filename, ".deb")) { 
774
 
        const char *args[] = { "ar", "x", NULL, NULL };
775
 
        args[2] = filename;
776
 
        if((userprg = opt_arg(opt, "deb")))
777
 
            ret = clamav_unpack(userprg, args, gendir, user, opt);
778
 
        else
779
 
            ret = clamav_unpack("ar", args, gendir, user, opt);
780
 
 
781
 
    } else if((cli_strbcasestr(filename, ".tar.gz") || cli_strbcasestr(filename, ".tgz"))) {
782
 
        const char *args[] = { "tar", "-zxpvf", NULL, NULL };
783
 
        args[2] = filename;
784
 
        if((userprg = opt_arg(opt, "tgz")))
785
 
            ret = clamav_unpack(userprg, args, gendir, user, opt);
786
 
        else
787
 
            ret = clamav_unpack("tar", args, gendir, user, opt);
788
 
    }
789
 
 
790
 
    /* fix permissions of extracted files */
791
 
    fixperms(gendir);
792
 
 
793
 
    if(!ret) { /* execute successful */
794
 
            short oldrec = recursion;
795
 
 
796
 
        recursion = 1;
797
 
        ret = treewalk(gendir, engine, user, opt, limits, options, 1);
798
 
        recursion = oldrec;
799
 
    }
800
 
 
801
 
    /* remove the directory  - as clamav */
802
 
    if(!opt_check(opt, "leave-temps"))
803
 
        clamav_rmdirs(gendir);
804
 
 
805
 
    /* free gendir - it's not necessary now */
806
 
    free(gendir);
807
 
 
808
 
    switch(ret) {
809
 
        case -1:
810
 
            logg("!Can't fork()\n");
811
 
            exit(61); /* this is critical problem, so we just exit here */
812
 
        case -2:
813
 
            logg("^Can't execute some unpacker. Check paths and permissions on the temporary directory\n");
814
 
            /* This is no longer a critical error (since 0.24). We scan
815
 
             * raw archive.
816
 
             */
817
 
            if((ret = checkfile(filename, engine, limits, 0, 0)) == CL_VIRUS) {
818
 
                if(opt_check(opt, "remove")) {
819
 
                    if(unlink(filename)) {
820
 
                        logg("^%s: Can't remove\n", filename);
821
 
                        info.notremoved++;
822
 
                    } else {
823
 
                        logg("~%s: Removed\n", filename);
824
 
                    }
825
 
                } else if (opt_check(opt, "move") || opt_check(opt, "copy"))
826
 
                    move_infected(filename, opt);
827
 
            }
828
 
            return ret;
829
 
        case -3:
830
 
            return 0;
831
 
        case 0:
832
 
            /* no viruses found in archive, we scan just in case a raw file
833
 
             */
834
 
            if((ret = checkfile(filename, engine, limits, 0, 1)) == CL_VIRUS) {
835
 
                if(opt_check(opt, "remove")) {
836
 
                    if(unlink(filename)) {
837
 
                        logg("^%s: Can't remove\n", filename);
838
 
                        info.notremoved++;
839
 
                    } else {
840
 
                        logg("~%s: Removed\n", filename);
841
 
                    }
842
 
                } else if (opt_check(opt, "move") || opt_check(opt, "copy"))
843
 
                    move_infected(filename, opt);
844
 
            }
845
 
            return ret;
846
 
        case 1:
847
 
            logg("~%s: Infected.Archive FOUND\n", filename);
848
 
 
849
 
            if(bell)
850
 
                fprintf(stderr, "\007");
851
 
 
852
 
            if(opt_check(opt, "remove")) {
853
 
                if(unlink(filename)) {
854
 
                    logg("^%s: Can't remove\n", filename);
855
 
                    info.notremoved++;
856
 
                } else {
857
 
                    logg("~%s: Removed\n", filename);
858
 
                }
859
 
            } else if (opt_check(opt, "move") || opt_check(opt, "copy"))
860
 
                move_infected(filename, opt);
861
 
 
862
 
            return 1;
863
 
        default:
864
 
            logg("^Strange value (%d) returned in scancompressed()\n", ret);
865
 
            return 0;
866
 
    }
867
 
}
868
 
 
869
 
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)
870
 
{
871
 
        char *gendir, *tmp_file;
872
 
        const char *tmpdir, *pt;
873
 
        struct stat statbuf;
874
 
        int ret;
875
 
 
876
 
    stat(filename, &statbuf);
877
 
    if(!S_ISREG(statbuf.st_mode)) {
878
 
        logg("^Suspect archive %s (not a regular file)\n", filename);
879
 
        return 0;
880
 
    }
881
 
 
882
 
    /* check write access */
883
 
 
884
 
    tmpdir = getenv("TMPDIR");
885
 
 
886
 
    if(tmpdir == NULL)
887
 
#ifdef P_tmpdir
888
 
        tmpdir = P_tmpdir;
889
 
#else
890
 
        tmpdir = "/tmp";
891
 
#endif
892
 
 
893
 
 
894
 
    if(checkaccess(tmpdir, CLAMAVUSER, W_OK) != 1) {
895
 
        logg("!Can't write to the temporary directory %s\n", tmpdir);
896
 
        exit(64);
897
 
    }
898
 
 
899
 
    /* generate the temporary directory */
900
 
    gendir = cli_gentemp(tmpdir);
901
 
    if(mkdir(gendir, 0700)) {
902
 
        logg("^Can't create the temporary directory %s\n", gendir);
903
 
        exit(63); /* critical */
904
 
    }
905
 
 
906
 
    tmp_file = (char *) malloc(strlen(gendir) + strlen(filename) + 10);
907
 
    pt = strrchr(filename, '/');
908
 
    if(!pt)
909
 
        pt = filename;
910
 
    else
911
 
        pt += 1;
912
 
 
913
 
    sprintf(tmp_file, "%s/%s", gendir, pt);
914
 
 
915
 
    if(filecopy(filename, tmp_file) == -1) {
916
 
        logg("!I/O error\n");
917
 
        perror("copyfile()");
918
 
        exit(58);
919
 
    }
920
 
 
921
 
    fixperms(gendir);
922
 
 
923
 
#if !defined(C_OS2) && !defined(C_WINDOWS)
924
 
    if(user) {
925
 
        chown(gendir, user->pw_uid, user->pw_gid);
926
 
        chown(tmp_file, user->pw_uid, user->pw_gid);
927
 
    }
928
 
#endif
929
 
 
930
 
    if((ret = treewalk(gendir, engine, user, opt, limits, options, 1)) == 1) {
931
 
        logg("(Real infected archive: %s)\n", filename);
932
 
 
933
 
        if(opt_check(opt, "remove")) {
934
 
            if(unlink(filename)) {
935
 
                logg("^%s: Can't remove\n", filename);
936
 
                info.notremoved++;
937
 
            } else {
938
 
                logg("~%s: Removed\n", filename);
939
 
            }
940
 
        } else if (opt_check(opt, "move") || opt_check(opt, "copy"))
941
 
            move_infected(filename, opt);
942
 
    }
943
 
 
944
 
    /* remove the directory  - as clamav */
945
 
    clamav_rmdirs(gendir);
946
 
 
947
 
    free(gendir);
948
 
    free(tmp_file);
949
 
 
950
 
    return ret;
951
 
}
952
 
 
953
 
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)
954
 
{
955
 
        int ret = 0, included, printclean = 1;
956
 
        const struct optnode *optnode;
957
 
        char *argument;
958
 
#ifdef C_LINUX
959
 
        struct stat sb;
960
 
 
961
 
    /* argh, don't scan /proc files */
962
 
    if(procdev)
963
 
        if(stat(filename, &sb) != -1)
964
 
            if(sb.st_dev == procdev) {
965
 
                if(!printinfected)
966
 
                    logg("~%s: Excluded (/proc)\n", filename);
967
 
                return 0;
968
 
            }
969
 
#endif    
970
 
 
971
 
    if(opt_check(opt, "exclude")) {
972
 
        argument = opt_firstarg(opt, "exclude", &optnode);
973
 
        while(argument) {
974
 
            if(match_regex(filename, argument) == 1) {
975
 
                if(!printinfected)
976
 
                    logg("~%s: Excluded\n", filename);
977
 
                return 0;
978
 
            }
979
 
            argument = opt_nextarg(&optnode, "exclude");
980
 
        }
981
 
    }
982
 
 
983
 
   if(opt_check(opt, "include")) {
984
 
        included = 0;
985
 
        argument = opt_firstarg(opt, "include", &optnode);
986
 
        while(argument && !included) {
987
 
            if(match_regex(filename, argument) == 1) {
988
 
                included = 1;
989
 
                break;
990
 
            }
991
 
            argument = opt_nextarg(&optnode, "include");
992
 
        }
993
 
 
994
 
        if(!included) {
995
 
            if(!printinfected)
996
 
                logg("~%s: Excluded\n", filename);
997
 
            return 0;
998
 
        }
999
 
    }
1000
 
 
1001
 
    if(fileinfo(filename, 1) == 0) {
1002
 
        if(!printinfected)
1003
 
            logg("~%s: Empty file\n", filename);
1004
 
        return 0;
1005
 
    }
1006
 
 
1007
 
#ifndef C_WINDOWS
1008
 
    if(geteuid())
1009
 
        if(checkaccess(filename, NULL, R_OK) != 1) {
1010
 
            if(!printinfected)
1011
 
                logg("~%s: Access denied\n", filename);
1012
 
            return 0;
1013
 
        }
1014
 
#endif
1015
 
 
1016
 
    info.files++;
1017
 
 
1018
 
    /* 
1019
 
     * check the extension  - this is a special case, normally we don't need to
1020
 
     * do this (libclamav detects archive by its magic string), but here we
1021
 
     * want to know the exit code from internal unpacker and try to use
1022
 
     * external (if provided) when internal cannot extract data.
1023
 
     */
1024
 
 
1025
 
    if((cli_strbcasestr(filename, ".zip") || cli_strbcasestr(filename, ".rar")) && (options & CL_SCAN_ARCHIVE)) {
1026
 
 
1027
 
#ifndef ENABLE_UNRAR
1028
 
      if(cli_strbcasestr(filename, ".zip"))
1029
 
#endif
1030
 
        /* try to use internal archivers */
1031
 
        if((ret = checkfile(filename, engine, limits, options, 1)) == CL_VIRUS) {
1032
 
            if(opt_check(opt, "remove")) {
1033
 
                if(unlink(filename)) {
1034
 
                    logg("^%s: Can't remove\n", filename);
1035
 
                    info.notremoved++;
1036
 
                } else {
1037
 
                    logg("~%s: Removed\n", filename);
1038
 
                }
1039
 
            } else if (opt_check(opt, "move") || opt_check(opt, "copy"))
1040
 
                move_infected(filename, opt);
1041
 
 
1042
 
            return 1;
1043
 
 
1044
 
        } else if(ret == CL_CLEAN) {
1045
 
            return 0;
1046
 
        } else if(ret == 54) {
1047
 
            return ret;
1048
 
        }
1049
 
 
1050
 
        /* in other case try to continue with external archivers */
1051
 
        options &= ~CL_SCAN_ARCHIVE; /* and disable decompression for the checkfile() below */
1052
 
    }
1053
 
 
1054
 
    if((cli_strbcasestr(filename, ".zip") && opt_check(opt, "unzip"))
1055
 
    || (cli_strbcasestr(filename, ".rar") && opt_check(opt, "unrar"))
1056
 
    || (cli_strbcasestr(filename, ".arj") && opt_check(opt, "arj"))
1057
 
    || (cli_strbcasestr(filename, ".zoo") && opt_check(opt, "unzoo"))
1058
 
    || (cli_strbcasestr(filename, ".jar") && opt_check(opt, "jar"))
1059
 
    || (cli_strbcasestr(filename, ".lzh") && opt_check(opt, "lha"))
1060
 
    || (cli_strbcasestr(filename, ".tar") && opt_check(opt, "tar"))
1061
 
    || (cli_strbcasestr(filename, ".deb") && opt_check(opt, "deb"))
1062
 
    || ((cli_strbcasestr(filename, ".tar.gz") || cli_strbcasestr(filename, ".tgz")) 
1063
 
         && (opt_check(opt, "tgz") || opt_check(opt, "deb"))) ) {
1064
 
 
1065
 
        /* check permissions */
1066
 
        switch(checkaccess(filename, CLAMAVUSER, R_OK)) {
1067
 
            case -1:
1068
 
                logg("^Can't get information about user "CLAMAVUSER"\n");
1069
 
                exit(60); /* this is a critical problem so we just exit here */
1070
 
            case -2:
1071
 
                logg("^Can't fork\n");
1072
 
                exit(61);
1073
 
            case 0: /* read access denied */
1074
 
                if(geteuid()) {
1075
 
                    if(!printinfected)
1076
 
                        logg("^%s: Access denied to archive\n", filename);
1077
 
                } else {
1078
 
 
1079
 
                    if(limits && limits->maxfilesize)
1080
 
                        if((unsigned int) fileinfo(filename, 1) / 1024 > limits->maxfilesize) {
1081
 
                            if(!printinfected)
1082
 
                                logg("^%s: Archive too big\n", filename);
1083
 
                            return 0;
1084
 
                        }
1085
 
 
1086
 
                    return(scandenied(filename, engine, user, opt, limits, options));
1087
 
                }
1088
 
                return 0;
1089
 
            case 1:
1090
 
                return(scancompressed(filename, engine, user, opt, limits, options));
1091
 
        }
1092
 
    }
1093
 
 
1094
 
    if((ret = checkfile(filename, engine, limits, options, printclean)) == CL_VIRUS) {
1095
 
        if(opt_check(opt, "remove")) {
1096
 
            if(unlink(filename)) {
1097
 
                logg("^%s: Can't remove\n", filename);
1098
 
                info.notremoved++;
1099
 
            } else {
1100
 
                logg("~%s: Removed\n", filename);
1101
 
            }
1102
 
        } else if (opt_check(opt, "move") || opt_check(opt, "copy"))
1103
 
            move_infected(filename, opt);
1104
 
    }
1105
 
    return ret;
1106
 
}