e3aaff8e |
/* |
e1cbc270 |
* Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2007-2013 Sourcefire, Inc.
* Copyright (C) 2002-2007 Tomasz Kojm <tkojm@clamav.net> |
e3aaff8e |
*
* This program is free software; you can redistribute it and/or modify |
bb34cb31 |
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. |
e3aaff8e |
*
* 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 |
48b7b4a7 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA. |
e3aaff8e |
*/ |
5ca6034b |
|
6d6e8271 |
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
|
694e7882 |
#include "libfreshclam/libfreshclam.h"
|
e3aaff8e |
#include <stdio.h>
#include <stdlib.h> |
288057e9 |
#ifdef HAVE_UNISTD_H |
e3aaff8e |
#include <unistd.h> |
97eb9786 |
#endif |
e3aaff8e |
#include <string.h> |
dd95cc2d |
#include <errno.h> |
5ca6034b |
#include <signal.h> |
8000d078 |
#include <time.h> |
e3aaff8e |
#include <sys/types.h> |
288057e9 |
#ifndef _WIN32 |
5f0d9945 |
#include <sys/wait.h> |
97eb9786 |
#endif |
e3aaff8e |
#include <sys/stat.h>
#include <fcntl.h> |
288057e9 |
#ifdef HAVE_PWD_H |
e3aaff8e |
#include <pwd.h> |
081f6473 |
#endif
#ifdef HAVE_GRP_H |
e3aaff8e |
#include <grp.h> |
97eb9786 |
#endif |
e3aaff8e |
|
edb23158 |
#if defined(USE_SYSLOG) && !defined(C_AIX)
#include <syslog.h>
#endif
|
cc71d7c2 |
#include "target.h" |
a889f40e |
#include "clamav.h" |
694e7882 |
#include "libfreshclam/libfreshclam.h" |
a889f40e |
|
c57aae32 |
#include "libclamav/others.h" |
8bf6e781 |
#include "libclamav/str.h" |
c57aae32 |
|
3f7802c9 |
#include "shared/optparser.h" |
a889f40e |
#include "shared/output.h"
#include "shared/misc.h"
|
5f0d9945 |
#include "execute.h" |
694e7882 |
#include "notify.h" |
e3aaff8e |
|
694e7882 |
#define DEFAULT_SERVER_PORT 443 |
dd95cc2d |
|
1e3e2b76 |
int g_sigchildWait = 1;
short g_terminate = 0;
short g_foreground = -1;
const char *g_pidfile = NULL;
char g_freshclamTempDirectory[PATH_MAX] = {0}; |
0ae41a2d |
|
694e7882 |
typedef struct fc_ctx_ {
uint32_t bTestDatabases;
uint32_t bBytecodeEnabled;
} fc_ctx; |
dbc6d4b2 |
|
6df88f29 |
static void |
288057e9 |
sighandler(int sig) |
6df88f29 |
{ |
288057e9 |
switch (sig) {
#ifdef SIGCHLD
case SIGCHLD: |
694e7882 |
if (g_sigchildWait) |
288057e9 |
waitpid(-1, NULL, WNOHANG); |
694e7882 |
g_active_children--; |
288057e9 |
break; |
97eb9786 |
#endif |
536cf542 |
#ifdef SIGPIPE |
288057e9 |
case SIGPIPE:
/* no action, app will get EPIPE */
break; |
536cf542 |
#endif |
288057e9 |
#ifdef SIGALRM
case SIGALRM: |
694e7882 |
g_terminate = -1; |
288057e9 |
break; |
97eb9786 |
#endif |
288057e9 |
#ifdef SIGUSR1
case SIGUSR1: |
694e7882 |
g_terminate = -1; |
288057e9 |
break; |
97eb9786 |
#endif |
288057e9 |
#ifdef SIGHUP
case SIGHUP: |
694e7882 |
g_terminate = -2; |
288057e9 |
break; |
97eb9786 |
#endif |
288057e9 |
default: |
1e3e2b76 |
if (*g_freshclamTempDirectory)
cli_rmdirs(g_freshclamTempDirectory); |
694e7882 |
if (g_pidfile)
unlink(g_pidfile); |
288057e9 |
logg("Update process terminated\n");
exit(0); |
dd95cc2d |
} |
e3eaadd0 |
|
dd95cc2d |
return;
}
|
694e7882 |
static void writepid(const char *pidfile) |
a889f40e |
{ |
6df88f29 |
FILE *fd;
int old_umask; |
288057e9 |
old_umask = umask(0006);
if ((fd = fopen(pidfile, "w")) == NULL) {
logg("!Can't save PID to file %s: %s\n", pidfile, strerror(errno));
} else {
fprintf(fd, "%d\n", (int)getpid());
fclose(fd); |
6df88f29 |
} |
288057e9 |
umask(old_umask); |
dd95cc2d |
}
|
694e7882 |
static void help(void) |
a889f40e |
{ |
20493f29 |
printf("\n");
printf(" Clam AntiVirus: Database Updater %s\n", get_version());
printf(" By The ClamAV Team: https://www.clamav.net/about.html#credits\n");
printf(" (C) 2019 Cisco Systems, Inc.\n");
printf("\n");
printf(" freshclam [options]\n");
printf("\n");
printf(" --help -h Show this help\n");
printf(" --version -V Print version number and exit\n");
printf(" --verbose -v Be verbose\n");
printf(" --debug Enable debug messages\n");
printf(" --quiet Only output error messages\n");
printf(" --no-warnings Don't print and log warnings\n");
printf(" --stdout Write to stdout instead of stderr. Does not affect 'debug' messages.\n");
printf(" --show-progress Show download progress percentage\n");
printf("\n");
printf(" --config-file=FILE Read configuration from FILE.\n");
printf(" --log=FILE -l FILE Log into FILE\n");
printf(" --daemon -d Run in daemon mode\n");
printf(" --pid=FILE -p FILE Save daemon's pid in FILE\n"); |
694e7882 |
#ifndef _WIN32 |
3e63bdc8 |
printf(" --foreground -F Don't fork into background (for use in daemon mode).\n"); |
20493f29 |
printf(" --user=USER -u USER Run as USER\n"); |
be4bf7f4 |
#endif |
20493f29 |
printf(" --no-dns Force old non-DNS verification method\n");
printf(" --checks=#n -c #n Number of checks per day, 1 <= n <= 50\n");
printf(" --datadir=DIRECTORY Download new databases into DIRECTORY\n"); |
a889f40e |
#ifdef BUILD_CLAMD |
20493f29 |
printf(" --daemon-notify[=/path/clamd.conf] Send RELOAD command to clamd\n"); |
a889f40e |
#endif |
20493f29 |
printf(" --local-address=IP -a IP Bind to IP for HTTP downloads\n");
printf(" --on-update-execute=COMMAND Execute COMMAND after successful update\n");
printf(" --on-error-execute=COMMAND Execute COMMAND if errors occurred\n");
printf(" --on-outdated-execute=COMMAND Execute COMMAND when software is outdated\n");
printf(" --update-db=DBNAME Only update database DBNAME\n");
printf("\n"); |
a889f40e |
}
|
edb23158 |
static void libclamav_msg_callback(enum cl_msg severity, const char *fullmsg, const char *msg, void *ctx) |
6bbbf1cc |
{ |
6df13d04 |
UNUSEDPARAM(fullmsg);
UNUSEDPARAM(ctx);
|
288057e9 |
switch (severity) {
case CL_MSG_ERROR:
logg("^[LibClamAV] %s", msg);
break;
case CL_MSG_WARN:
logg("~[LibClamAV] %s", msg);
break;
default:
logg("*[LibClamAV] %s", msg);
break; |
6bbbf1cc |
}
}
|
edb23158 |
static void libclamav_msg_callback_quiet(enum cl_msg severity, const char *fullmsg, const char *msg, void *ctx)
{
UNUSEDPARAM(fullmsg);
UNUSEDPARAM(ctx);
switch (severity) {
case CL_MSG_ERROR:
logg("^[LibClamAV] %s", msg);
break;
default:
break;
}
}
|
694e7882 |
fc_error_t download_complete_callback(const char *dbFilename, void *context) |
e3aaff8e |
{ |
694e7882 |
fc_error_t status = FC_EARG;
fc_error_t ret;
fc_ctx *fc_context = (fc_ctx *)context;
#ifndef WIN32
char firstline[256];
char lastline[256];
int pipefd[2];
pid_t pid;
int stat_loc = 0;
int waitpidret; |
95d401c4 |
#endif |
3f7802c9 |
|
694e7882 |
if ((NULL == context) || (NULL == dbFilename)) {
logg("^Invalid arguments to download_complete_callback.\n");
goto done; |
af38c8ae |
}
|
694e7882 |
logg("*download_complete_callback: Download complete for database : %s\n", dbFilename);
logg("*download_complete_callback: fc_context->bTestDatabases : %u\n", fc_context->bBytecodeEnabled);
logg("*download_complete_callback: fc_context->bBytecodeEnabled : %u\n", fc_context->bBytecodeEnabled);
|
ee3439ca |
printf("Testing database: '%s' ...\n", dbFilename);
|
694e7882 |
if (fc_context->bTestDatabases) {
#ifdef WIN32
__try {
ret = fc_test_database(dbFilename, fc_context->bBytecodeEnabled);
} __except (logg("!Exception during database testing, code %08x\n",
GetExceptionCode()),
EXCEPTION_CONTINUE_SEARCH) { |
ee3439ca |
ret = FC_ETESTFAIL; |
694e7882 |
}
if (FC_SUCCESS != ret) {
logg("^Database load exited with \"%s\" (%d)\n", fc_strerror(ret), ret);
status = FC_ETESTFAIL;
goto done;
}
#else
if (pipe(pipefd) == -1) {
/*
* Failed to create pipe.
* Test database without using pipe & child process.
*/
logg("^pipe() failed: %s\n", strerror(errno));
ret = fc_test_database(dbFilename, fc_context->bBytecodeEnabled);
if (FC_SUCCESS != ret) {
logg("^Database load exited with \"%s\" (%d)\n", fc_strerror(ret), ret);
status = FC_ETESTFAIL;
goto done;
}
} else {
switch (pid = fork()) {
case -1: {
/*
* Fork failed.
* Test database without using pipe & child process.
*/
close(pipefd[0]);
close(pipefd[1]);
logg("^fork() to test database failed: %s\n", strerror(errno));
/* Test the database without forking. */
ret = fc_test_database(dbFilename, fc_context->bBytecodeEnabled);
if (FC_SUCCESS != ret) {
logg("^Database load exited with \"%s\" (%d)\n", fc_strerror(ret), ret);
status = FC_ETESTFAIL;
goto done;
}
}
case 0: {
/*
* Child process.
*/
close(pipefd[0]);
/* Redirect stderr to the pipe for the parent process */
if (dup2(pipefd[1], 2) == -1) {
logg("^dup2() call to redirect stderr to pipe failed: %s\n", strerror(errno));
}
/* Test the database */
status = fc_test_database(dbFilename, fc_context->bBytecodeEnabled);
exit(status);
}
default: {
/*
* Original/parent process.
*/
FILE *pipeHandle = NULL;
/* read first / last line printed by child */
close(pipefd[1]);
pipeHandle = fdopen(pipefd[0], "r");
firstline[0] = 0;
lastline[0] = 0;
do {
if (!fgets(firstline, sizeof(firstline), pipeHandle))
break;
/* ignore warning messages, otherwise the outdated warning will
* make us miss the important part of the error message */
} while (!strncmp(firstline, "LibClamAV Warning:", 18));
/* must read entire output, child doesn't like EPIPE */
while (fgets(lastline, sizeof(firstline), pipeHandle)) {
/* print the full output only when LogVerbose or -v is given */
logg("*%s", lastline);
}
fclose(pipeHandle);
pipeHandle = NULL;
while ((-1 == (waitpidret = waitpid(pid, &stat_loc, 0))) && (errno == EINTR)) {
continue;
}
if ((waitpidret == -1) && (errno != ECHILD))
logg("^waitpid() failed: %s\n", strerror(errno));
/* Strip trailing whitespace from child error output */
cli_chomp(firstline);
cli_chomp(lastline);
if (firstline[0]) {
/* The child process output some error messages */
logg("^Stderr output from database load : %s%s%s\n", firstline, lastline[0] ? " [...] " : "", lastline);
}
if (WIFEXITED(stat_loc)) {
ret = (fc_error_t)WEXITSTATUS(stat_loc);
if (FC_SUCCESS != ret) {
logg("^Database load exited with \"%s\" (%d)\n", fc_strerror(ret), ret);
status = FC_ETESTFAIL;
goto done;
}
if (firstline[0])
logg("^Database successfully loaded, but there is stderr output\n");
} else if (WIFSIGNALED(stat_loc)) {
logg("!Database load killed by signal %d\n", WTERMSIG(stat_loc));
status = FC_ETESTFAIL;
goto done;
} else {
logg("^Unknown status from wait: %d\n", stat_loc);
status = FC_ETESTFAIL;
goto done;
}
}
}
}
#endif |
7b8edc5c |
} |
e3aaff8e |
|
694e7882 |
status = FC_SUCCESS;
done: |
ee3439ca |
if (FC_SUCCESS == status) {
printf("Database test passed.\n");
} else {
printf("Database test FAILED.\n");
}
|
694e7882 |
g_sigchildWait = 1;
return status;
}
/**
* @brief Adapt server strings to protocol://server:port format.
*
* IPv6 addresses must be enclosed with square brackets.
* Port number and port number delimiter (:) are optional.
* If port number is omitted, 443 will be assumed.
*
* Example server strings:
* - database.clamav.net
* - http://db.sample.net:5678
* - [2001::100a]
* - https://[2001:db8:1f70::999:de8:7648:6e8]:7890
*
* @param server Server string
* @param defaultProtocol Default protocol if not already specified. Eg: "https"
* @param defaultPort Default port if not already specified. Eg: 443
* @param serverUrl [out] A malloced string in the protocol://server:port format.
* @return fc_error_t FC_SUCCESS if success.
* @return fc_error_t FC_EARG if invalid args.
* @return fc_error_t FC_EMEM if malloc failed.
* @return fc_error_t FC_ECONFIG if a parsing issue occured.
*/
static fc_error_t get_server_node(
const char *server,
char *defaultProtocol,
char **serverUrl)
{
fc_error_t status = FC_EARG;
char *url = NULL;
size_t urlLen = 0;
if ((NULL == server) || (NULL == defaultProtocol) || (NULL == serverUrl)) {
mprintf("!get_server_node: Invalid args!\n");
goto done; |
a889f40e |
}
|
694e7882 |
*serverUrl = NULL;
/*
* Ensure that URL contains protocol.
*/ |
0e7399e5 |
if (!strncmp(server, "db.", 3) && strstr(server, ".clamav.net")) {
url = cli_strdup("https://database.clamav.net");
if (NULL == url) {
logg("!get_server_node: Failed to duplicate string for database.clamav.net url.\n");
status = FC_EMEM;
goto done;
}
} else if (!strstr(server, "://")) { |
694e7882 |
urlLen = strlen(defaultProtocol) + strlen("://") + strlen(server);
url = malloc(urlLen + 1);
if (NULL == url) {
logg("!get_server_node: Failed to allocate memory for server url.\n");
status = FC_EMEM;
goto done;
}
snprintf(url, urlLen + 1, "%s://%s", defaultProtocol, server);
} else {
urlLen = strlen(server);
url = cli_strdup(server);
if (NULL == url) {
logg("!get_server_node: Failed to duplicate string for server url.\n");
status = FC_EMEM;
goto done; |
b68375fd |
}
}
|
694e7882 |
*serverUrl = url;
status = FC_SUCCESS;
done:
if (FC_SUCCESS != status) {
if (NULL != url) {
free(url); |
288057e9 |
}
} |
b68375fd |
|
694e7882 |
return status;
}
/**
* @brief Add string to list of strings.
*
* @param item string to add to list.
* @param stringList [in/out] String list to add string to.
* @param nListItems [in/out] Number of strings in list.
* @return fc_error_t FC_SUCCESS if success.
* @return fc_error_t FC_EARG if invalid args passed to function.
* @return fc_error_t FC_EMEM if failed to allocate memory.
*/
static fc_error_t string_list_add(const char *item, char ***stringList, uint32_t *nListItems)
{
fc_error_t status = FC_EARG;
char **newList = NULL;
uint32_t nItems = 0;
if ((NULL == item) || (NULL == stringList) || (NULL == nListItems)) {
mprintf("!string_list_add: Invalid arguments.\n");
goto done; |
95d401c4 |
}
|
694e7882 |
nItems = *nListItems + 1;
newList = (char **)cli_realloc(*stringList, nItems * sizeof(char *));
if (newList == NULL) {
mprintf("!string_list_add: Failed to allocate memory for optional database list entry.\n");
status = FC_EMEM;
goto done; |
0aa3ba06 |
}
|
694e7882 |
*stringList = newList;
newList[nItems - 1] = cli_strdup(item);
if (newList[nItems - 1] == NULL) {
mprintf("!string_list_add: Failed to allocate memory for optional database list item.\n");
status = FC_EMEM;
goto done;
}
*nListItems = nItems;
status = FC_SUCCESS;
done:
return status;
}
/**
* @brief Convenience function to free strings in an array of strings.
*
* Will also free the list itself.
*
* @param stringList
* @param nListItems
*/
static void free_string_list(char **stringList, uint32_t nListItems)
{
uint32_t i;
if (NULL != stringList) {
for (i = 0; i < nListItems; i++) {
if (stringList[i] != NULL) {
free(stringList[i]);
stringList[i] = NULL;
} |
6df88f29 |
} |
97eb9786 |
|
694e7882 |
free(stringList);
}
}
/**
* @brief Get the database server list object
*
* @param opts Freshclam options struct.
* @param serverList [out] List of servers.
* @param nServers [out] Number of servers in list.
* @param bPrivate [out] Non-zero if PrivateMirror servers were selected.
* @return fc_error_t
*/
static fc_error_t get_database_server_list(
struct optstruct *opts,
char ***serverList,
uint32_t *nServers,
int *bPrivate)
{
fc_error_t ret;
fc_error_t status = FC_EARG;
const struct optstruct *opt;
char **servers = NULL;
uint32_t numServers = 0;
if ((NULL == opts) || (NULL == serverList) || (NULL == nServers) || (NULL == bPrivate)) {
mprintf("!get_database_server_list: Invalid args!\n");
goto done;
}
*serverList = NULL;
*nServers = 0;
*bPrivate = 0;
if ((opt = optget(opts, "PrivateMirror"))->enabled) {
/* Config specifies at least one PrivateMirror.
* Ignore the DatabaseMirrors. */
*bPrivate = 1;
do {
char *serverUrl = NULL;
if (cli_strbcasestr(opt->strarg, ".clamav.net")) {
logg("!The PrivateMirror config option may not include servers under *.clamav.net.\n");
status = FC_ECONFIG;
goto done;
}
if (FC_SUCCESS != (ret = get_server_node(opt->strarg, "http", &serverUrl))) {
mprintf("!get_database_server_list: Failed to read PrivateMirror server %s", opt->strarg);
status = ret;
goto done;
}
if (FC_SUCCESS != (ret = string_list_add(serverUrl, &servers, &numServers))) {
free(serverUrl);
mprintf("!get_database_server_list: Failed to add string to list.\n");
status = ret;
goto done;
}
free(serverUrl);
} while (NULL != (opt = opt->nextarg));
} else {
/* Check for DatabaseMirrors. */
if (!(opt = optget(opts, "DatabaseMirror"))->enabled) {
/* No DatabaseMirror configured. Fail out. */
logg("!No DatabaseMirror or PrivateMirror servers set in freshclam config file.\n");
status = FC_ECONFIG;
goto done; |
6df88f29 |
} |
694e7882 |
do {
char *serverUrl = NULL;
if (FC_SUCCESS != (ret = get_server_node(opt->strarg, "https", &serverUrl))) {
mprintf("!get_database_server_list: Failed to parse DatabaseMirror server %s.", opt->strarg);
status = ret;
goto done;
}
if (FC_SUCCESS != (ret = string_list_add(serverUrl, &servers, &numServers))) {
free(serverUrl);
mprintf("!get_database_server_list: Failed to add string to list.\n");
status = ret;
goto done;
}
free(serverUrl);
} while (NULL != (opt = opt->nextarg));
}
*serverList = servers;
*nServers = numServers;
status = FC_SUCCESS;
done:
if (FC_SUCCESS != status) {
free_string_list(servers, numServers); |
c2a3cdf9 |
} |
af22ece1 |
|
694e7882 |
return status;
}
/**
* @brief Switch users to the DatabaseOwner, if specified.
*
* @param dbowner A user account that will have write permissions in the database directory.
* @return fc_error_t FC_SUCCESS if success.
* @return fc_error_t FC_EARG if success.
*/
static fc_error_t switch_user(const char *dbowner)
{
fc_error_t status = FC_EARG;
|
4cd80898 |
#ifdef HAVE_PWD_H |
694e7882 |
struct passwd *user;
if (NULL == dbowner) {
logg("*No DatabaseOwner specified. Freshclam wlil run as the current user.\n");
status = FC_SUCCESS;
goto done;
} |
288057e9 |
if (!geteuid()) {
if ((user = getpwnam(dbowner)) == NULL) {
logg("^Can't get information about user %s.\n", dbowner); |
694e7882 |
status = FC_ECONFIG;
goto done; |
6df88f29 |
} |
e3aaff8e |
|
fdeade2a |
#ifdef HAVE_INITGROUPS |
288057e9 |
if (initgroups(dbowner, user->pw_gid)) {
logg("^initgroups() failed.\n"); |
694e7882 |
status = FC_ECONFIG;
goto done; |
288057e9 |
} |
2ea4230d |
#elif HAVE_SETGROUPS |
288057e9 |
if (setgroups(1, &user->pw_gid)) {
logg("^setgroups() failed.\n"); |
694e7882 |
status = FC_ECONFIG;
goto done; |
288057e9 |
} |
a7d9bef2 |
#endif |
6df88f29 |
|
288057e9 |
if (setgid(user->pw_gid)) {
logg("^setgid(%d) failed.\n", (int)user->pw_gid); |
694e7882 |
status = FC_ECONFIG;
goto done; |
6df88f29 |
}
|
288057e9 |
if (setuid(user->pw_uid)) {
logg("^setuid(%d) failed.\n", (int)user->pw_uid); |
694e7882 |
status = FC_ECONFIG;
goto done; |
6df88f29 |
} |
e3aaff8e |
} |
f7c0a571 |
#endif /* HAVE_PWD_H */ |
e3aaff8e |
|
694e7882 |
status = FC_SUCCESS; |
e3aaff8e |
|
694e7882 |
done: |
e4ae7726 |
|
694e7882 |
return status;
} |
e3aaff8e |
|
694e7882 |
/**
* @brief Get a list of strings for a given repeatable opt argument.
*
* @param opt optstruct of repeatable argument to collect in a list.
* @param stringList [out] String list.
* @param nListItems [out] Number of strings in list.
* @return fc_error_t FC_SUCCESS if success.
* @return fc_error_t FC_EARG if invalid args passed to function.
* @return fc_error_t FC_EMEM if failed to allocate memory.
*/
static fc_error_t get_string_list(const struct optstruct *opt, char ***stringList, uint32_t *nListItems)
{
fc_error_t ret;
fc_error_t status = FC_EARG;
char **newList = NULL;
uint32_t nItems = 0; |
e3aaff8e |
|
694e7882 |
if ((NULL == opt) || (NULL == stringList) || (NULL == nListItems)) {
mprintf("!get_string_list: Invalid arguments.\n");
goto done; |
bcbe6ad7 |
}
|
694e7882 |
*stringList = NULL;
*nListItems = 0; |
e3aaff8e |
|
694e7882 |
/* handle extra dbs */
if (opt->enabled) {
while (opt) {
if (FC_SUCCESS != (ret = string_list_add(opt->strarg, stringList, nListItems))) {
mprintf("!get_string_list: Failed to add string to list.\n");
status = ret;
goto done;
}
opt = opt->nextarg;
}
}
status = FC_SUCCESS;
done:
if (FC_SUCCESS != status) {
free_string_list(newList, nItems);
}
return status;
}
static fc_error_t initialize(struct optstruct *opts)
{
fc_error_t ret;
fc_error_t status = FC_EARG;
cl_error_t cl_init_retcode;
fc_config fcConfig;
char *tempDirectory = NULL;
size_t tempDirectoryLen;
const struct optstruct *logFileOpt = NULL;
STATBUF statbuf;
memset(&fcConfig, 0, sizeof(fc_config));
if (NULL == opts) {
mprintf("!initialize: Invalid arguments.\n");
goto done;
}
/* Now that the config has been parsed,
check Foreground again if not already determined. */
if (g_foreground == -1) {
if (optget(opts, "Foreground")->enabled) {
g_foreground = 1;
} else {
g_foreground = 0;
}
}
|
75a5857d |
#ifdef HAVE_PWD_H
/*
* freshclam shouldn't work with root privileges.
* Drop privileges to the DatabaseOwner user, if specified.
*/
ret = switch_user(optget(opts, "DatabaseOwner")->strarg);
if (FC_SUCCESS != ret) {
logg("!Failed to switch to %s user.\n", optget(opts, "DatabaseOwner")->strarg);
status = ret;
goto done;
}
#endif /* HAVE_PWD_H */
|
694e7882 |
/*
* Initilize libclamav.
*/
if (CL_SUCCESS != (cl_init_retcode = cl_init(CL_INIT_DEFAULT))) {
mprintf("!initialize: Can't initialize libclamav: %s\n", cl_strerror(cl_init_retcode));
status = FC_EINIT;
goto done;
}
/*
* Identify libfreshclam config options.
*/ |
edb23158 |
/* Set libclamav Message and [file-based] Logging option flags.
mprintf and logg options are also directly set, as they are also
used in freshclam (not only used in libfreshclam) */ |
694e7882 |
if (optget(opts, "Debug")->enabled || optget(opts, "debug")->enabled)
fcConfig.msgFlags |= FC_CONFIG_MSG_DEBUG; |
edb23158 |
if ((optget(opts, "verbose")->enabled) ||
(optget(opts, "LogVerbose")->enabled)) { |
694e7882 |
fcConfig.msgFlags |= FC_CONFIG_MSG_VERBOSE; |
edb23158 |
fcConfig.logFlags |= FC_CONFIG_LOG_VERBOSE;
}
if (optget(opts, "quiet")->enabled) { |
694e7882 |
fcConfig.msgFlags |= FC_CONFIG_MSG_QUIET; |
edb23158 |
/* Silence libclamav messages. */
cl_set_clcb_msg(libclamav_msg_callback_quiet);
} else {
/* Enable libclamav messages, with [LibClamAV] message prefix. */
cl_set_clcb_msg(libclamav_msg_callback);
}
if (optget(opts, "no-warnings")->enabled) { |
694e7882 |
fcConfig.msgFlags |= FC_CONFIG_MSG_NOWARN; |
edb23158 |
fcConfig.logFlags |= FC_CONFIG_LOG_NOWARN;
}
if (optget(opts, "stdout")->enabled) { |
694e7882 |
fcConfig.msgFlags |= FC_CONFIG_MSG_STDOUT; |
edb23158 |
}
if (optget(opts, "show-progress")->enabled) { |
694e7882 |
fcConfig.msgFlags |= FC_CONFIG_MSG_SHOWPROGRESS; |
edb23158 |
} |
694e7882 |
|
edb23158 |
if (optget(opts, "LogTime")->enabled) { |
694e7882 |
fcConfig.logFlags |= FC_CONFIG_LOG_TIME; |
edb23158 |
}
if (optget(opts, "LogFileMaxSize")->numarg && optget(opts, "LogRotate")->enabled) { |
694e7882 |
fcConfig.logFlags |= FC_CONFIG_LOG_ROTATE; |
edb23158 |
} |
694e7882 |
if (optget(opts, "LogSyslog")->enabled)
fcConfig.logFlags |= FC_CONFIG_LOG_SYSLOG;
logFileOpt = optget(opts, "UpdateLogFile"); |
edb23158 |
if (logFileOpt->enabled) { |
694e7882 |
fcConfig.logFile = logFileOpt->strarg; |
edb23158 |
}
if (optget(opts, "LogFileMaxSize")->numarg) { |
694e7882 |
fcConfig.maxLogSize = optget(opts, "LogFileMaxSize")->numarg; |
edb23158 |
} |
e3aaff8e |
|
afb48b28 |
#if defined(USE_SYSLOG) && !defined(C_AIX) |
288057e9 |
if (optget(opts, "LogSyslog")->enabled) { |
edb23158 |
if (optget(opts, "LogFacility")->enabled) { |
694e7882 |
fcConfig.logFacility = optget(opts, "LogFacility")->strarg; |
edb23158 |
} |
694e7882 |
}
#endif
if ((optget(opts, "LocalIPAddress"))->enabled)
fcConfig.localIP = (optget(opts, "LocalIPAddress"))->strarg;
fcConfig.databaseDirectory = optget(opts, "DatabaseDirectory")->strarg;
/* Select a path for the temp directory: databaseDirectory/tmp */
tempDirectoryLen = strlen(fcConfig.databaseDirectory) + strlen(PATHSEP) + strlen("tmp") + 1; /* mdir/fname\0 */
tempDirectory = (char *)cli_calloc(tempDirectoryLen, sizeof(char));
if (!tempDirectory) {
mprintf("!initialize: out of memory\n");
status = FC_EMEM;
goto done;
}
snprintf(tempDirectory, tempDirectoryLen, "%s" PATHSEP "tmp", fcConfig.databaseDirectory);
fcConfig.tempDirectory = tempDirectory;
if (LSTAT(tempDirectory, &statbuf) == -1) {
if (0 != mkdir(tempDirectory, 0755)) {
logg("!Can't create temporary directory %s\n", tempDirectory);
logg("Hint: The database directory must be writable for UID %d or GID %d\n", getuid(), getgid());
status = FC_EDBDIRACCESS;
goto done; |
6df88f29 |
} |
694e7882 |
} |
6df88f29 |
|
694e7882 |
/* Store the path of the temp directory so we can delete it later. */ |
1e3e2b76 |
strncpy(g_freshclamTempDirectory, fcConfig.tempDirectory, sizeof(g_freshclamTempDirectory));
g_freshclamTempDirectory[sizeof(g_freshclamTempDirectory) - 1] = '\0'; |
694e7882 |
#ifndef _WIN32
/*
* If clamd.conf includes a HTTPProxyPassword,...
* ...make sure that permissions on the clamd.conf file aren't just wide open.
* If they are, fail out and warn the user so they will fix it.
*/
if (optget(opts, "HTTPProxyPassword")->enabled) {
STATBUF statbuf;
const char *cfgfile = NULL;
cfgfile = optget(opts, "config-file")->strarg;
if (CLAMSTAT(cfgfile, &statbuf) == -1) {
logg("^Can't stat %s (critical error)\n", cfgfile);
status = FC_ECONFIG;
goto done;
}
if (statbuf.st_mode & (S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH)) {
logg("^Insecure permissions (for HTTPProxyPassword): %s must have no more than 0700 permissions.\n", cfgfile);
status = FC_ECONFIG;
goto done;
} |
58bcf502 |
} |
fb787a06 |
#endif
|
694e7882 |
/* Initialize proxy settings */
if (optget(opts, "HTTPProxyServer")->enabled) {
fcConfig.proxyServer = optget(opts, "HTTPProxyServer")->strarg;
if (strncasecmp(fcConfig.proxyServer, "http://", strlen("http://")) == 0)
fcConfig.proxyServer += strlen("http://");
if (optget(opts, "HTTPProxyUsername")->enabled) {
fcConfig.proxyUsername = optget(opts, "HTTPProxyUsername")->strarg;
if (optget(opts, "HTTPProxyPassword")->enabled) {
fcConfig.proxyPassword = optget(opts, "HTTPProxyPassword")->strarg;
} else {
logg("HTTPProxyUsername requires HTTPProxyPassword\n");
status = FC_ECONFIG;
goto done;
} |
6df88f29 |
} |
694e7882 |
if (optget(opts, "HTTPProxyPort")->enabled)
fcConfig.proxyPort = (uint16_t)optget(opts, "HTTPProxyPort")->numarg;
logg("Connecting via %s\n", fcConfig.proxyServer); |
92e22a60 |
} |
e3aaff8e |
|
694e7882 |
if (optget(opts, "HTTPUserAgent")->enabled)
fcConfig.userAgent = optget(opts, "HTTPUserAgent")->strarg;
fcConfig.maxAttempts = optget(opts, "MaxAttempts")->numarg;
fcConfig.connectTimeout = optget(opts, "ConnectTimeout")->numarg;
fcConfig.requestTimeout = optget(opts, "ReceiveTimeout")->numarg;
fcConfig.bCompressLocalDatabase = optget(opts, "CompressLocalDatabase")->enabled;
/*
* Initilize libfreshclam.
*/
if (FC_SUCCESS != (ret = fc_initialize(&fcConfig))) {
mprintf("!initialize: libfreshclam init failed.\n");
status = ret;
goto done;
}
/*
* Set libfreshclam callback functions.
*/
fc_set_fccb_download_complete(download_complete_callback);
status = FC_SUCCESS;
done:
if (NULL != tempDirectory) {
free(tempDirectory);
}
return status;
}
/**
* @brief Get the official database lists.
*
* TODO: Implement system to query list of available standard and optional databases.
*
* @param standardDatabases [out] Standard database string list.
* @param nStandardDatabases [out] Number of standard databases in list.
* @param optionalDatabases [out] Optional database string list.
* @param nOptionalDatabases [out] Number of optional databases in list.
* @return fc_error_t FC_SUCCESS if all databases upddated successfully.
*/
fc_error_t get_official_database_lists(
char ***standardDatabases,
uint32_t *nStandardDatabases,
char ***optionalDatabases,
uint32_t *nOptionalDatabases)
{
fc_error_t ret;
fc_error_t status = FC_EARG;
uint32_t i;
const char *hardcodedStandardDatabaseList[] = {"daily", "main", "bytecode"};
const char *hardcodedOptionalDatabaseList[] = {"safebrowsing", "test"};
if ((NULL == standardDatabases) || (NULL == nStandardDatabases) || (NULL == optionalDatabases) || (NULL == nOptionalDatabases)) {
mprintf("!get_official_database_lists: Invalid arguments.\n");
goto done;
}
*standardDatabases = NULL;
*nStandardDatabases = 0;
*optionalDatabases = NULL;
*nOptionalDatabases = 0;
for (i = 0; i < sizeof(hardcodedStandardDatabaseList) / sizeof(hardcodedStandardDatabaseList[0]); i++) {
if (FC_SUCCESS != (ret = string_list_add(hardcodedStandardDatabaseList[i], standardDatabases, nStandardDatabases))) {
logg("!Failed to add %s to list of standard databases.\n", hardcodedStandardDatabaseList[i]);
status = ret;
goto done; |
6df88f29 |
} |
376307a0 |
}
|
694e7882 |
for (i = 0; i < sizeof(hardcodedOptionalDatabaseList) / sizeof(hardcodedOptionalDatabaseList[0]); i++) {
if (FC_SUCCESS != (ret = string_list_add(hardcodedOptionalDatabaseList[i], optionalDatabases, nOptionalDatabases))) {
logg("!Failed to add %s to list of optional databases.\n", hardcodedOptionalDatabaseList[i]);
status = ret;
goto done;
}
} |
6df88f29 |
|
694e7882 |
logg("*Collected lists of official standard and optional databases.\n");
status = FC_SUCCESS;
done:
if (FC_SUCCESS != status) {
if ((NULL != standardDatabases) && (*standardDatabases != NULL) && (nStandardDatabases != NULL)) {
free_string_list(*standardDatabases, *nStandardDatabases);
*standardDatabases = NULL;
*nStandardDatabases = 0;
}
if ((NULL != optionalDatabases) && (*optionalDatabases != NULL) && (nOptionalDatabases != NULL)) {
free_string_list(*optionalDatabases, *nOptionalDatabases);
*optionalDatabases = NULL;
*nOptionalDatabases = 0;
}
}
return status;
}
/**
* @brief Select desire databases from standard and optional database lists.
*
* Select:
* all standard databases excluding those in the opt-out list,
* any optional databases includedd in the opt-in list.
*
* databaseList should be free'd with free_string_list().
*
* @param optInList List of desired opt-in databases.
* @param nOptIns Number of opt-in database strings in list.
* @param optOutList List of standard databases that are not desired.
* @param nOptOuts Number of opt-out database strings in list.
* @param databaseList [out] String list of desired databases.
* @param nDatabases [out] Number of desired databases in list.
* @return fc_error_t
*/
fc_error_t select_from_official_databases(
char **optInList,
uint32_t nOptIns,
char **optOutList,
uint32_t nOptOuts,
char ***databaseList,
uint32_t *nDatabases)
{
fc_error_t ret;
fc_error_t status = FC_EARG;
char **standardDatabases = NULL;
uint32_t nStandardDatabases = 0;
char **optionalDatabases = NULL;
uint32_t nOptionalDatabases = 0;
char **selectedDatabases = NULL;
uint32_t nSelectedDatabases = 0;
uint32_t i;
if ((NULL == databaseList) || (0 == nDatabases)) {
mprintf("!select_from_official_databases: Invalid arguments.\n");
goto done;
}
*databaseList = NULL;
*nDatabases = 0;
if ((0 < nOptIns) && (NULL == optInList)) {
mprintf("!select_from_official_databases: Invalid arguments. Number of opt-in databases does not match empty database array.\n");
goto done;
}
if ((0 < nOptOuts) && (NULL == optOutList)) {
mprintf("!select_from_official_databases: Invalid arguments. Number of opt-out databases does not match empty database array.\n");
goto done;
}
/*
* Get lists of available databases.
*/
if (FC_SUCCESS != (ret = get_official_database_lists(&standardDatabases, &nStandardDatabases, &optionalDatabases, &nOptionalDatabases))) {
logg("!Failed to get lists of official standard and optional databases.\n");
status = ret;
goto done;
}
selectedDatabases = cli_calloc(nStandardDatabases + nOptionalDatabases, sizeof(char *));
/*
* Select desired standard databases.
*/
for (i = 0; i < nStandardDatabases; i++) {
uint32_t j;
int skip = 0;
for (j = 0; j < nOptOuts; j++) {
if (0 == strcasecmp(standardDatabases[i], optOutList[j])) {
skip = 1; |
6df88f29 |
} |
694e7882 |
}
if (skip) {
logg("*Opting out of standard database: %s\n", standardDatabases[i]);
continue;
}
logg("*Selecting standard database: %s\n", standardDatabases[i]);
if (FC_SUCCESS != (ret = string_list_add(standardDatabases[i], &selectedDatabases, &nSelectedDatabases))) {
logg("!Failed to add standard database %s to list of selected databases.\n", standardDatabases[i]);
status = ret;
goto done;
}
} |
6df88f29 |
|
694e7882 |
/*
* Select desired optional databases.
*/
for (i = 0; i < nOptIns; i++) {
uint32_t j;
int found = 0;
for (j = 0; j < nOptionalDatabases; j++) {
if (0 == strcasecmp(optInList[i], optionalDatabases[j])) {
found = 1; |
6df88f29 |
} |
694e7882 |
}
if (!found) {
logg("^Desired optional database \"%s\" is not available.\n", optInList[i]);
continue;
}
|
60c149c9 |
logg("*Selecting optional database: %s\n", optInList[i]);
if (FC_SUCCESS != (ret = string_list_add(optInList[i], &selectedDatabases, &nSelectedDatabases))) {
logg("!Failed to add optional database %s to list of selected databases.\n", optInList[i]); |
694e7882 |
status = ret;
goto done;
}
}
*databaseList = selectedDatabases;
*nDatabases = nSelectedDatabases;
status = FC_SUCCESS;
done:
if (NULL != standardDatabases) {
free_string_list(standardDatabases, nStandardDatabases);
}
if (NULL != optionalDatabases) {
free_string_list(optionalDatabases, nOptionalDatabases);
}
if (FC_SUCCESS != status) {
if (NULL != selectedDatabases) {
free_string_list(selectedDatabases, nSelectedDatabases);
}
}
return status;
}
/**
* @brief Select specific databases provided by standard and optional database lists.
*
* Validate that requested databases are available.
*
* databaseList should be free'd with free_string_list().
*
* @param specificDatabaseList List of desired databases.
* @param nSpecificDatabases Number of databases in list.
* @param databaseList [out] String list of desired databases.
* @param nDatabases [out] Number of desired databases in list.
* @param bCustom [out] "custom" selected.
* @return fc_error_t
*/
fc_error_t select_specific_databases(
char **specificDatabaseList,
uint32_t nSpecificDatabases,
char ***databaseList,
uint32_t *nDatabases,
int *bCustom)
{
fc_error_t ret;
fc_error_t status = FC_EARG;
char **standardDatabases = NULL;
uint32_t nStandardDatabases = 0;
char **optionalDatabases = NULL;
uint32_t nOptionalDatabases = 0;
char **selectedDatabases = NULL;
uint32_t nSelectedDatabases = 0;
uint32_t i;
if ((NULL == specificDatabaseList) || (0 == nSpecificDatabases) ||
(NULL == databaseList) || (0 == nDatabases) ||
(NULL == bCustom)) {
mprintf("!select_from_official_databases: Invalid arguments.\n");
goto done;
}
*bCustom = 0;
*databaseList = NULL;
*nDatabases = 0;
selectedDatabases = cli_calloc(nSpecificDatabases, sizeof(char *));
/*
* Get lists of available databases.
*/
if (FC_SUCCESS != (ret = get_official_database_lists(&standardDatabases, &nStandardDatabases, &optionalDatabases, &nOptionalDatabases))) {
logg("!Failed to get lists of official standard and optional databases.\n");
status = ret;
goto done;
}
/*
* Select desired standard databases.
*/
for (i = 0; i < nSpecificDatabases; i++) {
uint32_t j;
int bFound = 0;
/* If "custom" requested, then user will be updating unofficial database(s) by URLs. */
if (0 == strcmp(specificDatabaseList[i], "custom")) {
*bCustom = 1;
continue;
}
/* Check if provided by standard database list. */
for (j = 0; j < nStandardDatabases; j++) {
if (0 == strcmp(specificDatabaseList[i], standardDatabases[j])) {
if (FC_SUCCESS != (ret = string_list_add(standardDatabases[j], &selectedDatabases, &nSelectedDatabases))) {
logg("!Failed to add standard database %s to list of selected databases.\n", standardDatabases[j]);
status = ret;
goto done; |
6df88f29 |
} |
694e7882 |
bFound = 1;
break;
}
}
if (!bFound) {
/* Check if provided by optional database list. */
for (j = 0; j < nOptionalDatabases; j++) {
if (0 == strcmp(specificDatabaseList[i], optionalDatabases[j])) {
if (FC_SUCCESS != (ret = string_list_add(optionalDatabases[j], &selectedDatabases, &nSelectedDatabases))) {
logg("!Failed to add optional database %s to list of selected databases.\n", optionalDatabases[j]);
status = ret;
goto done;
}
bFound = 1;
break;
}
}
}
if (!bFound) {
logg("!Requested database is not available: %s.\n", specificDatabaseList[i]);
status = FC_ECONFIG;
goto done;
}
}
*databaseList = selectedDatabases;
*nDatabases = nSelectedDatabases;
status = FC_SUCCESS;
done:
if (NULL != standardDatabases) {
free_string_list(standardDatabases, nStandardDatabases);
}
if (NULL != optionalDatabases) {
free_string_list(optionalDatabases, nOptionalDatabases);
}
if (FC_SUCCESS != status) {
if (NULL != selectedDatabases) {
free_string_list(selectedDatabases, nSelectedDatabases);
}
}
return status;
}
static fc_error_t executeIfNewVersion(
const char *command,
char *newVersion,
int bDaemonized)
{
fc_error_t status = FC_EARG;
char *modifiedCommand = NULL;
char *replace_version = NULL;
if ((NULL == command) || (NULL == newVersion)) {
logg("!executeIfNewVersion: Invalid args\n");
status = FC_EARG;
goto done;
}
if (NULL == (replace_version = strstr(command, "%v"))) {
/*
* Execute command as-is.
*/
execute("OnOutdatedExecute", command, bDaemonized);
} else {
/*
* Replace "%v" with version numbers, then execute command.
*/
char *after_replace_version = NULL;
char *version = newVersion;
while (*version) {
if (!strchr("0123456789.", *version)) {
logg("!executeIfNewVersion: OnOutdatedExecute: Incorrect version number string\n");
status = FC_EARG;
goto done;
}
version++;
}
char *modifiedCommand = (char *)malloc(strlen(command) + strlen(version) + 10);
if (NULL == modifiedCommand) {
logg("!executeIfNewVersion: Can't allocate memory for modifiedCommand\n");
status = FC_EMEM;
goto done;
}
/* Copy first half of command */
strncpy(modifiedCommand, command, replace_version - command);
modifiedCommand[replace_version - command] = '\0'; /* Add null terminator */
/* Cat on the version number */
strcat(modifiedCommand, version);
/* Cat on the rest of the command */
after_replace_version = replace_version + 2;
strcat(modifiedCommand, after_replace_version);
/* Make it so. */
execute("OnOutdatedExecute", modifiedCommand, bDaemonized);
}
status = FC_SUCCESS;
done:
if (NULL != modifiedCommand) {
free(modifiedCommand);
}
return status;
}
/**
* @brief Update official databases.
*
* Will update select official databases given the configuration.
*
* @param databaseList String list of desired official databases.
* @param nDatabases Number of official databases in list.
* @param urlDatabaseList String list of desired unofficial databases updated by URL.
* @param nUrlDatabases Number of database URLs in list.
* @param serverList String list of DatabaseMirror or PrivateMirror servers.
* @param nServers Number of servers in list.
* @param dnsUpdateInfoServer (optional) DNS update info server. May be NULl to disable use of DNS.
* @param bScriptedUpdates Nonzero to enable incremental/scripted (efficient) updates.
* @param bPrune Prune official databases that are no longer desired or avaialable.
* @param onUpdateExecute (optional) Command to to run after 1+ databases have been updated.
* @param onOutdatedExecute (optional) Command to run if new version of ClamAV is available.
* @param bDaemonized Non-zero if process has daemonized.
* @param notifyClamd (optional) Path to clamd.conf to notify clamd.
* @param fc_context (optional) Context information for callback functions.
* @return fc_error_t FC_SUCCESS if all databases upddated successfully.
*/
fc_error_t perform_database_update(
char **databaseList,
uint32_t nDatabases,
char **urlDatabaseList,
uint32_t nUrlDatabases,
char **serverList,
uint32_t nServers,
int bPrivateMirror,
const char *dnsUpdateInfoServer,
int bScriptedUpdates,
int bPrune,
const char *onUpdateExecute,
const char *onOutdatedExecute,
int bDaemonized,
char *notifyClamd,
fc_ctx *fc_context)
{
fc_error_t ret;
fc_error_t status = FC_EARG;
time_t currtime;
char *dnsUpdateInfo = NULL;
char *newVersion = NULL;
uint32_t nUpdated = 0;
uint32_t nTotalUpdated = 0;
if (NULL == serverList) {
mprintf("!perform_database_update: Invalid arguments.\n");
goto done;
}
if (((NULL == databaseList) || (0 == nDatabases)) &&
((NULL == urlDatabaseList) || (0 == nUrlDatabases))) {
mprintf("!perform_database_update: No databases requested.\n");
goto done;
}
time(&currtime);
logg("ClamAV update process started at %s", ctime(&currtime));
if (bPrune) {
/*
* Prune database directory of official databases
* that are no longer available or no longer desired.
*/
(void)fc_prune_database_directory(databaseList, nDatabases);
}
/*
* Query DNS (if enabled) to get Update Info.
*/
(void)fc_dns_query_update_info(dnsUpdateInfoServer, &dnsUpdateInfo, &newVersion);
if ((NULL != databaseList) && (0 < nDatabases)) {
/*
* Download/update the desired official databases.
*/
ret = fc_update_databases(
databaseList,
nDatabases,
serverList,
nServers,
bPrivateMirror,
dnsUpdateInfo,
bScriptedUpdates,
(void *)fc_context,
&nUpdated);
if (FC_SUCCESS != ret) {
logg("!Database update process failed: %s (%d)\n", fc_strerror(ret), ret);
status = ret;
goto done;
}
nTotalUpdated += nUpdated;
}
if ((NULL != urlDatabaseList) && (0 < nUrlDatabases)) {
/*
* Download/update the desired unofficial / URL-based databases.
*/
ret = fc_download_url_databases(
urlDatabaseList,
nUrlDatabases,
(void *)fc_context,
&nUpdated);
if (FC_SUCCESS != ret) {
logg("!Database update process failed: %s (%d)\n", fc_strerror(ret), ret);
status = ret;
goto done;
}
nTotalUpdated += nUpdated;
logg("*Database update completed successfully.\n");
}
if (0 < nTotalUpdated) {
#ifdef BUILD_CLAMD
if (NULL != notifyClamd)
notify(notifyClamd);
#endif
if (NULL != onUpdateExecute) {
execute("OnUpdateExecute", onUpdateExecute, bDaemonized);
}
}
if ((NULL != newVersion) && (NULL != onOutdatedExecute)) {
executeIfNewVersion(onOutdatedExecute, newVersion, bDaemonized);
}
status = FC_SUCCESS;
done:
if (NULL != dnsUpdateInfo) {
free(dnsUpdateInfo);
}
if (NULL != newVersion) {
free(newVersion);
}
return status;
}
int main(int argc, char **argv)
{
fc_error_t ret;
fc_error_t status = FC_ECONNECTION;
char *cfgfile = NULL;
const char *arg = NULL;
struct optstruct *opts = NULL;
const struct optstruct *opt;
char **serverList = NULL;
uint32_t nServers = 0;
int bPrivate = 0;
const char *dnsUpdateInfoServer = NULL;
char **databaseList = NULL;
uint32_t nDatabases = 0;
char **urlDatabaseList = NULL;
uint32_t nUrlDatabases = 0;
int bPrune = 1;
fc_ctx fc_context = {0};
#ifndef _WIN32
struct sigaction sigact;
struct sigaction oldact;
#endif
int i;
if (check_flevel())
exit(FC_EINIT);
if ((opts = optparse(NULL, argc, argv, 1, OPT_FRESHCLAM, 0, NULL)) == NULL) {
mprintf("!Can't parse command line options\n");
status = FC_EINIT;
goto done;
}
if (optget(opts, "help")->enabled) {
help();
status = FC_SUCCESS;
goto done;
}
/* check foreground option from command line to override config file */
for (i = 0; i < argc; i += 1) {
if ((memcmp(argv[i], "--foreground", 12) == 0) || (memcmp(argv[i], "-F", 2) == 0)) {
/* found */
break;
}
}
/* If --foreground options was found in command line arguments,
get the value and set it. */
if (i < argc) {
if (optget(opts, "Foreground")->enabled) {
g_foreground = 1;
} else {
g_foreground = 0;
}
}
/*
* Parse the config file.
*/
cfgfile = cli_strdup(optget(opts, "config-file")->strarg);
if ((opts = optparse(cfgfile, 0, NULL, 1, OPT_FRESHCLAM, 0, opts)) == NULL) {
fprintf(stderr, "ERROR: Can't open/parse the config file %s\n", cfgfile);
status = FC_EINIT;
goto done;
}
/*
* Handle options that immediately exit.
*/
if (optget(opts, "version")->enabled) {
print_version(optget(opts, "DatabaseDirectory")->strarg);
status = FC_SUCCESS;
goto done;
}
if (optget(opts, "list-mirrors")->enabled) {
mprintf("^Deprecated option --list-mirrors. Individual mirrors are no longer tracked, as official signature distribution is now done through the CloudFlare CDN.\n");
status = FC_SUCCESS;
goto done;
}
/*
* Collect list of database servers from DatabaseMirror(s) or PrivateMirror(s).
*/
if (FC_SUCCESS != (ret = get_database_server_list(opts, &serverList, &nServers, &bPrivate))) {
mprintf("!Unable to find DatabaseMirror or PrivateMirror option(s) that specify database server FQDNs.\n");
status = ret;
goto done;
}
if (optget(opts, "update-db")->enabled) {
/*
* Prep for specific database updates.
*/
char **specificDatabaseList = NULL;
uint32_t nSpecificDatabases = 0;
int bCustom = 0;
/* Don't prune the database directory if only specific dabases were requested from the command line. */
bPrune = 0;
/*
* Get list of specific databases from command line args.
*/
if (FC_SUCCESS != (ret = get_string_list(optget(opts, "update-db"), &specificDatabaseList, &nSpecificDatabases))) {
mprintf("!Error when attempting to read ExtraDatabase entries.\n");
status = ret;
goto done;
}
/*
* Select specific databases from official lists.
*/
if (FC_SUCCESS != (ret = select_specific_databases(
specificDatabaseList,
nSpecificDatabases,
&databaseList,
&nDatabases,
&bCustom))) {
free_string_list(specificDatabaseList, nSpecificDatabases);
specificDatabaseList = NULL;
mprintf("!Failed to select specific databases from available official databases.\n");
status = ret;
goto done;
}
free_string_list(specificDatabaseList, nSpecificDatabases);
specificDatabaseList = NULL;
if (bCustom) {
/*
* Collect list of "custom"/unofficial URL-based databases.
*/
if (FC_SUCCESS != (ret = get_string_list(optget(opts, "DatabaseCustomURL"), &urlDatabaseList, &nUrlDatabases))) {
mprintf("!Error when attempting to read ExcludeDatabase entries.\n");
status = ret;
goto done;
}
if ((NULL == urlDatabaseList) || (0 == nUrlDatabases)) {
mprintf("!--update-db=custom requires at least one DatabaseCustomURL in freshclam.conf\n");
status = FC_ECONFIG;
goto done;
}
}
} else {
/*
* Prep for standard database updates.
*/
char **optInList = NULL;
uint32_t nOptIns = 0;
char **optOutList = NULL;
uint32_t nOptOuts = 0;
/*
* Collect list of database opt-ins.
*/
if (FC_SUCCESS != (ret = get_string_list(optget(opts, "ExtraDatabase"), &optInList, &nOptIns))) {
mprintf("!Error when attempting to read ExtraDatabase entries.\n");
status = ret;
goto done;
}
if (optget(opts, "SafeBrowsing")->enabled) {
if (FC_SUCCESS != (ret = string_list_add("safebrowsing", &optInList, &nOptIns))) {
free_string_list(optInList, nOptIns);
optInList = NULL;
mprintf("!Failed to add safebrowsing to list of opt-in databases.\n");
status = ret;
goto done; |
6df88f29 |
} |
694e7882 |
} |
6df88f29 |
|
694e7882 |
/*
* Collect list of database opt-outs.
*/
if (FC_SUCCESS != (ret = get_string_list(optget(opts, "ExcludeDatabase"), &optOutList, &nOptOuts))) {
free_string_list(optInList, nOptIns);
optInList = NULL;
mprintf("!Error when attempting to read ExcludeDatabase entries.\n");
status = ret;
goto done;
}
if (!optget(opts, "Bytecode")->enabled) {
if (FC_SUCCESS != (ret = string_list_add("bytecode", &optOutList, &nOptOuts))) {
free_string_list(optOutList, nOptOuts);
optInList = NULL;
mprintf("!Failed to add bytecode to list of opt-out databases.\n");
status = ret;
goto done;
}
}
/*
* Select databases from official lists using opt-ins and opt-outs.
*/
if (FC_SUCCESS != (ret = select_from_official_databases(
optInList,
nOptIns,
optOutList,
nOptOuts,
&databaseList,
&nDatabases))) {
free_string_list(optInList, nOptIns);
optInList = NULL;
free_string_list(optOutList, nOptOuts);
optOutList = NULL;
mprintf("!Failed to select databases from list of official databases.\n");
status = ret;
goto done;
}
free_string_list(optInList, nOptIns);
optInList = NULL;
free_string_list(optOutList, nOptOuts);
optOutList = NULL;
/*
* Collect list of "custom"/unofficial URL-based databases.
*/
if (FC_SUCCESS != (ret = get_string_list(optget(opts, "DatabaseCustomURL"), &urlDatabaseList, &nUrlDatabases))) {
mprintf("!Error when attempting to read ExcludeDatabase entries.\n");
status = ret;
goto done; |
6df88f29 |
} |
694e7882 |
} |
6df88f29 |
|
694e7882 |
fc_context.bTestDatabases = optget(opts, "TestDatabases")->enabled;
fc_context.bBytecodeEnabled = optget(opts, "Bytecode")->enabled; |
6df88f29 |
|
694e7882 |
/*
* Initialize libraries and configuration options.
*/
if (FC_SUCCESS != initialize(opts)) {
mprintf("!Initialization error!\n");
status = FC_EINIT;
goto done; |
cc590e07 |
}
|
694e7882 |
if (!optget(opts, "no-dns")->enabled && optget(opts, "DNSDatabaseInfo")->enabled) {
dnsUpdateInfoServer = optget(opts, "DNSDatabaseInfo")->strarg;
} |
af38c8ae |
#ifdef _WIN32 |
288057e9 |
signal(SIGINT, sighandler); |
af38c8ae |
#else |
288057e9 |
memset(&sigact, 0, sizeof(struct sigaction)); |
af38c8ae |
sigact.sa_handler = sighandler; |
288057e9 |
sigaction(SIGINT, &sigact, NULL);
sigaction(SIGPIPE, &sigact, NULL); |
af38c8ae |
#endif |
694e7882 |
if (!optget(opts, "daemon")->enabled) {
/*
* Daemon mode not enabled.
* Just update and exit.
*/
ret = perform_database_update(
databaseList,
nDatabases,
urlDatabaseList,
nUrlDatabases,
serverList,
nServers,
bPrivate,
bPrivate ? NULL : dnsUpdateInfoServer,
bPrivate ? 0 : optget(opts, "ScriptedUpdates")->enabled,
bPrune,
optget(opts, "OnUpdateExecute")->enabled ? optget(opts, "OnUpdateExecute")->strarg : NULL,
optget(opts, "OnOutdatedExecute")->enabled ? optget(opts, "OnUpdateExecute")->strarg : NULL,
optget(opts, "daemon")->enabled,
optget(opts, "NotifyClamd")->active ? optget(opts, "NotifyClamd")->strarg : NULL,
&fc_context);
if (FC_SUCCESS != ret) {
logg("!Update failed.\n");
status = ret;
goto done;
}
} else {
/*
* Daemon mode enabled.
* Keep running after update.
*/ |
6df88f29 |
int bigsleep, checks; |
288057e9 |
#ifndef _WIN32 |
6df88f29 |
time_t now, wakeup; |
e3aaff8e |
|
288057e9 |
sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGHUP, &sigact, NULL);
sigaction(SIGCHLD, &sigact, NULL); |
97eb9786 |
#endif |
af22ece1 |
|
694e7882 |
/*
* Determine sleep time based on # of checks per day.
* If HTTP is used instead of DNS to check for updates,
* limit the # of checks to 50 per day to restrict bandwidth usage.
*/ |
288057e9 |
checks = optget(opts, "Checks")->numarg; |
e3aaff8e |
|
288057e9 |
if (checks <= 0) {
logg("^Number of checks must be a positive integer.\n"); |
694e7882 |
status = FC_ECONFIG;
goto done; |
6df88f29 |
} |
e3aaff8e |
|
288057e9 |
if (!optget(opts, "DNSDatabaseInfo")->enabled || optget(opts, "no-dns")->enabled) {
if (checks > 50) {
logg("^Number of checks must be between 1 and 50.\n"); |
694e7882 |
status = FC_ECONFIG;
goto done; |
6df88f29 |
}
} |
3e92581e |
|
6df88f29 |
bigsleep = 24 * 3600 / checks; |
5951212c |
|
694e7882 |
/*
* If not set to foreground mode (and not Windows),
* daemonize and run in the background.
*/ |
b2354dc1 |
#ifndef _WIN32 |
b68375fd |
/* fork into background */ |
694e7882 |
if (g_foreground == 0) { |
288057e9 |
if (daemonize() == -1) {
logg("!daemonize() failed\n"); |
694e7882 |
status = FC_EFAILEDUPDATE;
goto done; |
6df88f29 |
}
mprintf_disabled = 1; |
0ae41a2d |
} |
b500915b |
#endif |
5951212c |
|
694e7882 |
/* Write PID of daemon process to pidfile. */ |
288057e9 |
if ((opt = optget(opts, "PidFile"))->enabled) { |
694e7882 |
g_pidfile = opt->strarg;
writepid(g_pidfile); |
6df88f29 |
} |
cc71d7c2 |
|
694e7882 |
g_active_children = 0;
logg("#freshclam daemon %s (OS: " TARGET_OS_TYPE ", ARCH: " TARGET_ARCH_TYPE ", CPU: " TARGET_CPU_TYPE ")\n", get_version());
while (!g_terminate) {
ret = perform_database_update(
databaseList,
nDatabases,
urlDatabaseList,
nUrlDatabases,
serverList,
nServers,
bPrivate,
bPrivate ? NULL : dnsUpdateInfoServer,
bPrivate ? 0 : optget(opts, "ScriptedUpdates")->enabled,
bPrune,
optget(opts, "OnUpdateExecute")->enabled ? optget(opts, "OnUpdateExecute")->strarg : NULL,
optget(opts, "OnOutdatedExecute")->enabled ? optget(opts, "OnUpdateExecute")->strarg : NULL,
optget(opts, "daemon")->enabled,
optget(opts, "NotifyClamd")->active ? optget(opts, "NotifyClamd")->strarg : NULL,
&fc_context);
if (FC_SUCCESS != ret) {
logg("!Update failed.\n");
} |
e3aaff8e |
|
694e7882 |
#ifndef _WIN32
/* Void the current alarm. */
alarm(0);
#endif |
af22ece1 |
|
288057e9 |
if (ret > 1) {
if ((opt = optget(opts, "OnErrorExecute"))->enabled) |
6df88f29 |
arg = opt->strarg; |
af22ece1 |
|
6df88f29 |
if (arg) |
694e7882 |
execute("OnErrorExecute", arg, optget(opts, "daemon")->enabled); |
770fb166 |
|
6df88f29 |
arg = NULL;
} |
e3aaff8e |
|
288057e9 |
logg("#--------------------------------------\n");
#ifdef SIGALRM
sigaction(SIGALRM, &sigact, &oldact); |
97eb9786 |
#endif |
288057e9 |
#ifdef SIGUSR1
sigaction(SIGUSR1, &sigact, &oldact); |
97eb9786 |
#endif
|
288057e9 |
#ifdef _WIN32
sleep(bigsleep); |
6df88f29 |
#else |
694e7882 |
/* Set a new alarm. */ |
288057e9 |
time(&wakeup); |
6df88f29 |
wakeup += bigsleep; |
288057e9 |
alarm(bigsleep);
do {
pause();
time(&now); |
694e7882 |
} while (!g_terminate && (now < wakeup)); |
288057e9 |
|
694e7882 |
if (g_terminate == -1) { |
288057e9 |
logg("Received signal: wake up\n"); |
694e7882 |
g_terminate = 0;
} else if (g_terminate == -2) { |
288057e9 |
logg("Received signal: re-opening log file\n"); |
694e7882 |
g_terminate = 0; |
288057e9 |
logg_close(); |
6df88f29 |
} |
97eb9786 |
#endif |
e3eaadd0 |
|
288057e9 |
#ifdef SIGALRM
sigaction(SIGALRM, &oldact, NULL); |
97eb9786 |
#endif |
288057e9 |
#ifdef SIGUSR1
sigaction(SIGUSR1, &oldact, NULL); |
6df88f29 |
#endif
} |
af22ece1 |
} |
770fb166 |
|
694e7882 |
status = FC_SUCCESS; |
e3aaff8e |
|
694e7882 |
done: |
97eb9786 |
|
694e7882 |
if ((status > FC_UPTODATE) && (NULL != opts)) {
if ((opt = optget(opts, "OnErrorExecute"))->enabled)
execute("OnErrorExecute", opt->strarg, optget(opts, "daemon")->enabled);
} |
f010300a |
|
694e7882 |
logg_close(); |
dbc6d4b2 |
|
694e7882 |
if (g_pidfile) {
unlink(g_pidfile);
} |
b7485a22 |
|
694e7882 |
if (NULL != databaseList) {
free_string_list(databaseList, nDatabases);
}
if (NULL != urlDatabaseList) {
free_string_list(urlDatabaseList, nUrlDatabases);
}
if (NULL != opts) {
optfree(opts);
}
if (NULL != cfgfile) {
free(cfgfile);
} |
b7485a22 |
|
694e7882 |
/* Cleanup libfreshclam */
fc_cleanup(); |
b7485a22 |
|
694e7882 |
/* Remove temp directory */ |
1e3e2b76 |
if (*g_freshclamTempDirectory) {
cli_rmdirs(g_freshclamTempDirectory); |
694e7882 |
} |
b7485a22 |
|
694e7882 |
if ((FC_UPTODATE == status) || (FC_SUCCESS == status)) { |
b7485a22 |
return 0; |
694e7882 |
} |
b7485a22 |
|
694e7882 |
return (int)status; |
b7485a22 |
} |