Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions clamd/clamd.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ static void help(void)
printf(" --debug Enable debug mode\n");
printf(" --log=FILE -l FILE Log into FILE\n");
printf(" --config-file=FILE -c FILE Read configuration from FILE\n");
printf(" --fail-if-cvd-older-than=days Return with a nonzero error code if virus database outdated.\n");
printf("\n");
printf("Pass in - as the filename for stdin.\n");
printf("\n");
Expand Down Expand Up @@ -651,6 +652,12 @@ int main(int argc, char **argv)
svc_register("clamd");
}
#endif
if (optget(opts, "fail-if-cvd-older-than")->enabled) {
if (check_if_cvd_outdated(dbdir, optget(opts, "fail-if-cvd-older-than")->numarg) != CL_SUCCESS) {
ret = 1;
break;
}
}

if ((ret = cl_load(dbdir, engine, &sigs, dboptions))) {
logg(LOGG_ERROR, "%s\n", cl_strerror(ret));
Expand Down
1 change: 1 addition & 0 deletions clamscan/clamscan.c
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ void help(void)
mprintf(LOGG_INFO, " A JSON file will dropped to the temp directory if --leave-temps is enabled.\n");
mprintf(LOGG_INFO, " --database=FILE/DIR -d FILE/DIR Load virus database from FILE or load all supported db files from DIR\n");
mprintf(LOGG_INFO, " --official-db-only[=yes/no(*)] Only load official signatures\n");
mprintf(LOGG_INFO, " --fail-if-cvd-older-than=days Return with a nonzero error code if virus database outdated.\n");
mprintf(LOGG_INFO, " --log=FILE -l FILE Save scan report to FILE\n");
mprintf(LOGG_INFO, " --recursive[=yes/no(*)] -r Scan subdirectories recursively\n");
mprintf(LOGG_INFO, " --allmatch[=yes/no(*)] -z Continue scanning within file after finding a match\n");
Expand Down
14 changes: 14 additions & 0 deletions clamscan/manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,13 @@ int scanmanager(const struct optstruct *opts)

if ((opt = optget(opts, "database"))->active) {
while (opt) {
if (optget(opts, "fail-if-cvd-older-than")->enabled) {
if (check_if_cvd_outdated(opt->strarg, optget(opts, "fail-if-cvd-older-than")->numarg) != CL_SUCCESS) {
ret = 2;
goto done;
}
}

if ((ret = cl_load(opt->strarg, engine, &info.sigs, dboptions))) {
logg(LOGG_ERROR, "%s\n", cl_strerror(ret));

Expand All @@ -1270,6 +1277,13 @@ int scanmanager(const struct optstruct *opts)
} else {
char *dbdir = freshdbdir();

if (optget(opts, "fail-if-cvd-older-than")->enabled) {
if (check_if_cvd_outdated(dbdir, optget(opts, "fail-if-cvd-older-than")->numarg) != CL_SUCCESS) {
ret = 2;
goto done;
}
}

if ((ret = cl_load(dbdir, engine, &info.sigs, dboptions))) {
logg(LOGG_ERROR, "%s\n", cl_strerror(ret));

Expand Down
19 changes: 18 additions & 1 deletion common/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
#include <errno.h>

// libclamav
#include "clamav.h"
#include "cvd.h"
#include "others.h" /* for cli_rmdirs() */
#include "regex/regex.h"
Expand Down Expand Up @@ -487,3 +486,21 @@ unsigned int countlines(const char *filename)
fclose(fh);
return lines;
}

cl_error_t check_if_cvd_outdated(const char *path, long long days)
{
cl_error_t status;
time_t cvd_age;

if ((status = cl_cvdgetage(path, &cvd_age)) != CL_SUCCESS) {
logg(LOGG_ERROR, "%s\n", cl_strerror(status));
return status;
}

if (days * 86400 < cvd_age) {
logg(LOGG_ERROR, "Virus database is older than %lld days!\n", days);
return CL_ECVD;
}

return CL_SUCCESS;
}
4 changes: 4 additions & 0 deletions common/misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#endif
#include <stdbool.h>

#include "clamav.h"
#include "platform.h"
#include "optparser.h"
/* Maximum filenames under various systems - njh */
Expand Down Expand Up @@ -105,4 +106,7 @@ int match_regex(const char *filename, const char *pattern);
int cli_is_abspath(const char *path);
unsigned int countlines(const char *filename);

/* Checks if a virus database file or directory is older than 'days'. */
cl_error_t check_if_cvd_outdated(const char *path, long long days);

#endif
2 changes: 2 additions & 0 deletions common/optparser.c
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ const struct clam_option __clam_options[] = {

{"OfficialDatabaseOnly", "official-db-only", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "Only load the official signatures published by the ClamAV project.", "no"},

{"FailIfCvdOlderThan", "fail-if-cvd-older-than", 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, -1, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "Return with a nonzero error code if the virus database is older than the specified number of days.", "-1"},

{"YaraRules", "yara-rules", 0, CLOPT_TYPE_STRING, NULL, 0, NULL, 0, OPT_CLAMSCAN, "By default, yara rules will be loaded. This option allows you to exclude yara rules when scanning and also to scan only using yara rules. Valid options are yes|no|only", "yes"},

{"LocalSocket", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_CLAMD, "Path to a local socket file the daemon will listen on.", "/tmp/clamd.socket"},
Expand Down
3 changes: 3 additions & 0 deletions docs/man/clamd.8.in
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ Enable debug mode.
.TP
\fB\-c FILE, \-\-config\-file=FILE\fR
Read configuration from FILE.
.TP
\fB\-\-fail\-if\-cvd\-older\-than=days\fR
Return with a nonzero error code if the virus database is older than the specified number of days.

.SH "ENVIRONMENT VARIABLES"
.LP
Expand Down
5 changes: 5 additions & 0 deletions docs/man/clamd.conf.5.in
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ Only load the official signatures published by the ClamAV project.
.br
Default: no
.TP
\fBFailIfCvdOlderThan NUMBER\fR
Return with a nonzero error code if the virus database is older than the specified number of days.
.br
Default: -1
.TP
\fBLocalSocket STRING\fR
Path to a local (Unix) socket the daemon will listen on.
.br
Expand Down
3 changes: 3 additions & 0 deletions docs/man/clamscan.1.in
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ Load virus database from FILE or load all virus database files from DIR.
\fB\-\-official\-db\-only=[yes/no(*)]\fR
Only load the official signatures published by the ClamAV project.
.TP
\fB\-\-fail\-if\-cvd\-older\-than=days\fR
Return with a nonzero error code if the virus database is older than the specified number of days.
.TP
\fB\-l FILE, \-\-log=FILE\fR
Save scan report to FILE.
.TP
Expand Down
5 changes: 5 additions & 0 deletions etc/clamd.conf.sample
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ Example
# Default: no
#OfficialDatabaseOnly no

# Return with a nonzero error code if the virus database is older than
# the specified number of days.
# Default: -1
#FailIfCvdOlderThan 7

# The daemon can work in local mode, network mode or both.
# Due to security reasons we recommend the local mode.

Expand Down
12 changes: 12 additions & 0 deletions libclamav/clamav.h
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,18 @@ extern void cl_cvdfree(struct cl_cvd *cvd);
*/
extern cl_error_t cl_cvdunpack(const char *file, const char *dir, bool dont_verify);

/**
* @brief Retrieve the age of CVD disk data.
*
* Will retrieve the age of the youngest file in a database directory,
* or the age of a single CVD (or CLD) file.
*
* @param path Filepath of CVD directory or file.
* @param age_seconds Age of the directory or file.
* @return cl_error_t CL_SUCCESS if success, else a CL_E* error code.
*/
extern cl_error_t cl_cvdgetage(const char *path, time_t *age_seconds);

/* ----------------------------------------------------------------------------
* DB directory stat functions.
* Use these functions to watch for database changes.
Expand Down
104 changes: 104 additions & 0 deletions libclamav/cvd.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
Expand Down Expand Up @@ -774,3 +775,106 @@ cl_error_t cl_cvdunpack(const char *file, const char *dir, bool dont_verify)

return status;
}

static cl_error_t cvdgetfileage(const char *path, time_t *age_seconds)
{
struct cl_cvd cvd;
time_t s_time;
cl_error_t status = CL_SUCCESS;
FILE *fs = NULL;

if ((fs = fopen(path, "rb")) == NULL) {
cli_errmsg("cvdgetfileage: Can't open file %s\n", path);
return CL_EOPEN;
}

if ((status = cli_cvdverify(fs, &cvd, 1)) != CL_SUCCESS)
goto done;

time(&s_time);

if (cvd.stime > s_time)
*age_seconds = 0;
else
*age_seconds = s_time - cvd.stime;

done:
if (fs)
fclose(fs);

return status;
}

cl_error_t cl_cvdgetage(const char *path, time_t *age_seconds)
{
STATBUF statbuf;
struct dirent *dent;
size_t path_len;
bool ends_with_sep = false;
DIR *dd = NULL;
bool first_age_set = true;
cl_error_t status = CL_SUCCESS;

if (CLAMSTAT(path, &statbuf) == -1) {
cli_errmsg("cl_cvdgetage: Can't get status of: %s\n", path);
status = CL_ESTAT;
goto done;
}

if (!S_ISDIR(statbuf.st_mode)) {
status = cvdgetfileage(path, age_seconds);
goto done;
}

if ((dd = opendir(path)) == NULL) {
cli_errmsg("cl_cvdgetage: Can't open directory %s\n", path);
status = CL_EOPEN;
goto done;
}

path_len = strlen(path);

if (path_len >= strlen(PATHSEP)) {
if (strcmp(path + path_len - strlen(PATHSEP), PATHSEP) == 0) {
cli_dbgmsg("cl_cvdgetage: path ends with separator\n");
ends_with_sep = true;
}
}

while ((dent = readdir(dd))) {
char fname[1024] = {0};
time_t file_age;

if (!dent->d_ino)
continue;

if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
continue;

if (!CLI_DBEXT(dent->d_name))
continue;

if (ends_with_sep)
snprintf(fname, sizeof(fname) - 1, "%s%s", path, dent->d_name);
else
snprintf(fname, sizeof(fname) - 1, "%s" PATHSEP "%s", path, dent->d_name);

if ((status = cvdgetfileage(fname, &file_age)) != CL_SUCCESS) {
cli_errmsg("cl_cvdgetage: cvdgetfileage() failed for %s\n", fname);
goto done;
}

if (first_age_set) {
first_age_set = false;
*age_seconds = file_age;
} else {
*age_seconds = MIN(file_age, *age_seconds);
}
}

done:
if (dd)
closedir(dd);

return status;
}
4 changes: 4 additions & 0 deletions libclamav/libclamav.map
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ CLAMAV_1.0.0 {
cl_cvdunpack;
cl_engine_set_clcb_file_inspection;
} CLAMAV_0.104.0;
CLAMAV_1.1.0 {
global:
cl_cvdgetage;
} CLAMAV_1.0.0;
CLAMAV_PRIVATE {
global:
cli_sigperf_print;
Expand Down
5 changes: 5 additions & 0 deletions win32/conf_examples/clamd.conf.sample
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ Example
# Default: no
#OfficialDatabaseOnly no

# Return with a nonzero error code if the virus database is older than
# the specified number of days.
# Default: -1
#FailIfCvdOlderThan 7

# The daemon on Windows only supports unsecured TCP sockets.
# Due to security reasons make sure that your IP & port is not
# exposed to the open internet.
Expand Down