Skip to content

Commit

Permalink
Add options: --fail-if-cvd-older-than, FailIfCvdOlderThan
Browse files Browse the repository at this point in the history
* Add a new function cl_cvdgetage() to the libclamav API. 

This function will retrieve the age of the youngest file in a
database directory, or the age of a single CVD (or CLD) file.

* Add new clamscan option --fail-if-cvd-older-than=days

When passed, causes clamscan to exit with a non-zero return code
if the virus database is older than the specified number of days.

* Add new clamd option --fail-if-cvd-older-than=days

When passed, causes clamd to exit on start-up with a non-zero
return code if the virus database is older than the specified
number of days.

Additionally, we introduce FailIfCvdOlderThan as a clamd.conf
synonym for --fail-if-cvd-older-than.

Fixes #820
  • Loading branch information
rzvncj authored Mar 28, 2023
1 parent dda7398 commit e4fe665
Show file tree
Hide file tree
Showing 14 changed files with 187 additions and 1 deletion.
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

0 comments on commit e4fe665

Please sign in to comment.