libclamav/cvd.c
8139fd99
 /*
e1cbc270
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2007-2013 Sourcefire, Inc.
8139fd99
  *
2023340a
  *  Authors: Tomasz Kojm
544fa973
  *
6289eda8
  *  Summary: Code to parse Clamav CVD database format.
544fa973
  *
6289eda8
  *  Acknowledgements: ClamAV untar code is based on a public domain minitar utility
  *                    by Charles G. Waldman.
8139fd99
  *
  *  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.
8139fd99
  *
  *  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.
8139fd99
  */
 
6d6e8271
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
8139fd99
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
288057e9
 #ifdef HAVE_UNISTD_H
5ff81952
 #include <unistd.h>
b58fdfc2
 #endif
 #include "zlib.h"
c48c9d2b
 #include <time.h>
b58fdfc2
 #include <errno.h>
8139fd99
 
 #include "clamav.h"
6a2532ca
 #include "others.h"
 #include "dsig.h"
e4ae7726
 #include "str.h"
079229d6
 #include "cvd.h"
056d95dc
 #include "readdb.h"
a6a98456
 #include "default.h"
8139fd99
 
 #define TAR_BLOCKSIZE 512
 
bebd86a6
 static void cli_untgz_cleanup(char *path, gzFile infile, FILE *outfile, int fdd)
 {
cd94be7a
     UNUSEDPARAM(fdd);
bebd86a6
     cli_dbgmsg("in cli_untgz_cleanup()\n");
     if (path != NULL)
288057e9
         free(path);
     if (infile != NULL)
         gzclose(infile);
bebd86a6
     if (outfile != NULL)
         fclose(outfile);
 }
 
afff80ef
 static int cli_untgz(int fd, const char *destdir)
8139fd99
 {
288057e9
     char *path, osize[13], name[101], type;
     char block[TAR_BLOCKSIZE];
     int nbytes, nread, nwritten, in_block = 0, fdd = -1;
     unsigned int size, pathlen = strlen(destdir) + 100 + 5;
     FILE *outfile = NULL;
     STATBUF foo;
     gzFile infile = NULL;
a7ac5978
 
8139fd99
     cli_dbgmsg("in cli_untgz()\n");
 
288057e9
     if ((fdd = dup(fd)) == -1) {
         cli_errmsg("cli_untgz: Can't duplicate descriptor %d\n", fd);
         return -1;
     }
 
     if ((infile = gzdopen(fdd, "rb")) == NULL) {
         cli_errmsg("cli_untgz: Can't gzdopen() descriptor %d, errno = %d\n", fdd, errno);
         if (FSTAT(fdd, &foo) == 0)
             close(fdd);
         return -1;
     }
 
     path = (char *)cli_calloc(sizeof(char), pathlen);
     if (!path) {
         cli_errmsg("cli_untgz: Can't allocate memory for path\n");
         cli_untgz_cleanup(NULL, infile, NULL, fdd);
         return -1;
     }
 
     while (1) {
 
         nread = gzread(infile, block, TAR_BLOCKSIZE);
 
         if (!in_block && !nread)
             break;
 
         if (nread != TAR_BLOCKSIZE) {
             cli_errmsg("cli_untgz: Incomplete block read\n");
             cli_untgz_cleanup(path, infile, outfile, fdd);
             return -1;
         }
 
         if (!in_block) {
             if (block[0] == '\0') /* We're done */
                 break;
 
             strncpy(name, block, 100);
             name[100] = '\0';
 
             if (strchr(name, '/')) {
                 cli_errmsg("cli_untgz: Slash separators are not allowed in CVD\n");
                 cli_untgz_cleanup(path, infile, outfile, fdd);
                 return -1;
             }
 
             snprintf(path, pathlen, "%s" PATHSEP "%s", destdir, name);
             cli_dbgmsg("cli_untgz: Unpacking %s\n", path);
             type = block[156];
 
             switch (type) {
                 case '0':
                 case '\0':
                     break;
                 case '5':
                     cli_errmsg("cli_untgz: Directories are not supported in CVD\n");
                     cli_untgz_cleanup(path, infile, outfile, fdd);
                     return -1;
                 default:
                     cli_errmsg("cli_untgz: Unknown type flag '%c'\n", type);
                     cli_untgz_cleanup(path, infile, outfile, fdd);
                     return -1;
             }
 
             if (outfile) {
                 if (fclose(outfile)) {
                     cli_errmsg("cli_untgz: Cannot close file %s\n", path);
                     outfile = NULL;
                     cli_untgz_cleanup(path, infile, outfile, fdd);
                     return -1;
                 }
                 outfile = NULL;
             }
 
             if (!(outfile = fopen(path, "wb"))) {
                 cli_errmsg("cli_untgz: Cannot create file %s\n", path);
                 cli_untgz_cleanup(path, infile, outfile, fdd);
                 return -1;
             }
 
             strncpy(osize, block + 124, 12);
             osize[12] = '\0';
 
             if ((sscanf(osize, "%o", &size)) == 0) {
                 cli_errmsg("cli_untgz: Invalid size in header\n");
                 cli_untgz_cleanup(path, infile, outfile, fdd);
                 return -1;
             }
 
             if (size > 0)
                 in_block = 1;
 
         } else { /* write or continue writing file contents */
             nbytes   = size > TAR_BLOCKSIZE ? TAR_BLOCKSIZE : size;
             nwritten = fwrite(block, 1, nbytes, outfile);
 
             if (nwritten != nbytes) {
                 cli_errmsg("cli_untgz: Wrote %d instead of %d (%s)\n", nwritten, nbytes, path);
                 cli_untgz_cleanup(path, infile, outfile, fdd);
                 return -1;
             }
 
             size -= nbytes;
             if (size == 0)
                 in_block = 0;
         }
8139fd99
     }
 
bebd86a6
     cli_untgz_cleanup(path, infile, outfile, fdd);
8139fd99
     return 0;
 }
 
