clamconf/clamconf.c
0d06ef19
 /*
e1cbc270
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
265427ce
  *  Copyright (C) 2009-2013 Sourcefire, Inc.
e1cbc270
  *
0d06ef19
  *  Author: Tomasz Kojm <tkojm@clamav.net>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  *
  *  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
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
  */
 
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
 #include <stdio.h>
 #include <string.h>
b3acc382
 #ifdef HAVE_UNISTD_H
0d06ef19
 #include <unistd.h>
b3acc382
 #endif
5bdf0b2a
 #include <time.h>
9b2f14fa
 #ifdef HAVE_UNAME_SYSCALL
 #include <sys/utsname.h>
 #endif
 #include <zlib.h>
4dccd075
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <dirent.h>
0d06ef19
 
 #include "shared/optparser.h"
 #include "shared/misc.h"
 
048a88e6
 #include "clamav-config.h"
1d8a56d4
 #include "libclamav/str.h"
5bdf0b2a
 #include "libclamav/clamav.h"
 #include "libclamav/others.h"
4dccd075
 #include "libclamav/readdb.h"
2487a4a3
 #include "libclamav/bytecode.h"
ed6cd43e
 #include "libclamav/bytecode_detect.h"
9b2f14fa
 #include "target.h"
b02190b4
 #include "fpu.h"
1d8a56d4
 
e46b546c
 #ifndef _WIN32
 extern const struct clam_option *clam_options;
 #else
 __declspec(dllimport) extern const struct clam_option *clam_options;
 #endif
 
0d06ef19
 static struct _cfgfile {
     const char *name;
     int tool;
 } cfgfile[] = {
288057e9
     {"clamd.conf", OPT_CLAMD},
     {"freshclam.conf", OPT_FRESHCLAM},
     {"clamav-milter.conf", OPT_MILTER},
     {NULL, 0}};
0d06ef19
 
 static void printopts(struct optstruct *opts, int nondef)
 {
288057e9
     const struct optstruct *opt;
 
     while (opts) {
         if (!opts->name) {
             opts = opts->next;
             continue;
         }
         if (clam_options[opts->idx].owner & OPT_DEPRECATED) {
             if (opts->active)
                 printf("*** %s is DEPRECATED ***\n", opts->name);
             opts = opts->next;
             continue;
         }
         if (nondef && (opts->numarg == clam_options[opts->idx].numarg) && ((opts->strarg == clam_options[opts->idx].strarg) || (opts->strarg && clam_options[opts->idx].strarg && !strcmp(opts->strarg, clam_options[opts->idx].strarg)))) {
             opts = opts->next;
             continue;
         }
         if (!opts->enabled)
             printf("%s disabled\n", opts->name);
         else
             switch (clam_options[opts->idx].argtype) {
                 case CLOPT_TYPE_STRING:
                     printf("%s = \"%s\"", opts->name, opts->strarg);
                     opt = opts;
                     while ((opt = opt->nextarg))
                         printf(", \"%s\"", opt->strarg);
                     printf("\n");
                     break;
 
                 case CLOPT_TYPE_NUMBER:
                 case CLOPT_TYPE_SIZE:
                     printf("%s = \"%lld\"", opts->name, opts->numarg);
                     opt = opts;
                     while ((opt = opt->nextarg))
                         printf(", \"%lld\"", opt->numarg);
                     printf("\n");
                     break;
 
                 case CLOPT_TYPE_BOOL:
                     printf("%s = \"yes\"\n", opts->name);
                     break;
 
                 default:
                     printf("!!! %s: UNKNOWN INTERNAL TYPE !!!\n", opts->name);
             }
         opts = opts->next;
0d06ef19
     }
 }
 
