b151ef55 |
/* |
2bc31f05 |
* Copyright (C) 2002 - 2006 Tomasz Kojm <tkojm@clamav.net> |
b151ef55 |
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software |
30738099 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA. |
b151ef55 |
*
* Sat May 18 15:23:21 CEST 2002: included cpu autodetection from Magnus Ekdahl
* Wed Mar 5 03:45:31 CET 2003: included --move code from Damien Curtain
*/
|
c7cef0eb |
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
|
b151ef55 |
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h> |
039b84ec |
#include <utime.h> |
b151ef55 |
#include <grp.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <clamav.h>
#include <errno.h>
#include "defaults.h"
#include "others.h"
#include "options.h"
#include "manager.h"
#include "treewalk.h"
#include "shared.h" |
e8217f5a |
#include "str.h" |
36f2038b |
#include "memory.h"
#include "output.h" |
e441c102 |
#include "misc.h" |
f91f55e0 |
#include "../libclamav/others.h" |
9f986368 |
#include "../libclamav/matcher-ac.h" |
b151ef55 |
#ifdef C_LINUX
dev_t procdev;
#endif
|
f91f55e0 |
|
b151ef55 |
int scanmanager(const struct optstruct *opt)
{
mode_t fmode; |
64f4dd45 |
int ret = 0, compression = 0, fmodeint, options = 0, i, x; |
8eec8ae5 |
unsigned int dboptions = 0; |
b151ef55 |
struct cl_node *trie = NULL;
struct cl_limits *limits = NULL;
struct passwd *user = NULL;
struct stat sb; |
89a917da |
char *fullpath = NULL, cwd[1024]; |
e441c102 |
|
b151ef55 |
/* njh@bandsman.co.uk: BeOS */ |
4bed6861 |
#if !defined(C_CYGWIN) && !defined(C_OS2) && !defined(C_BEOS) |
a6c3fdb9 |
if(!geteuid()) { |
b151ef55 |
if((user = getpwnam(UNPUSER)) == NULL) { |
d00076f1 |
logg("!Can't get information about user "UNPUSER"\n"); |
b151ef55 |
exit(60); /* this is critical problem, so we just exit here */
}
}
#endif
|
2bc31f05 |
if(opt_check(opt, "unzip") || opt_check(opt, "unrar") || opt_check(opt, "arj") ||
opt_check(opt, "unzoo") || opt_check(opt, "jar") || opt_check(opt, "lha") ||
opt_check(opt, "tar") || opt_check(opt, "tgz") || opt_check(opt, "deb")) |
b151ef55 |
compression = 1;
|
d119f7a0 |
|
7def75f3 |
if(opt_check(opt, "ncore"))
dboptions |= CL_DB_NCORE; |
d119f7a0 |
|
2bc31f05 |
if(opt_check(opt, "no-phishing")) |
8eec8ae5 |
dboptions |= CL_DB_NOPHISHING; |
b151ef55 |
|
6366e652 |
#ifdef CL_EXPERIMENTAL
if(opt_check(opt,"no-phishing-scan-urls"))
options |= CL_SCAN_NOPHISHING; |
b8917580 |
if(opt_check(opt,"phishing-strict-url-check")) |
6366e652 |
options |= CL_PHISH_NO_DOMAINLIST;
#endif
|
2bc31f05 |
if(opt_check(opt, "dev-ac-only")) { |
9f986368 |
dboptions |= CL_DB_ACONLY;
|
2bc31f05 |
if(opt_check(opt, "dev-ac-depth"))
cli_ac_setdepth(atoi(opt_arg(opt, "dev-ac-depth"))); |
9f986368 |
}
|
2bc31f05 |
if(opt_check(opt, "database")) {
if((ret = cl_load(opt_arg(opt, "database"), &trie, &claminfo.signs, dboptions))) { |
d00076f1 |
logg("!%s\n", cl_strerror(ret)); |
8eec8ae5 |
return 50; |
b151ef55 |
}
} else { |
b2e70235 |
char *dbdir = freshdbdir(); |
cfe76364 |
|
8eec8ae5 |
if((ret = cl_load(dbdir, &trie, &claminfo.signs, dboptions))) { |
d00076f1 |
logg("!%s\n", cl_strerror(ret)); |
b2e70235 |
free(dbdir); |
b151ef55 |
return 50;
} |
b2e70235 |
free(dbdir); |
b151ef55 |
}
if(!trie) { |
d00076f1 |
logg("!Can't initialize the virus database\n"); |
b151ef55 |
return 50;
}
|
f91f55e0 |
if((ret = cl_build(trie)) != 0) { |
d00076f1 |
logg("!Database initialization error: %s\n", cl_strerror(ret));; |
e8217f5a |
return 50;
} |
b151ef55 |
/* set (default) limits */
limits = (struct cl_limits *) calloc(1, sizeof(struct cl_limits));
|
2bc31f05 |
if(opt_check(opt, "max-space")) { |
b151ef55 |
char *cpy, *ptr; |
2bc31f05 |
ptr = opt_arg(opt, "max-space"); |
b151ef55 |
if(tolower(ptr[strlen(ptr) - 1]) == 'm') {
cpy = mcalloc(strlen(ptr), sizeof(char)); |
7cc3891c |
strncpy(cpy, ptr, strlen(ptr) - 1); |
b151ef55 |
limits->maxfilesize = atoi(cpy) * 1024 * 1024;
free(cpy);
} else
limits->maxfilesize = atoi(ptr) * 1024;
} else
limits->maxfilesize = 10485760;
|
2bc31f05 |
if(opt_check(opt, "max-files"))
limits->maxfiles = atoi(opt_arg(opt, "max-files")); |
b151ef55 |
else
limits->maxfiles = 500;
|
2bc31f05 |
if(opt_check(opt, "max-recursion"))
limits->maxreclevel = atoi(opt_arg(opt, "max-recursion")); |
b151ef55 |
else |
6761e1ee |
limits->maxreclevel = 8; |
b151ef55 |
|
2bc31f05 |
if(opt_check(opt, "max-ratio"))
limits->maxratio = atoi(opt_arg(opt, "max-ratio")); |
a19f21b6 |
else |
36004784 |
limits->maxratio = 250; |
b151ef55 |
|
0c4f7c46 |
/* set options */
|
2bc31f05 |
if(opt_check(opt, "disable-archive") || opt_check(opt, "no-archive")) |
06d4e856 |
options &= ~CL_SCAN_ARCHIVE; |
0c4f7c46 |
else |
06d4e856 |
options |= CL_SCAN_ARCHIVE; |
0c4f7c46 |
|
2bc31f05 |
if(opt_check(opt, "detect-broken")) |
ac4e01f9 |
options |= CL_SCAN_BLOCKBROKEN; |
0c4f7c46 |
|
2bc31f05 |
if(opt_check(opt, "block-encrypted")) |
f852d214 |
options |= CL_SCAN_BLOCKENCRYPTED; |
0c4f7c46 |
|
2bc31f05 |
if(opt_check(opt, "block-max")) |
06d4e856 |
options |= CL_SCAN_BLOCKMAX; |
728f8802 |
|
2bc31f05 |
if(opt_check(opt, "no-pe")) |
06d4e856 |
options &= ~CL_SCAN_PE; |
0c4f7c46 |
else |
06d4e856 |
options |= CL_SCAN_PE; |
0c4f7c46 |
|
2bc31f05 |
if(opt_check(opt, "no-ole2")) |
06d4e856 |
options &= ~CL_SCAN_OLE2; |
0c4f7c46 |
else |
06d4e856 |
options |= CL_SCAN_OLE2; |
0c4f7c46 |
|
2bc31f05 |
if(opt_check(opt, "no-html")) |
06d4e856 |
options &= ~CL_SCAN_HTML; |
0c4f7c46 |
else |
06d4e856 |
options |= CL_SCAN_HTML; |
0c4f7c46 |
|
2bc31f05 |
if(opt_check(opt, "no-mail")) { |
06d4e856 |
options &= ~CL_SCAN_MAIL; |
0c4f7c46 |
} else { |
06d4e856 |
options |= CL_SCAN_MAIL; |
0c4f7c46 |
|
2bc31f05 |
if(opt_check(opt, "mail-follow-urls")) |
b4017e32 |
#ifdef WITH_CURL |
06d4e856 |
options |= CL_SCAN_MAILURL; |
b4017e32 |
#else
logg("^Support for URLs downloading with libcurl not compiled in\n");
#endif |
0c4f7c46 |
}
|
2bc31f05 |
if(opt_check(opt, "no-algorithmic")) |
95345f70 |
options &= ~CL_SCAN_ALGO;
else
options |= CL_SCAN_ALGO;
|
b151ef55 |
#ifdef C_LINUX |
2ddb7559 |
procdev = (dev_t) 0; |
4b7a1d76 |
if(stat("/proc", &sb) != -1 && !sb.st_size) |
b151ef55 |
procdev = sb.st_dev;
#endif
/* check filetype */ |
e8217f5a |
if(opt->filename == NULL || strlen(opt->filename) == 0) {
/* we need full path for some reasons (eg. archive handling) */ |
89a917da |
if(!getcwd(cwd, sizeof(cwd))) { |
d00076f1 |
logg("!Can't get absolute pathname of current working directory\n"); |
e8217f5a |
ret = 57;
} else |
0c4f7c46 |
ret = scandirs(cwd, trie, user, opt, limits, options); |
e8217f5a |
} else if(!strcmp(opt->filename, "-")) { /* read data from stdin */ |
0c4f7c46 |
ret = checkstdin(trie, limits, options); |
b151ef55 |
} else { |
e8217f5a |
char *thefilename; |
64f4dd45 |
for (x = 0; (thefilename = cli_strtok(opt->filename, x, "\t")) != NULL; x++) { |
e8217f5a |
if((fmodeint = fileinfo(thefilename, 2)) == -1) { |
f1c4563e |
logg("^Can't access file %s\n", thefilename); |
e8217f5a |
perror(thefilename);
ret = 56;
} else { |
f3bd399f |
int slash = 1;
for(i = strlen(thefilename) - 1; i > 0 && slash; i--) {
if(thefilename[i] == '/')
thefilename[i] = 0;
else
slash = 0;
}
|
e8217f5a |
fmode = (mode_t) fmodeint;
|
f3bd399f |
if(compression && (thefilename[0] != '/' && thefilename[0] != '\\' && thefilename[1] != ':')) { |
e8217f5a |
/* we need to complete the path */ |
89a917da |
if(!getcwd(cwd, sizeof(cwd))) { |
d00076f1 |
logg("!Can't get absolute pathname of current working directory\n"); |
8a8b70d4 |
free(limits); |
e8217f5a |
return 57;
} else {
fullpath = mcalloc(512, sizeof(char)); |
b151ef55 |
#ifdef NO_SNPRINTF |
e8217f5a |
sprintf(fullpath, "%s/%s", cwd, thefilename); |
b151ef55 |
#else |
e8217f5a |
snprintf(fullpath, 512, "%s/%s", cwd, thefilename); |
b151ef55 |
#endif |
f1c4563e |
logg("*Full path: %s\n", fullpath); |
e8217f5a |
}
} else |
f3bd399f |
fullpath = thefilename; |
e8217f5a |
switch(fmode & S_IFMT) {
case S_IFREG: |
0c4f7c46 |
ret = scanfile(fullpath, trie, user, opt, limits, options); |
e8217f5a |
break;
case S_IFDIR: |
0c4f7c46 |
ret = scandirs(fullpath, trie, user, opt, limits, options); |
e8217f5a |
break;
default: |
d00076f1 |
logg("!Not supported file type (%s)\n", thefilename); |
e8217f5a |
ret = 52;
} |
b151ef55 |
|
f3bd399f |
if(compression && (thefilename[0] != '/' && thefilename[0] != '\\' && thefilename[1] != ':')) { |
e8217f5a |
free(fullpath);
fullpath = NULL;
}
}
free(thefilename); |
b151ef55 |
}
}
/* free the trie */ |
f91f55e0 |
cl_free(trie); |
b151ef55 |
free(limits);
/* overwrite return code */
if(claminfo.ifiles)
ret = 1;
else if(ret < 50) /* hopefully no error detected */
ret = 0; /* just make sure it's 0 */
return ret;
}
|
0c4f7c46 |
int scanfile(const char *filename, struct cl_node *root, const struct passwd *user, const struct optstruct *opt, const struct cl_limits *limits, int options) |
b151ef55 |
{ |
18bf7562 |
int ret, included, printclean = 1; |
b151ef55 |
struct optnode *optnode;
char *argument;
#ifdef C_LINUX
struct stat sb;
/* argh, don't scan /proc files */
if(procdev)
if(stat(filename, &sb) != -1)
if(sb.st_dev == procdev) { |
cf0744c5 |
if(!printinfected) |
f1c4563e |
logg("%s: Excluded (/proc)\n", filename); |
b151ef55 |
return 0;
} |
7084e554 |
#endif |
4cc291aa |
|
2bc31f05 |
if(opt_check(opt, "exclude")) {
argument = opt_firstarg(opt, "exclude", &optnode); |
4cc291aa |
while(argument) { |
7084e554 |
if(match_regex(filename, argument) == 1) { |
b151ef55 |
if(!printinfected) |
f1c4563e |
logg("%s: Excluded\n", filename); |
b151ef55 |
return 0;
} |
2bc31f05 |
argument = opt_nextarg(&optnode, "exclude"); |
b151ef55 |
}
} |
4cc291aa |
|
2bc31f05 |
if(opt_check(opt, "include")) { |
b151ef55 |
included = 0; |
2bc31f05 |
argument = opt_firstarg(opt, "include", &optnode); |
4cc291aa |
while(argument && !included) {
if(match_regex(filename, argument) == 1) { |
b151ef55 |
included = 1; |
4cc291aa |
break;
} |
2bc31f05 |
argument = opt_nextarg(&optnode, "include"); |
b151ef55 |
}
|
4cc291aa |
if(!included) { |
b151ef55 |
if(!printinfected) |
f1c4563e |
logg("%s: Excluded\n", filename); |
b151ef55 |
return 0;
}
}
if(fileinfo(filename, 1) == 0) {
if(!printinfected) |
f1c4563e |
logg("%s: Empty file\n", filename); |
b151ef55 |
return 0;
}
|
df9ec24a |
if(geteuid())
if(checkaccess(filename, NULL, R_OK) != 1) {
if(!printinfected) |
f1c4563e |
logg("%s: Access denied\n", filename); |
df9ec24a |
return 0;
}
claminfo.files++;
|
b151ef55 |
/*
* check the extension - this is a special case, normally we don't need to
* do this (libclamav detects archive by its magic string), but here we
* want to know the exit code from internal unpacker and try to use
* external (if provided) when internal cannot extract data.
*/
|
06d4e856 |
if((cli_strbcasestr(filename, ".zip") || cli_strbcasestr(filename, ".rar")) && (options & CL_SCAN_ARCHIVE)) { |
b151ef55 |
/* try to use internal archivers */ |
9f0b3299 |
if((ret = checkfile(filename, root, limits, options, 1)) == CL_VIRUS) { |
2bc31f05 |
if(opt_check(opt, "remove")) { |
b151ef55 |
if(unlink(filename)) { |
d00076f1 |
logg("^%s: Can't remove\n", filename); |
b151ef55 |
claminfo.notremoved++;
} else { |
4cc291aa |
logg("%s: Removed\n", filename); |
b151ef55 |
} |
2bc31f05 |
} else if (opt_check(opt, "move")) |
b151ef55 |
move_infected(filename, opt);
return 1;
|
d8372d3b |
} else if(ret == CL_CLEAN) { |
b151ef55 |
return 0; |
d8372d3b |
} else if(ret == 54) {
return ret;
}
|
b151ef55 |
/* in other case try to continue with external archivers */ |
9f0b3299 |
options &= ~CL_SCAN_ARCHIVE; /* and disable decompression for the checkfile() below */ |
18bf7562 |
printclean = 0; |
b151ef55 |
}
|
2bc31f05 |
if((cli_strbcasestr(filename, ".zip") && opt_check(opt, "unzip"))
|| (cli_strbcasestr(filename, ".rar") && opt_check(opt, "unrar"))
|| (cli_strbcasestr(filename, ".arj") && opt_check(opt, "arj"))
|| (cli_strbcasestr(filename, ".zoo") && opt_check(opt, "unzoo"))
|| (cli_strbcasestr(filename, ".jar") && opt_check(opt, "jar"))
|| (cli_strbcasestr(filename, ".lzh") && opt_check(opt, "lha"))
|| (cli_strbcasestr(filename, ".tar") && opt_check(opt, "tar"))
|| (cli_strbcasestr(filename, ".deb") && opt_check(opt, "deb")) |
36f2038b |
|| ((cli_strbcasestr(filename, ".tar.gz") || cli_strbcasestr(filename, ".tgz")) |
2bc31f05 |
&& (opt_check(opt, "tgz") || opt_check(opt, "deb"))) ) { |
b151ef55 |
/* check permissions */ |
941f3ab8 |
switch(checkaccess(filename, UNPUSER, R_OK)) { |
b151ef55 |
case -1: |
f1c4563e |
logg("^Can't get information about user "UNPUSER"\n"); |
941f3ab8 |
exit(60); /* this is a critical problem so we just exit here */ |
b151ef55 |
case -2: |
f1c4563e |
logg("^Can't fork\n"); |
941f3ab8 |
exit(61); |
b151ef55 |
case 0: /* read access denied */ |
a6c3fdb9 |
if(geteuid()) { |
b151ef55 |
if(!printinfected) |
d00076f1 |
logg("^%s: Access denied to archive\n", filename); |
b151ef55 |
} else {
if(limits && limits->maxfilesize)
if(fileinfo(filename, 1) / 1024 > limits->maxfilesize) {
if(!printinfected) |
d00076f1 |
logg("^%s: Archive too big\n", filename); |
b151ef55 |
return 0;
}
|
0c4f7c46 |
return(scandenied(filename, root, user, opt, limits, options)); |
b151ef55 |
}
return 0;
case 1: |
0c4f7c46 |
return(scancompressed(filename, root, user, opt, limits, options)); |
b151ef55 |
}
}
|
18bf7562 |
if((ret = checkfile(filename, root, limits, options, printclean)) == CL_VIRUS) { |
2bc31f05 |
if(opt_check(opt, "remove")) { |
b151ef55 |
if(unlink(filename)) { |
d00076f1 |
logg("^%s: Can't remove\n", filename); |
b151ef55 |
claminfo.notremoved++;
} else { |
4cc291aa |
logg("%s: Removed\n", filename); |
b151ef55 |
} |
2bc31f05 |
} else if (opt_check(opt, "move")) |
b151ef55 |
move_infected(filename, opt);
}
return ret;
}
/* it has guaranteed read access to the archive */ |
0c4f7c46 |
int scancompressed(const char *filename, struct cl_node *root, const struct passwd *user, const struct optstruct *opt, const struct cl_limits *limits, int options) |
b151ef55 |
{
int ret = 0;
char *tmpdir, *gendir, *userprg;
struct stat statbuf;
|
3fa35dc1 |
|
b151ef55 |
stat(filename, &statbuf);
if(!S_ISREG(statbuf.st_mode)) { |
d00076f1 |
logg("^Suspect archive %s (not a regular file)\n", filename); |
b151ef55 |
return 0; /* hmm ? */
}
/* check write access */
|
3506c157 |
tmpdir = getenv("TMPDIR"); |
b151ef55 |
if(tmpdir == NULL)
#ifdef P_tmpdir
tmpdir = P_tmpdir;
#else
tmpdir = "/tmp";
#endif
|
941f3ab8 |
if(checkaccess(tmpdir, UNPUSER, W_OK) != 1) { |
d00076f1 |
logg("!Can't write to the temporary directory\n"); |
b151ef55 |
exit(64);
}
/* generate the temporary directory */
|
f91f55e0 |
gendir = cli_gentemp(tmpdir); |
b151ef55 |
if(mkdir(gendir, 0700)) { |
d00076f1 |
logg("!Can't create the temporary directory %s\n", gendir); |
b151ef55 |
exit(63); /* critical */
}
|
193c72c5 |
#ifndef C_OS2 |
b151ef55 |
if(user)
chown(gendir, user->pw_uid, user->pw_gid); |
193c72c5 |
#endif |
b151ef55 |
/* unpack file - as unprivileged user */ |
36f2038b |
if(cli_strbcasestr(filename, ".zip")) { |
2457e0a8 |
char *args[] = { "unzip", "-P", "clam", "-o", NULL, NULL };
/* Sun's SUNWspro C compiler doesn't allow direct initialisation
* with a variable
*/
args[4] = (char *) filename; |
b151ef55 |
|
2bc31f05 |
if((userprg = opt_arg(opt, "unzip"))) |
b151ef55 |
ret = clamav_unpack(userprg, args, gendir, user, opt);
else
ret = clamav_unpack("unzip", args, gendir, user, opt);
|
36f2038b |
} else if(cli_strbcasestr(filename, ".rar")) { |
2457e0a8 |
char *args[] = { "unrar", "x", "-p-", "-y", NULL, NULL };
args[4] = (char *) filename; |
2bc31f05 |
if((userprg = opt_arg(opt, "unrar"))) |
b151ef55 |
ret = clamav_unpack(userprg, args, gendir, user, opt);
else
ret = clamav_unpack("unrar", args, gendir, user, opt);
|
36f2038b |
} else if(cli_strbcasestr(filename, ".arj")) { |
2457e0a8 |
char *args[] = { "arj", "x","-y", NULL, NULL };
args[3] = (char *) filename; |
2bc31f05 |
if((userprg = opt_arg(opt, "arj"))) |
b151ef55 |
ret = clamav_unpack(userprg, args, gendir, user, opt);
else
ret = clamav_unpack("arj", args, gendir, user, opt);
|
36f2038b |
} else if(cli_strbcasestr(filename, ".zoo")) { |
2457e0a8 |
char *args[] = { "unzoo", "-x","-j","./", NULL, NULL };
args[4] = (char *) filename; |
2bc31f05 |
if((userprg = opt_arg(opt, "unzoo"))) |
b151ef55 |
ret = clamav_unpack(userprg, args, gendir, user, opt);
else
ret = clamav_unpack("unzoo", args, gendir, user, opt);
|
36f2038b |
} else if(cli_strbcasestr(filename, ".jar")) { |
2457e0a8 |
char *args[] = { "unzip", "-P", "clam", "-o", NULL, NULL };
args[4] = (char *) filename; |
2bc31f05 |
if((userprg = opt_arg(opt, "jar"))) |
b151ef55 |
ret = clamav_unpack(userprg, args, gendir, user, opt);
else
ret = clamav_unpack("unzip", args, gendir, user, opt);
|
36f2038b |
} else if(cli_strbcasestr(filename, ".lzh")) { |
2457e0a8 |
char *args[] = { "lha", "xf", NULL, NULL };
args[2] = (char *) filename; |
2bc31f05 |
if((userprg = opt_arg(opt, "lha"))) |
b151ef55 |
ret = clamav_unpack(userprg, args, gendir, user, opt);
else
ret = clamav_unpack("lha", args, gendir, user, opt);
|
36f2038b |
} else if(cli_strbcasestr(filename, ".tar")) { |
2457e0a8 |
char *args[] = { "tar", "-xpvf", NULL, NULL };
args[2] = (char *) filename; |
2bc31f05 |
if((userprg = opt_arg(opt, "tar"))) |
b151ef55 |
ret = clamav_unpack(userprg, args, gendir, user, opt);
else
ret = clamav_unpack("tar", args, gendir, user, opt);
|
36f2038b |
} else if(cli_strbcasestr(filename, ".deb")) { |
2457e0a8 |
char *args[] = { "ar", "x", NULL, NULL };
args[2] = (char *) filename; |
2bc31f05 |
if((userprg = opt_arg(opt, "deb"))) |
b151ef55 |
ret = clamav_unpack(userprg, args, gendir, user, opt);
else
ret = clamav_unpack("ar", args, gendir, user, opt);
|
36f2038b |
} else if((cli_strbcasestr(filename, ".tar.gz") || cli_strbcasestr(filename, ".tgz"))) { |
2457e0a8 |
char *args[] = { "tar", "-zxpvf", NULL, NULL };
args[2] = (char *) filename; |
2bc31f05 |
if((userprg = opt_arg(opt, "tgz"))) |
b151ef55 |
ret = clamav_unpack(userprg, args, gendir, user, opt);
else
ret = clamav_unpack("tar", args, gendir, user, opt);
}
/* fix permissions of extracted files */
fixperms(gendir);
|
3fa35dc1 |
if(!ret) { /* execute successful */
short oldrec = recursion;
recursion = 1;
ret = treewalk(gendir, root, user, opt, limits, options, 1);
recursion = oldrec;
} |
b151ef55 |
/* remove the directory - as clamav */ |
2bc31f05 |
if(!opt_check(opt, "leave-temps")) |
f39fb336 |
clamav_rmdirs(gendir); |
b151ef55 |
/* free gendir - it's not necessary now */
free(gendir);
switch(ret) {
case -1: |
d00076f1 |
logg("!Can't fork()\n"); |
b151ef55 |
exit(61); /* this is critical problem, so we just exit here */
case -2: |
f1c4563e |
logg("^Can't execute some unpacker. Check paths and permissions on the temporary directory\n"); |
b151ef55 |
/* This is no longer a critical error (since 0.24). We scan
* raw archive.
*/ |
9f0b3299 |
if((ret = checkfile(filename, root, limits, 0, 0)) == CL_VIRUS) { |
2bc31f05 |
if(opt_check(opt, "remove")) { |
b151ef55 |
if(unlink(filename)) { |
d00076f1 |
logg("^%s: Can't remove\n", filename); |
b151ef55 |
claminfo.notremoved++;
} else { |
4cc291aa |
logg("%s: Removed\n", filename); |
b151ef55 |
} |
2bc31f05 |
} else if (opt_check(opt, "move")) |
b151ef55 |
move_infected(filename, opt);
}
return ret;
case -3:
return 0;
case 0: |
3fa35dc1 |
/* no viruses found in archive, we scan just in case a raw file |
b151ef55 |
*/ |
9f0b3299 |
if((ret = checkfile(filename, root, limits, 0, 1)) == CL_VIRUS) { |
2bc31f05 |
if(opt_check(opt, "remove")) { |
b151ef55 |
if(unlink(filename)) { |
d00076f1 |
logg("^%s: Can't remove\n", filename); |
b151ef55 |
claminfo.notremoved++;
} else { |
4cc291aa |
logg("%s: Removed\n", filename); |
b151ef55 |
} |
2bc31f05 |
} else if (opt_check(opt, "move")) |
b151ef55 |
move_infected(filename, opt);
}
return ret;
case 1: |
3fa35dc1 |
logg("%s: Infected.Archive FOUND\n", filename); |
026ebd88 |
if(bell) |
cc938d61 |
fprintf(stderr, "\007"); |
026ebd88 |
|
2bc31f05 |
if(opt_check(opt, "remove")) { |
b151ef55 |
if(unlink(filename)) { |
d00076f1 |
logg("^%s: Can't remove\n", filename); |
b151ef55 |
claminfo.notremoved++;
} else { |
4cc291aa |
logg("%s: Removed\n", filename); |
b151ef55 |
} |
2bc31f05 |
} else if (opt_check(opt, "move")) |
b151ef55 |
move_infected(filename, opt);
return 1;
default: |
f1c4563e |
logg("^Strange value (%d) returned in scancompressed()\n", ret); |
b151ef55 |
return 0;
}
}
|
0c4f7c46 |
int scandenied(const char *filename, struct cl_node *root, const struct passwd *user, const struct optstruct *opt, const struct cl_limits *limits, int options) |
b151ef55 |
{
char *tmpdir, *gendir, *tmpfile, *pt;
struct stat statbuf;
int ret;
stat(filename, &statbuf);
if(!S_ISREG(statbuf.st_mode)) { |
d00076f1 |
logg("^Suspect archive %s (not a regular file)\n", filename); |
b151ef55 |
return 0;
}
/* check write access */
|
3506c157 |
tmpdir = getenv("TMPDIR"); |
b151ef55 |
if(tmpdir == NULL)
#ifdef P_tmpdir
tmpdir = P_tmpdir;
#else
tmpdir = "/tmp";
#endif
|
941f3ab8 |
if(checkaccess(tmpdir, UNPUSER, W_OK) != 1) { |
d00076f1 |
logg("!Can't write to the temporary directory %s\n", tmpdir); |
b151ef55 |
exit(64);
}
/* generate the temporary directory */ |
f91f55e0 |
gendir = cli_gentemp(tmpdir); |
b151ef55 |
if(mkdir(gendir, 0700)) { |
f1c4563e |
logg("^Can't create the temporary directory %s\n", gendir); |
b151ef55 |
exit(63); /* critical */
}
tmpfile = (char *) mcalloc(strlen(gendir) + strlen(filename) + 10, sizeof(char));
pt = strrchr(filename, '/');
if(!pt)
pt = (char *) filename;
else
pt += 1;
sprintf(tmpfile, "%s/%s", gendir, pt);
if(filecopy(filename, tmpfile) == -1) { |
f1c4563e |
logg("!I/O error\n"); |
b151ef55 |
perror("copyfile()");
exit(58);
}
fixperms(gendir);
|
193c72c5 |
#ifndef C_OS2 |
b151ef55 |
if(user) {
chown(gendir, user->pw_uid, user->pw_gid);
chown(tmpfile, user->pw_uid, user->pw_gid);
} |
193c72c5 |
#endif |
b151ef55 |
|
3fa35dc1 |
if((ret = treewalk(gendir, root, user, opt, limits, options, 1)) == 1) { |
b151ef55 |
logg("(Real infected archive: %s)\n", filename);
|
2bc31f05 |
if(opt_check(opt, "remove")) { |
b151ef55 |
if(unlink(filename)) { |
d00076f1 |
logg("^%s: Can't remove\n", filename); |
b151ef55 |
claminfo.notremoved++;
} else { |
4cc291aa |
logg("%s: Removed\n", filename); |
b151ef55 |
} |
2bc31f05 |
} else if (opt_check(opt, "move")) |
b151ef55 |
move_infected(filename, opt);
}
/* remove the directory - as clamav */
clamav_rmdirs(gendir);
free(gendir);
free(tmpfile);
return ret;
}
|
0c4f7c46 |
int scandirs(const char *dirname, struct cl_node *root, const struct passwd *user, const struct optstruct *opt, const struct cl_limits *limits, int options) |
b151ef55 |
{ |
3fa35dc1 |
return treewalk(dirname, root, user, opt, limits, options, 1); |
b151ef55 |
}
|
9f0b3299 |
int checkfile(const char *filename, const struct cl_node *root, const struct cl_limits *limits, int options, short printclean) |
b151ef55 |
{
int fd, ret; |
5aad82e2 |
const char *virname; |
b151ef55 |
|
c2484690 |
|
f1c4563e |
logg("*Scanning %s\n", filename); |
c2484690 |
|
b151ef55 |
if((fd = open(filename, O_RDONLY)) == -1) { |
f1c4563e |
logg("^Can't open file %s\n", filename); |
b151ef55 |
return 54;
}
if((ret = cl_scandesc(fd, &virname, &claminfo.blocks, root, limits, options)) == CL_VIRUS) {
logg("%s: %s FOUND\n", filename, virname);
claminfo.ifiles++; |
026ebd88 |
if(bell) |
cc938d61 |
fprintf(stderr, "\007"); |
026ebd88 |
|
b151ef55 |
} else if(ret == CL_CLEAN) { |
9f0b3299 |
if(!printinfected && printclean) |
18bf7562 |
mprintf("%s: OK\n", filename); |
b151ef55 |
} else
if(!printinfected) |
f1c4563e |
logg("%s: %s\n", filename, cl_strerror(ret)); |
b151ef55 |
close(fd);
return ret;
}
|
0c4f7c46 |
int checkstdin(const struct cl_node *root, const struct cl_limits *limits, int options) |
b151ef55 |
{
int ret; |
0c4f7c46 |
const char *virname, *tmpdir;
char *file, buff[FILEBUFF];
FILE *fs;
/* check write access */
tmpdir = getenv("TMPDIR");
if(tmpdir == NULL)
#ifdef P_tmpdir
tmpdir = P_tmpdir;
#else
tmpdir = "/tmp";
#endif
if(checkaccess(tmpdir, UNPUSER, W_OK) != 1) { |
d00076f1 |
logg("!Can't write to temporary directory\n"); |
0c4f7c46 |
return 64;
}
file = cli_gentemp(tmpdir);
if(!(fs = fopen(file, "wb"))) { |
d00076f1 |
logg("!Can't open %s for writing\n", file); |
0c4f7c46 |
return 63;
}
while((ret = fread(buff, 1, FILEBUFF, stdin)))
fwrite(buff, 1, ret, fs); |
b151ef55 |
|
0c4f7c46 |
fclose(fs); |
b151ef55 |
|
f1c4563e |
logg("*Checking %s\n", file); |
b151ef55 |
claminfo.files++;
|
0c4f7c46 |
if((ret = cl_scanfile(file, &virname, &claminfo.blocks, root, limits, options)) == CL_VIRUS) { |
f1c4563e |
logg("stdin: %s FOUND\n", virname); |
b151ef55 |
claminfo.ifiles++; |
026ebd88 |
if(bell) |
cc938d61 |
fprintf(stderr, "\007"); |
026ebd88 |
|
b151ef55 |
} else if(ret == CL_CLEAN) {
if(!printinfected) |
18bf7562 |
mprintf("stdin: OK\n"); |
b151ef55 |
} else
if(!printinfected) |
f1c4563e |
logg("stdin: %s\n", cl_strerror(ret)); |
b151ef55 |
|
0c4f7c46 |
unlink(file);
free(file); |
b151ef55 |
return ret;
}
/*
* -1 -> can't fork
* -2 -> can't execute
* -3 -> external signal
* 0 -> OK
*/
int clamav_unpack(const char *prog, char **args, const char *tmpdir, const struct passwd *user, const struct optstruct *opt)
{
pid_t pid;
int status, wret, maxfiles, maxspace, fdevnull;
struct s_du n;
|
2bc31f05 |
if(opt_check(opt, "max-files"))
maxfiles = atoi(opt_arg(opt, "max-files")); |
b151ef55 |
else
maxfiles = 0;
|
2bc31f05 |
if(opt_check(opt, "max-space")) { |
b151ef55 |
char *cpy, *ptr; |
2bc31f05 |
ptr = opt_arg(opt, "max-space"); |
b151ef55 |
if(tolower(ptr[strlen(ptr) - 1]) == 'm') { /* megabytes */
cpy = mcalloc(strlen(ptr), sizeof(char)); |
7cc3891c |
strncpy(cpy, ptr, strlen(ptr) - 1); |
b151ef55 |
maxspace = atoi(cpy) * 1024;
free(cpy);
} else /* default - kilobytes */
maxspace = atoi(ptr);
} else
maxspace = 0;
switch(pid = fork()) {
case -1:
return -1;
case 0:
#ifndef C_CYGWIN |
a6c3fdb9 |
if(!geteuid() && user) { |
819bbe1f |
#ifdef HAVE_SETGROUPS |
eeb69538 |
if(setgroups(1, &user->pw_gid)) { |
4cc291aa |
fprintf(stderr, "ERROR: setgroups() failed\n"); |
eeb69538 |
exit(1);
} |
819bbe1f |
#endif |
eeb69538 |
if(setgid(user->pw_gid)) { |
4cc291aa |
fprintf(stderr, "ERROR: setgid(%d) failed\n", (int) user->pw_gid); |
eeb69538 |
exit(1);
}
if(setuid(user->pw_uid)) { |
4cc291aa |
fprintf(stderr, "ERROR: setuid(%d) failed\n", (int) user->pw_uid); |
eeb69538 |
exit(1);
} |
b151ef55 |
}
#endif
chdir(tmpdir);
if(printinfected) {
fdevnull = open("/dev/null", O_WRONLY);
if(fdevnull == -1) { |
f1c4563e |
logg("Non fatal error: cannot open /dev/null. Continuing with full output\n"); |
b151ef55 |
printinfected = 0;
} else {
dup2(fdevnull,1);
dup2(fdevnull,2);
}
}
if(strchr(prog, '/')) /* we have full path */
execv(prog, args);
else
execvp(prog, args);
perror("execv(p)");
abort();
break;
default:
if(maxfiles || maxspace) {
while(!(wret = waitpid(pid, &status, WNOHANG))) {
memset(&n, 0, sizeof(struct s_du));
if(!du(tmpdir, &n))
if((maxfiles && n.files > maxfiles) || (maxspace && n.space > maxspace)) { |
f1c4563e |
logg("*n.files: %d, n.space: %d\n", n.files, n.space); |
b151ef55 |
kill(pid, 9); /* stop it immediately */
}
}
} else
waitpid(pid, &status, 0);
if(WIFSIGNALED(status)) {
switch(WTERMSIG(status)) {
case 9: |
f1c4563e |
logg("\nUnpacker process %d stopped due to exceeded limits\n", pid); |
b151ef55 |
return 0;
case 6: /* abort */ |
f1c4563e |
logg("^Can't run %s\n", prog); |
b151ef55 |
return -2;
default: |
f1c4563e |
logg("^\nUnpacker stopped with external signal %d\n", WTERMSIG(status)); |
b151ef55 |
return -3;
}
} else if(WIFEXITED(status))
return 0;
}
return 0;
}
void move_infected(const char *filename, const struct optstruct *opt)
{ |
039b84ec |
char *movedir, *movefilename, *tmp, numext[4 + 1];
struct stat fstat, mfstat;
int n, len, movefilename_size;
struct utimbuf ubuf; |
b151ef55 |
|
7084e554 |
|
2bc31f05 |
if(!(movedir = opt_arg(opt, "move"))) { |
b151ef55 |
/* Should never reach here */ |
2bc31f05 |
logg("!opt_arg() returned NULL\n", filename); |
b151ef55 |
claminfo.notmoved++;
return;
}
|
7084e554 |
if(access(movedir, W_OK|X_OK) == -1) { |
2bc31f05 |
logg("!Can't move file '%s': cannot write to '%s': %s\n", filename, movedir, strerror(errno)); |
b151ef55 |
claminfo.notmoved++;
return;
} |
7084e554 |
|
039b84ec |
if(!(tmp = strrchr(filename, '/')))
tmp = (char *) filename; |
7084e554 |
|
039b84ec |
movefilename_size = sizeof(char) * (strlen(movedir) + strlen(tmp) + sizeof(numext) + 2); |
7084e554 |
|
039b84ec |
if(!(movefilename = mmalloc(movefilename_size))) { |
2bc31f05 |
logg("!mmalloc() failed\n"); |
039b84ec |
exit(71); |
b151ef55 |
} |
7084e554 |
|
f2b52716 |
if(!(cli_strrcpy(movefilename, movedir))) {
logg("!cli_strrcpy() returned NULL\n"); |
b151ef55 |
claminfo.notmoved++;
free(movefilename);
return;
} |
7084e554 |
|
039b84ec |
strcat(movefilename, "/");
|
7084e554 |
if(!(strcat(movefilename, tmp))) { |
2bc31f05 |
logg("!strcat() returned NULL\n"); |
b151ef55 |
claminfo.notmoved++;
free(movefilename);
return;
} |
7084e554 |
|
b151ef55 |
stat(filename, &fstat);
|
7084e554 |
if(!stat(movefilename, &mfstat)) { |
b151ef55 |
if(fstat.st_ino == mfstat.st_ino) { /* It's the same file*/ |
039b84ec |
logg("File excluded '%s'\n", filename); |
b151ef55 |
claminfo.notmoved++;
free(movefilename);
return; |
7084e554 |
} else {
/* file exists - try to append an ordinal number to the
* quranatined file in an attempt not to overwrite existing
* files in quarantine
*/
len = strlen(movefilename);
n = 0;
do {
/* reset the movefilename to it's initial value by
* truncating to the original filename length
*/
movefilename[len] = 0;
/* append .XXX */
sprintf(numext, ".%03d", n++);
strcat(movefilename, numext);
} while(!stat(movefilename, &mfstat) && (n < 1000));
} |
b151ef55 |
} |
7084e554 |
|
039b84ec |
if(rename(filename, movefilename) == -1) {
if(filecopy(filename, movefilename) == -1) { |
2bc31f05 |
logg("!Can't move '%s' to '%s': %s\n", filename, movefilename, strerror(errno)); |
039b84ec |
claminfo.notmoved++;
free(movefilename);
return;
} |
b151ef55 |
|
039b84ec |
chmod(movefilename, fstat.st_mode); |
193c72c5 |
#ifndef C_OS2 |
039b84ec |
chown(movefilename, fstat.st_uid, fstat.st_gid); |
193c72c5 |
#endif |
7084e554 |
|
039b84ec |
ubuf.actime = fstat.st_atime;
ubuf.modtime = fstat.st_mtime;
utime(movefilename, &ubuf);
if(unlink(filename)) { |
2bc31f05 |
logg("!Can't unlink '%s': %s\n", filename, strerror(errno)); |
039b84ec |
claminfo.notremoved++;
free(movefilename);
return;
} |
b151ef55 |
}
|
039b84ec |
logg("%s: moved to '%s'\n", filename, movefilename);
|
b151ef55 |
free(movefilename);
} |