bebd86a6
 static void cli_tgzload_cleanup(int comp, struct cli_dbio *dbio, int fdd)
 {
cd94be7a
     UNUSEDPARAM(fdd);
bebd86a6
     cli_dbgmsg("in cli_tgzload_cleanup()\n");
288057e9
     if (comp) {
bebd86a6
         gzclose(dbio->gzs);
         dbio->gzs = NULL;
288057e9
     } else {
bebd86a6
         fclose(dbio->fs);
         dbio->fs = NULL;
     }
288057e9
     if (dbio->buf != NULL) {
bebd86a6
         free(dbio->buf);
         dbio->buf = NULL;
     }
a1cbd793
 
     if (dbio->hashctx) {
da6e06dd
         cl_hash_destroy(dbio->hashctx);
a1cbd793
         dbio->hashctx = NULL;
     }
bebd86a6
 }
 
ace26bfe
 static int cli_tgzload(int fd, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, struct cli_dbinfo *dbinfo)
056d95dc
 {
288057e9
     char osize[13], name[101];
     char block[TAR_BLOCKSIZE];
     int nread, fdd, ret;
     unsigned int type, size, pad, compr = 1;
     off_t off;
     struct cli_dbinfo *db;
     char hash[32];
056d95dc
 
9d193ff2
     cli_dbgmsg("in cli_tgzload()\n");
056d95dc
 
288057e9
     if (lseek(fd, 512, SEEK_SET) < 0) {
0944b8ee
         return CL_ESEEK;
     }
 
288057e9
     if (cli_readn(fd, block, 7) != 7)
         return CL_EFORMAT; /* truncated file? */
4d7a1184
 
288057e9
     if (!strncmp(block, "COPYING", 7))
         compr = 0;
4d7a1184
 
288057e9
     if (lseek(fd, 512, SEEK_SET) < 0) {
0944b8ee
         return CL_ESEEK;
     }
e8ae4fae
 
288057e9
     if ((fdd = dup(fd)) == -1) {
         cli_errmsg("cli_tgzload: Can't duplicate descriptor %d\n", fd);
         return CL_EDUP;
056d95dc
     }
 
288057e9
     if (compr) {
         if ((dbio->gzs = gzdopen(fdd, "rb")) == NULL) {
             cli_errmsg("cli_tgzload: Can't gzdopen() descriptor %d, errno = %d\n", fdd, errno);
             if (fdd > -1)
                 close(fdd);
             return CL_EOPEN;
         }
         dbio->fs = NULL;
e8ae4fae
     } else {
288057e9
         if ((dbio->fs = fdopen(fdd, "rb")) == NULL) {
             cli_errmsg("cli_tgzload: Can't fdopen() descriptor %d, errno = %d\n", fdd, errno);
             if (fdd > -1)
                 close(fdd);
             return CL_EOPEN;
         }
         dbio->gzs = NULL;
056d95dc
     }
 
ace26bfe
     dbio->bufsize = CLI_DEFAULT_DBIO_BUFSIZE;
288057e9
     dbio->buf     = cli_malloc(dbio->bufsize);
     if (!dbio->buf) {
         cli_errmsg("cli_tgzload: Can't allocate memory for dbio->buf\n");
         cli_tgzload_cleanup(compr, dbio, fdd);
         return CL_EMALFDB;
a6a98456
     }
288057e9
     dbio->bufpt  = NULL;
ace26bfe
     dbio->usebuf = 1;
     dbio->readpt = dbio->buf;
a6a98456
 
288057e9
     while (1) {
 
         if (compr)
             nread = gzread(dbio->gzs, block, TAR_BLOCKSIZE);
         else
             nread = fread(block, 1, TAR_BLOCKSIZE, dbio->fs);
 
         if (!nread)
             break;
 
         if (nread != TAR_BLOCKSIZE) {
             cli_errmsg("cli_tgzload: Incomplete block read\n");
a1cbd793
             cli_tgzload_cleanup(compr, dbio, fdd);
             return CL_EMALFDB;
         }
288057e9
 
         if (block[0] == '\0') /* We're done */
             break;
 
         strncpy(name, block, 100);
         name[100] = '\0';
 
         if (strchr(name, '/')) {
             cli_errmsg("cli_tgzload: Slash separators are not allowed in CVD\n");
             cli_tgzload_cleanup(compr, dbio, fdd);
             return CL_EMALFDB;
         }
 
         type = block[156];
 
         switch (type) {
             case '0':
             case '\0':
                 break;
             case '5':
                 cli_errmsg("cli_tgzload: Directories are not supported in CVD\n");
                 cli_tgzload_cleanup(compr, dbio, fdd);
                 return CL_EMALFDB;
             default:
                 cli_errmsg("cli_tgzload: Unknown type flag '%c'\n", type);
                 cli_tgzload_cleanup(compr, dbio, fdd);
                 return CL_EMALFDB;
         }
 
         strncpy(osize, block + 124, 12);
         osize[12] = '\0';
 
         if ((sscanf(osize, "%o", &size)) == 0) {
             cli_errmsg("cli_tgzload: Invalid size in header\n");
             cli_tgzload_cleanup(compr, dbio, fdd);
             return CL_EMALFDB;
         }
         dbio->size     = size;
         dbio->readsize = dbio->size < dbio->bufsize ? dbio->size : dbio->bufsize - 1;
         dbio->bufpt    = NULL;
         dbio->readpt   = dbio->buf;
         if (!(dbio->hashctx)) {
da6e06dd
             dbio->hashctx = cl_hash_init("sha256");
             if (!(dbio->hashctx)) {
                 cli_tgzload_cleanup(compr, dbio, fdd);
                 return CL_EMALFDB;
             }
288057e9
         }
         dbio->bread = 0;
 
         /* cli_dbgmsg("cli_tgzload: Loading %s, size: %u\n", name, size); */
         if (compr)
             off = (off_t)gzseek(dbio->gzs, 0, SEEK_CUR);
         else
             off = ftell(dbio->fs);
 
afe940da
         if ((!dbinfo && cli_strbcasestr(name, ".info")) || (dbinfo && CLI_DBEXT(name))) {
288057e9
             ret = cli_load(name, engine, signo, options, dbio);
             if (ret) {
                 cli_errmsg("cli_tgzload: Can't load %s\n", name);
                 cli_tgzload_cleanup(compr, dbio, fdd);
                 return CL_EMALFDB;
             }
             if (!dbinfo) {
                 cli_tgzload_cleanup(compr, dbio, fdd);
                 return CL_SUCCESS;
             } else {
                 db = dbinfo;
                 while (db && strcmp(db->name, name))
                     db = db->next;
                 if (!db) {
                     cli_errmsg("cli_tgzload: File %s not found in .info\n", name);
                     cli_tgzload_cleanup(compr, dbio, fdd);
                     return CL_EMALFDB;
                 }
                 if (dbio->bread) {
                     if (db->size != dbio->bread) {
                         cli_errmsg("cli_tgzload: File %s not correctly loaded\n", name);
                         cli_tgzload_cleanup(compr, dbio, fdd);
                         return CL_EMALFDB;
                     }
                     cl_finish_hash(dbio->hashctx, hash);
                     dbio->hashctx = cl_hash_init("sha256");
                     if (!(dbio->hashctx)) {
                         cli_tgzload_cleanup(compr, dbio, fdd);
                         return CL_EMALFDB;
                     }
                     if (memcmp(db->hash, hash, 32)) {
                         cli_errmsg("cli_tgzload: Invalid checksum for file %s\n", name);
                         cli_tgzload_cleanup(compr, dbio, fdd);
                         return CL_EMALFDB;
                     }
                 }
             }
         }
         pad = size % TAR_BLOCKSIZE ? (TAR_BLOCKSIZE - (size % TAR_BLOCKSIZE)) : 0;
         if (compr) {
             if (off == gzseek(dbio->gzs, 0, SEEK_CUR))
                 gzseek(dbio->gzs, size + pad, SEEK_CUR);
             else if (pad)
                 gzseek(dbio->gzs, pad, SEEK_CUR);
         } else {
             if (off == ftell(dbio->fs))
                 fseek(dbio->fs, size + pad, SEEK_CUR);
             else if (pad)
                 fseek(dbio->fs, pad, SEEK_CUR);
         }
056d95dc
     }
 
bebd86a6
     cli_tgzload_cleanup(compr, dbio, fdd);
e8ae4fae
     return CL_SUCCESS;
056d95dc
 }
 