1d8a56d4
 static int printconf(const char *name)
 {
288057e9
     int i, j, tool = 0, tokens_count;
     char buffer[1025];
     const char *tokens[128];
     const struct clam_option *cpt;
 
     for (i = 0; cfgfile[i].name; i++) {
         if (!strcmp(name, cfgfile[i].name)) {
             tool = cfgfile[i].tool;
             break;
         }
1d8a56d4
     }
288057e9
     if (!tool) {
         printf("ERROR: Unknown config file\nAvailable options:");
         for (i = 0; cfgfile[i].name; i++)
             printf(" %s", cfgfile[i].name);
         printf("\n");
         return 1;
1d8a56d4
     }
 
288057e9
     printf("##\n## %s - automatically generated by clamconf " VERSION "\n##\n", name);
1d8a56d4
     printf("\n# Comment out or remove the line below.\nExample\n");
 
288057e9
     for (i = 0; clam_options[i].owner; i++) {
         cpt = &clam_options[i];
         if (cpt->name && (cpt->owner & tool) && !(cpt->owner & OPT_DEPRECATED) && !(cpt->flags & 4)) {
             strncpy(buffer, cpt->description, sizeof(buffer) - 1);
             buffer[sizeof(buffer) - 1] = 0;
             tokens_count               = cli_strtokenize(buffer, '\n', 128, tokens);
             printf("\n");
             for (j = 0; j < tokens_count; j++)
                 printf("# %s\n", tokens[j]);
 
             switch (cpt->argtype) {
                 case CLOPT_TYPE_STRING:
                     if (cpt->strarg)
                         printf("# Default: %s\n", cpt->strarg);
                     else
                         printf("# Default: disabled\n");
                     break;
 
                 case CLOPT_TYPE_NUMBER:
                     if (cpt->numarg != -1)
                         printf("# Default: %lld\n", cpt->numarg);
                     else
                         printf("# Default: disabled\n");
                     break;
 
                 case CLOPT_TYPE_SIZE:
                     printf("# You may use 'M' or 'm' for megabytes (1M = 1m = 1048576 bytes)\n# and 'K' or 'k' for kilobytes (1K = 1k = 1024 bytes). To specify the size\n# in bytes just don't use modifiers.\n");
                     if (cpt->numarg != -1)
                         printf("# Default: %lld\n", cpt->numarg);
                     else
                         printf("# Default: disabled\n");
                     break;
 
                 case CLOPT_TYPE_BOOL:
                     if (cpt->numarg != -1)
                         printf("# Default: %s\n", cpt->numarg ? "yes" : "no");
                     else
                         printf("# Default: disabled\n");
                     break;
 
                 default:
                     printf("!!! %s: UNKNOWN INTERNAL TYPE !!!\n", cpt->name);
             }
 
             if (cpt->suggested && strchr(cpt->suggested, '\n')) {
                 strncpy(buffer, cpt->suggested, sizeof(buffer) - 1);
                 buffer[sizeof(buffer) - 1] = 0;
                 tokens_count               = cli_strtokenize(buffer, '\n', 128, tokens);
                 for (j = 0; j < tokens_count; j++)
                     printf("#%s %s\n", cpt->name, tokens[j]);
             } else {
                 printf("#%s %s\n", cpt->name, cpt->suggested ? cpt->suggested : "ARG");
             }
         }
1d8a56d4
     }
 
     return 0;
 }
 
0d06ef19
 static void help(void)
 {
     printf("\n");
e098cdc5
     printf("                       Clam AntiVirus: Configuration Tool %s\n", get_version());
964a1e73
     printf("           By The ClamAV Team: https://www.clamav.net/about.html#credits\n");
e1cbc270
     printf("           (C) 2019 Cisco Systems, Inc.\n");
e098cdc5
     printf("\n");
     printf("    --help                 -h         Show this help\n");
1d8a56d4
     printf("    --version              -V         Show version\n");
     printf("    --config-dir=DIR       -c DIR     Read configuration files from DIR\n");
     printf("    --non-default          -n         Only display non-default settings\n");
     printf("    --generate-config=NAME -g NAME    Generate example config file\n");
0d06ef19
     printf("\n");
     return;
 }
 
ed6cd43e
 static void print_platform(struct cli_environment *env)