e4ae7726
 struct cl_cvd *cl_cvdparse(const char *head)
8139fd99
 {
288057e9
     struct cl_cvd *cvd;
     char *pt;
6a2532ca
 
288057e9
     if (strncmp(head, "ClamAV-VDB:", 11)) {
         cli_errmsg("cli_cvdparse: Not a CVD file\n");
         return NULL;
6a2532ca
     }
8139fd99
 
288057e9
     if (!(cvd = (struct cl_cvd *)cli_malloc(sizeof(struct cl_cvd)))) {
         cli_errmsg("cl_cvdparse: Can't allocate memory for cvd\n");
         return NULL;
e12c29d2
     }
8139fd99
 
288057e9
     if (!(cvd->time = cli_strtok(head, 1, ":"))) {
         cli_errmsg("cli_cvdparse: Can't parse the creation time\n");
         free(cvd);
         return NULL;
976bcd2a
     }
 
288057e9
     if (!(pt = cli_strtok(head, 2, ":"))) {
         cli_errmsg("cli_cvdparse: Can't parse the version number\n");
         free(cvd->time);
         free(cvd);
         return NULL;
976bcd2a
     }
8139fd99
     cvd->version = atoi(pt);
     free(pt);
 
288057e9
     if (!(pt = cli_strtok(head, 3, ":"))) {
         cli_errmsg("cli_cvdparse: Can't parse the number of signatures\n");
         free(cvd->time);
         free(cvd);
         return NULL;
976bcd2a
     }
8139fd99
     cvd->sigs = atoi(pt);
     free(pt);
 
288057e9
     if (!(pt = cli_strtok(head, 4, ":"))) {
         cli_errmsg("cli_cvdparse: Can't parse the functionality level\n");
         free(cvd->time);
         free(cvd);
         return NULL;
976bcd2a
     }
b5134815
     cvd->fl = atoi(pt);
8139fd99
     free(pt);
 
288057e9
     if (!(cvd->md5 = cli_strtok(head, 5, ":"))) {
         cli_errmsg("cli_cvdparse: Can't parse the MD5 checksum\n");
         free(cvd->time);
         free(cvd);
         return NULL;
976bcd2a
     }
 
288057e9
     if (!(cvd->dsig = cli_strtok(head, 6, ":"))) {
         cli_errmsg("cli_cvdparse: Can't parse the digital signature\n");
         free(cvd->time);
         free(cvd->md5);
         free(cvd);
         return NULL;
976bcd2a
     }
 
288057e9
     if (!(cvd->builder = cli_strtok(head, 7, ":"))) {
         cli_errmsg("cli_cvdparse: Can't parse the builder name\n");
         free(cvd->time);
         free(cvd->md5);
         free(cvd->dsig);
         free(cvd);
         return NULL;
976bcd2a
     }
8139fd99
 
288057e9
     if ((pt = cli_strtok(head, 8, ":"))) {
         cvd->stime = atoi(pt);
         free(pt);
a7ac5978
     } else {
288057e9
         cli_dbgmsg("cli_cvdparse: No creation time in seconds (old file format)\n");
         cvd->stime = 0;
a7ac5978
     }
c48c9d2b
 
8139fd99
     return cvd;
 }
 
 struct cl_cvd *cl_cvdhead(const char *file)
 {
288057e9
     FILE *fs;
     char head[513], *pt;
     int i;
     unsigned int bread;
8139fd99
 
288057e9
     if ((fs = fopen(file, "rb")) == NULL) {
         cli_errmsg("cl_cvdhead: Can't open file %s\n", file);
         return NULL;
8139fd99
     }
 
288057e9
     if (!(bread = fread(head, 1, 512, fs))) {
         cli_errmsg("cl_cvdhead: Can't read CVD header in %s\n", file);
         fclose(fs);
         return NULL;
e4ae7726
     }
 
1ccd6e94
     fclose(fs);
e4ae7726
 
b4bd1749
     head[bread] = 0;
288057e9
     if ((pt = strpbrk(head, "\n\r")))
         *pt = 0;
 
     for (i = bread - 1; i > 0 && (head[i] == ' ' || head[i] == '\n' || head[i] == '\r'); head[i] = 0, i--)
         ;
e4ae7726
 
     return cl_cvdparse(head);
8139fd99
 }
 
 void cl_cvdfree(struct cl_cvd *cvd)
 {
     free(cvd->time);
     free(cvd->md5);
     free(cvd->dsig);
     free(cvd->builder);
     free(cvd);
 }
 