9b2f14fa
 {
69184d40
     printf("\nPlatform information\n--------------------\n");
ed6cd43e
     printf("uname: %s %s %s %s\n",
288057e9
            env->sysname, env->release, env->version, env->machine);
ed6cd43e
 
288057e9
     printf("OS: " TARGET_OS_TYPE ", ARCH: " TARGET_ARCH_TYPE ", CPU: " TARGET_CPU_TYPE "\n");
9b2f14fa
 
 #ifdef C_LINUX
     if (!access("/usr/bin/lsb_release", X_OK)) {
288057e9
         fputs("Full OS version: ", stdout);
         fflush(stdout);
         if (system("/usr/bin/lsb_release -d -s") == -1) {
             perror("failed to determine");
         }
9b2f14fa
     }
 #else
     /* e.g. Solaris */
     if (!access("/etc/release", R_OK)) {
022677ab
         char buf[1024];
         FILE *f = fopen("/etc/release", "r");
 
         if (f) {
             fgets(buf, sizeof(buf), f);
             printf("Full OS version: %s", buf);
             fclose(f);
         }
9b2f14fa
     }
 #endif
 
e4fedabe
     if (strcmp(ZLIB_VERSION, zlibVersion()))
288057e9
         printf("WARNING: zlib version mismatch: %s (%s)\n", ZLIB_VERSION, zlibVersion());
9b2f14fa
 #ifdef ZLIB_VERNUM
69184d40
     printf("zlib version: %s (%s), compile flags: %02lx\n",
288057e9
            ZLIB_VERSION, zlibVersion(), zlibCompileFlags());
9b2f14fa
 #else
     /* old zlib w/o zlibCompileFlags() */
     printf("zlib version: %s (%s)\n",
288057e9
            ZLIB_VERSION, zlibVersion());
9b2f14fa
 #endif
9cc0b8c9
 
ed6cd43e
     if (env->triple[0])
288057e9
         printf("Triple: %s\n", env->triple);
88d54dcb
     if (env->cpu[0])
288057e9
         printf("CPU: %s, %s\n", env->cpu, env->big_endian ? "Big-endian" : "Little-endian");
ed6cd43e
     printf("platform id: 0x%08x%08x%08x\n",
288057e9
            env->platform_id_a,
            env->platform_id_b,
            env->platform_id_c);
9b2f14fa
 }
 
ed6cd43e
 static void print_build(struct cli_environment *env)
9b2f14fa
 {
ed6cd43e
     const char *name;
     const char *version = NULL;
9b2f14fa
     printf("\nBuild information\n-----------------\n");
     /* Try to print information about some commonly used compilers */
69650bea
 #ifdef __GNUC__
288057e9
     version = __VERSION__;
69650bea
 #endif
ed6cd43e
     switch (env->compiler) {
288057e9
         case compiler_gnuc:
             name = "GNU C";
             break;
         case compiler_clang:
             name = "Clang";
             break;
         case compiler_llvm:
             name = "LLVM-GCC";
             break;
         case compiler_intel:
             name = "Intel Compiler";
             break;
         case compiler_msc:
             name = "Microsoft Visual C++";
             break;
         case compiler_sun:
             name = "Sun studio";
             break;
         default:
             name = NULL;
ed6cd43e
     }
     if (name)
288057e9
         printf("%s: %s%s(%u.%u.%u)\n", name,
                version ? version : "",
                version ? " " : "",
                env->c_version >> 16,
                (env->c_version >> 8) & 0xff,
                (env->c_version) & 0xff);
2a7f1cda
     cli_printcxxver();
331cce0a
 #if defined(BUILD_CPPFLAGS) && defined(BUILD_CFLAGS) && defined(BUILD_CXXFLAGS) && defined(BUILD_LDFLAGS) && defined(BUILD_CONFIGURE_FLAGS)
9b2f14fa
     printf("CPPFLAGS: %s\nCFLAGS: %s\nCXXFLAGS: %s\nLDFLAGS: %s\nConfigure: %s\n",
288057e9
            BUILD_CPPFLAGS, BUILD_CFLAGS, BUILD_CXXFLAGS, BUILD_LDFLAGS,
            BUILD_CONFIGURE_FLAGS);
331cce0a
 #endif
ed6cd43e
     printf("sizeof(void*) = %d\n", env->sizeof_ptr);
     printf("Engine flevel: %d, dconf: %d\n",
288057e9
            env->functionality_level,
            env->dconf_level);
9b2f14fa
 }
 
4dccd075
 static void print_dbs(const char *dir)
 {
288057e9
     DIR *dd;
     struct dirent *dent;
     char *dbfile;
     unsigned int flevel = cl_retflevel(), cnt, sigs = 0;
     struct cl_cvd *cvd;
4dccd075
 
288057e9
     if ((dd = opendir(dir)) == NULL) {
4dccd075
         printf("print_dbs: Can't open directory %s\n", dir);
         return;
     }
 
288057e9
     while ((dent = readdir(dd))) {
         if (dent->d_ino) {
             if (CLI_DBEXT(dent->d_name)) {
                 dbfile = (char *)malloc(strlen(dent->d_name) + strlen(dir) + 2);
                 if (!dbfile) {
                     printf("print_dbs: Can't allocate memory for dbfile\n");
                     closedir(dd);
                     return;
                 }
                 sprintf(dbfile, "%s" PATHSEP "%s", dir, dent->d_name);
                 if (cli_strbcasestr(dbfile, ".cvd") || cli_strbcasestr(dbfile, ".cld")) {
                     cvd = cl_cvdhead(dbfile);
                     if (!cvd) {
                         printf("%s: Can't get information about the database\n", dbfile);
                     } else {
                         const time_t t = cvd->stime;
                         printf("%s: version %u, sigs: %u, built on %s", dent->d_name, cvd->version, cvd->sigs, ctime(&t));
                         sigs += cvd->sigs;
                         if (cvd->fl > flevel)
                             printf("%s: WARNING: This database requires f-level %u (current f-level: %u)\n", dent->d_name, cvd->fl, flevel);
                         cl_cvdfree(cvd);
                     }
                 } else if (cli_strbcasestr(dbfile, ".cbc")) {
                     printf("[3rd Party] %s: bytecode\n", dent->d_name);
                     sigs++;
                 } else {
                     cnt = countlines(dbfile);
                     printf("[3rd Party] %s: %u sig%c\n", dent->d_name, cnt, cnt > 1 ? 's' : ' ');
                     sigs += cnt;
                 }
                 free(dbfile);
             }
         }
4dccd075
     }
     closedir(dd);
     printf("Total number of signatures: %u\n", sigs);
 }
 