cdddd014
 static int cli_cvdverify(FILE *fs, struct cl_cvd *cvdpt, unsigned int skipsig)
6a2532ca
 {
288057e9
     struct cl_cvd *cvd;
     char *md5, head[513];
     int i;
a7ac5978
 
1ccd6e94
     fseek(fs, 0, SEEK_SET);
288057e9
     if (fread(head, 1, 512, fs) != 512) {
         cli_errmsg("cli_cvdverify: Can't read CVD header\n");
         return CL_ECVD;
e4ae7726
     }
 
     head[512] = 0;
288057e9
     for (i = 511; i > 0 && (head[i] == ' ' || head[i] == 10); head[i] = 0, i--)
         ;
6a2532ca
 
288057e9
     if ((cvd = cl_cvdparse(head)) == NULL)
         return CL_ECVD;
6a2532ca
 
288057e9
     if (cvdpt)
         memcpy(cvdpt, cvd, sizeof(struct cl_cvd));
6a2532ca
 
288057e9
     if (skipsig) {
         cl_cvdfree(cvd);
         return CL_SUCCESS;
9d193ff2
     }
 
d5fde2eb
     md5 = cli_hashstream(fs, NULL, 1);
a6c9b781
     if (md5 == NULL) {
288057e9
         cli_dbgmsg("cli_cvdverify: Cannot generate hash, out of memory\n");
         cl_cvdfree(cvd);
         return CL_EMEM;
a6c9b781
     }
6a2532ca
     cli_dbgmsg("MD5(.tar.gz) = %s\n", md5);
 
288057e9
     if (strncmp(md5, cvd->md5, 32)) {
         cli_dbgmsg("cli_cvdverify: MD5 verification error\n");
         free(md5);
         cl_cvdfree(cvd);
         return CL_EVERIFY;
6a2532ca
     }
 
288057e9
     if (cli_versig(md5, cvd->dsig)) {
         cli_dbgmsg("cli_cvdverify: Digital signature verification error\n");
         free(md5);
         cl_cvdfree(cvd);
         return CL_EVERIFY;
6a2532ca
     }
 
e4ae7726
     free(md5);
9e431a95
     cl_cvdfree(cvd);
9d193ff2
     return CL_SUCCESS;
6a2532ca
 }
 