0d06ef19
 int main(int argc, char **argv)
 {
288057e9
     const char *dir;
     char path[512], dbdir[512], clamd_dbdir[512], *pt;
     struct optstruct *opts, *toolopts;
     const struct optstruct *opt;
     unsigned int i, j;
     struct cli_environment env;
0d06ef19
 
     opts = optparse(NULL, argc, argv, 1, OPT_CLAMCONF, 0, NULL);
288057e9
     if (!opts) {
         printf("ERROR: Can't parse command line options\n");
         return 1;
0d06ef19
     }
 
288057e9
     if (optget(opts, "help")->enabled) {
         help();
         optfree(opts);
         return 0;
0d06ef19
     }
 
288057e9
     if (optget(opts, "version")->enabled) {
         printf("Clam AntiVirus Configuration Tool %s\n", get_version());
         optfree(opts);
         return 0;
a128eb80
     }
 
288057e9
     if ((opt = optget(opts, "generate-config"))->enabled) {
         printconf(opt->strarg);
         optfree(opts);
         return 0;
1d8a56d4
     }
 
288057e9
     dbdir[0]       = 0;
53814f19
     clamd_dbdir[0] = 0;
288057e9
     dir            = optget(opts, "config-dir")->strarg;
0d06ef19
     printf("Checking configuration files in %s\n", dir);
288057e9
     for (i = 0; cfgfile[i].name; i++) {
         snprintf(path, sizeof(path), "%s" PATHSEP "%s", dir, cfgfile[i].name);
         path[511] = 0;
         if (access(path, R_OK)) {
             printf("\n%s not found\n", cfgfile[i].name);
             continue;
         }
         printf("\nConfig file: %s\n", cfgfile[i].name);
         for (j = 0; j < strlen(cfgfile[i].name) + 13; j++)
             printf("-");
         printf("\n");
         toolopts = optparse(path, 0, NULL, 1, cfgfile[i].tool | OPT_DEPRECATED, 0, NULL);
         if (!toolopts)
             continue;
         printopts(toolopts, optget(opts, "non-default")->enabled);
         if (cfgfile[i].tool == OPT_FRESHCLAM) {
             opt = optget(toolopts, "DatabaseDirectory");
             strncpy(dbdir, opt->strarg, sizeof(dbdir));
             dbdir[sizeof(dbdir) - 1] = 0;
         } else if (cfgfile[i].tool == OPT_CLAMD) {
             opt = optget(toolopts, "DatabaseDirectory");
             strncpy(clamd_dbdir, opt->strarg, sizeof(clamd_dbdir));
             clamd_dbdir[sizeof(clamd_dbdir) - 1] = 0;
         }
         optfree(toolopts);
0d06ef19
     }
     optfree(opts);
5bdf0b2a
 
     printf("\nSoftware settings\n-----------------\n");
     printf("Version: %s\n", cl_retver());
288057e9
     if (strcmp(cl_retver(), get_version()))
         printf("WARNING: Version mismatch: libclamav=%s, clamconf=%s\n", cl_retver(), get_version());
5bdf0b2a
     cl_init(CL_INIT_DEFAULT);
     printf("Optional features supported: ");
 #ifdef USE_MPOOL
288057e9
     printf("MEMPOOL ");
5bdf0b2a
 #endif
 #ifdef SUPPORT_IPv6
288057e9
     printf("IPv6 ");
5bdf0b2a
 #endif
 #ifdef CLAMUKO
288057e9
     printf("CLAMUKO ");
5bdf0b2a
 #endif
 #ifdef C_BIGSTACK
288057e9
     printf("BIGSTACK ");
5bdf0b2a
 #endif
 #ifdef FRESHCLAM_DNS_FIX
288057e9
     printf("FRESHCLAM_DNS_FIX ");
5bdf0b2a
 #endif
48bb121c
 #ifndef _WIN32
288057e9
     if (get_fpu_endian() != FPU_ENDIAN_UNKNOWN)
48bb121c
 #endif
288057e9
         printf("AUTOIT_EA06 ");
5bdf0b2a
 #ifdef HAVE_BZLIB_H
288057e9
     printf("BZIP2 ");
5bdf0b2a
 #endif
c648e6b4
 
9cc0b8c9
 #ifdef HAVE_LIBXML2
288057e9
     printf("LIBXML2 ");
9cc0b8c9
 #endif
17d741d8
 #ifdef HAVE_PCRE
41d46330
 #if USING_PCRE2
288057e9
     printf("PCRE2 ");
41d46330
 #else
288057e9
     printf("PCRE ");
17d741d8
 #endif
41d46330
 #endif
ee064318
 #ifdef HAVE_ICONV
288057e9
     printf("ICONV ");
ee064318
 #endif
9cc0b8c9
 #ifdef HAVE_JSON
288057e9
     printf("JSON ");
9cc0b8c9
 #endif
288057e9
     if (have_rar)
         printf("RAR ");
2487a4a3
     if (have_clamjit)
288057e9
         printf("JIT");
5bdf0b2a
     printf("\n");
 
288057e9
     if (!strlen(dbdir)) {
         pt = freshdbdir();
         if (pt) {
             strncpy(dbdir, pt, sizeof(dbdir));
             free(pt);
         } else {
             strncpy(dbdir, DATADIR, sizeof(dbdir));
         }
         dbdir[sizeof(dbdir) - 1] = 0;
5bdf0b2a
     }
4dccd075
 
     printf("\nDatabase information\n--------------------\n");
5bdf0b2a
     printf("Database directory: %s\n", dbdir);
288057e9
     if (strcmp(dbdir, clamd_dbdir))
         printf("WARNING: freshclam.conf and clamd.conf point to different database directories\n");
4dccd075
     print_dbs(dbdir);
 
ed6cd43e
     cli_detect_environment(&env);
     print_platform(&env);
     print_build(&env);
694e7882
 
0d06ef19
     return 0;
 }