e4ae7726
 int cl_cvdverify(const char *file)
6a2532ca
 {
288057e9
     struct cl_engine *engine;
     FILE *fs;
     int ret, dbtype = 0;
a7ac5978
 
288057e9
     if ((fs = fopen(file, "rb")) == NULL) {
         cli_errmsg("cl_cvdverify: Can't open file %s\n", file);
         return CL_EOPEN;
6a2532ca
     }
 
288057e9
     if (!(engine = cl_engine_new())) {
         cli_errmsg("cld_cvdverify: Can't create new engine\n");
         fclose(fs);
         return CL_EMEM;
f8202681
     }
7c92a662
     engine->cb_stats_submit = NULL; /* Don't submit stats if we're just verifying a CVD */
f8202681
 
52da9175
     if (!!cli_strbcasestr(file, ".cld"))
288057e9
         dbtype = 1;
52da9175
     else if (!!cli_strbcasestr(file, ".cud"))
288057e9
         dbtype = 2;
52da9175
 
     ret = cli_cvdload(fs, engine, NULL, CL_DB_STDOPT | CL_DB_PUA, dbtype, file, 1);
e4ae7726
 
f8202681
     cl_engine_free(engine);
     fclose(fs);
e4ae7726
     return ret;
6a2532ca
 }
 
cdddd014
 int cli_cvdload(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, unsigned int dbtype, const char *filename, unsigned int chkonly)
8139fd99
 {
288057e9
     struct cl_cvd cvd, dupcvd;
     FILE *dupfs;
     int ret;
     time_t s_time;
     int cfd;
     struct cli_dbio dbio;
     struct cli_dbinfo *dbinfo = NULL;
     char *dupname;
8139fd99
 
a1cbd793
     dbio.hashctx = NULL;
 
8139fd99
     cli_dbgmsg("in cli_cvdload()\n");
 
6a2532ca
     /* verify */
288057e9
     if ((ret = cli_cvdverify(fs, &cvd, dbtype)))
         return ret;
 
     if (dbtype <= 1) {
         /* check for duplicate db */
         dupname = cli_strdup(filename);
         if (!dupname)
             return CL_EMEM;
         dupname[strlen(dupname) - 2] = (dbtype == 1 ? 'v' : 'l');
         if (!access(dupname, R_OK) && (dupfs = fopen(dupname, "rb"))) {
             if ((ret = cli_cvdverify(dupfs, &dupcvd, !dbtype))) {
                 fclose(dupfs);
                 free(dupname);
                 return ret;
             }
             fclose(dupfs);
             if (dupcvd.version > cvd.version) {
                 cli_warnmsg("Detected duplicate databases %s and %s. The %s database is older and will not be loaded, you should manually remove it from the database directory.\n", filename, dupname, filename);
                 free(dupname);
                 return CL_SUCCESS;
             } else if (dupcvd.version == cvd.version && !dbtype) {
                 cli_warnmsg("Detected duplicate databases %s and %s, please manually remove one of them\n", filename, dupname);
                 free(dupname);
                 return CL_SUCCESS;
             }
         }
         free(dupname);
     }
 
     if (strstr(filename, "daily.")) {
         time(&s_time);
         if (cvd.stime > s_time) {
             if (cvd.stime - (unsigned int)s_time > 3600) {
                 cli_warnmsg("******************************************************\n");
                 cli_warnmsg("***      Virus database timestamp in the future!   ***\n");
                 cli_warnmsg("***  Please check the timezone and clock settings  ***\n");
                 cli_warnmsg("******************************************************\n");
             }
         } else if ((unsigned int)s_time - cvd.stime > 604800) {
             cli_warnmsg("**************************************************\n");
             cli_warnmsg("***  The virus database is older than 7 days!  ***\n");
             cli_warnmsg("***   Please update it as soon as possible.    ***\n");
             cli_warnmsg("**************************************************\n");
         }
         engine->dbversion[0] = cvd.version;
         engine->dbversion[1] = cvd.stime;
     }
 
     if (cvd.fl > cl_retflevel()) {
         cli_warnmsg("*******************************************************************\n");
         cli_warnmsg("***  This version of the ClamAV engine is outdated.             ***\n");
         cli_warnmsg("***   Read https://www.clamav.net/documents/installing-clamav   ***\n");
         cli_warnmsg("*******************************************************************\n");
     }
 
     cfd          = fileno(fs);
f8202681
     dbio.chkonly = 0;
288057e9
     if (dbtype == 2)
         ret = cli_tgzload(cfd, engine, signo, options | CL_DB_UNSIGNED, &dbio, NULL);
cdddd014
     else
288057e9
         ret = cli_tgzload(cfd, engine, signo, options | CL_DB_OFFICIAL, &dbio, NULL);
     if (ret != CL_SUCCESS)
         return ret;
ace26bfe
 
a2b0a11e
     dbinfo = engine->dbinfo;
288057e9
     if (!dbinfo || !dbinfo->cvd || (dbinfo->cvd->version != cvd.version) || (dbinfo->cvd->sigs != cvd.sigs) || (dbinfo->cvd->fl != cvd.fl) || (dbinfo->cvd->stime != cvd.stime)) {
         cli_errmsg("cli_cvdload: Corrupted CVD header\n");
         return CL_EMALFDB;
de351ee1
     }
a2b0a11e
     dbinfo = engine->dbinfo ? engine->dbinfo->next : NULL;
288057e9
     if (!dbinfo) {
         cli_errmsg("cli_cvdload: dbinfo error\n");
         return CL_EMALFDB;
e828f534
     }
a2b0a11e
 
f8202681
     dbio.chkonly = chkonly;
288057e9
     if (dbtype == 2)
         options |= CL_DB_UNSIGNED;
cdddd014
     else
288057e9
         options |= CL_DB_SIGNED | CL_DB_OFFICIAL;
cdddd014
 
     ret = cli_tgzload(cfd, engine, signo, options, &dbio, dbinfo);
ace26bfe
 
288057e9
     while (engine->dbinfo) {
         dbinfo         = engine->dbinfo;
         engine->dbinfo = dbinfo->next;
544fa973
         MPOOL_FREE(engine->mempool, dbinfo->name);
         MPOOL_FREE(engine->mempool, dbinfo->hash);
288057e9
         if (dbinfo->cvd)
             cl_cvdfree(dbinfo->cvd);
544fa973
         MPOOL_FREE(engine->mempool, dbinfo);
ac1b219c
     }
 
ace26bfe
     return ret;
8139fd99
 }
afff80ef
 
 int cli_cvdunpack(const char *file, const char *dir)
 {
288057e9
     int fd, ret;
afff80ef
 
288057e9
     fd = open(file, O_RDONLY | O_BINARY);
     if (fd == -1)
         return -1;
afff80ef
 
288057e9
     if (lseek(fd, 512, SEEK_SET) < 0) {
         close(fd);
         return -1;
afff80ef
     }
 
     ret = cli_untgz(fd, dir);
     close(fd);
     return ret;
 }