1 /*
2  *  Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3  *  Copyright (C) 2007-2013 Sourcefire, Inc.
4  *
5  *  Authors: Tomasz Kojm
6  *
7  *  Summary: Code to parse Clamav CVD database format.
8  *
9  *  Acknowledgements: ClamAV untar code is based on a public domain minitar utility
10  *                    by Charles G. Waldman.
11  *
12  *  This program is free software; you can redistribute it and/or modify
13  *  it under the terms of the GNU General Public License version 2 as
14  *  published by the Free Software Foundation.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24  *  MA 02110-1301, USA.
25  */
26 
27 #if HAVE_CONFIG_H
28 #include "clamav-config.h"
29 #endif
30 
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40 #include "zlib.h"
41 #include <time.h>
42 #include <errno.h>
43 
44 #include "clamav.h"
45 #include "others.h"
46 #include "dsig.h"
47 #include "str.h"
48 #include "cvd.h"
49 #include "readdb.h"
50 #include "default.h"
51 
52 #define TAR_BLOCKSIZE 512
53 
cli_untgz_cleanup(char * path,gzFile infile,FILE * outfile,int fdd)54 static void cli_untgz_cleanup(char *path, gzFile infile, FILE *outfile, int fdd)
55 {
56     UNUSEDPARAM(fdd);
57     cli_dbgmsg("in cli_untgz_cleanup()\n");
58     if (path != NULL)
59         free(path);
60     if (infile != NULL)
61         gzclose(infile);
62     if (outfile != NULL)
63         fclose(outfile);
64 }
65 
cli_untgz(int fd,const char * destdir)66 static int cli_untgz(int fd, const char *destdir)
67 {
68     char *path, osize[13], name[101], type;
69     char block[TAR_BLOCKSIZE];
70     int nbytes, nread, nwritten, in_block = 0, fdd = -1;
71     unsigned int size, pathlen = strlen(destdir) + 100 + 5;
72     FILE *outfile = NULL;
73     STATBUF foo;
74     gzFile infile = NULL;
75 
76     cli_dbgmsg("in cli_untgz()\n");
77 
78     if ((fdd = dup(fd)) == -1) {
79         cli_errmsg("cli_untgz: Can't duplicate descriptor %d\n", fd);
80         return -1;
81     }
82 
83     if ((infile = gzdopen(fdd, "rb")) == NULL) {
84         cli_errmsg("cli_untgz: Can't gzdopen() descriptor %d, errno = %d\n", fdd, errno);
85         if (FSTAT(fdd, &foo) == 0)
86             close(fdd);
87         return -1;
88     }
89 
90     path = (char *)cli_calloc(sizeof(char), pathlen);
91     if (!path) {
92         cli_errmsg("cli_untgz: Can't allocate memory for path\n");
93         cli_untgz_cleanup(NULL, infile, NULL, fdd);
94         return -1;
95     }
96 
97     while (1) {
98 
99         nread = gzread(infile, block, TAR_BLOCKSIZE);
100 
101         if (!in_block && !nread)
102             break;
103 
104         if (nread != TAR_BLOCKSIZE) {
105             cli_errmsg("cli_untgz: Incomplete block read\n");
106             cli_untgz_cleanup(path, infile, outfile, fdd);
107             return -1;
108         }
109 
110         if (!in_block) {
111             if (block[0] == '\0') /* We're done */
112                 break;
113 
114             strncpy(name, block, 100);
115             name[100] = '\0';
116 
117             if (strchr(name, '/')) {
118                 cli_errmsg("cli_untgz: Slash separators are not allowed in CVD\n");
119                 cli_untgz_cleanup(path, infile, outfile, fdd);
120                 return -1;
121             }
122 
123             snprintf(path, pathlen, "%s" PATHSEP "%s", destdir, name);
124             cli_dbgmsg("cli_untgz: Unpacking %s\n", path);
125             type = block[156];
126 
127             switch (type) {
128                 case '0':
129                 case '\0':
130                     break;
131                 case '5':
132                     cli_errmsg("cli_untgz: Directories are not supported in CVD\n");
133                     cli_untgz_cleanup(path, infile, outfile, fdd);
134                     return -1;
135                 default:
136                     cli_errmsg("cli_untgz: Unknown type flag '%c'\n", type);
137                     cli_untgz_cleanup(path, infile, outfile, fdd);
138                     return -1;
139             }
140 
141             if (outfile) {
142                 if (fclose(outfile)) {
143                     cli_errmsg("cli_untgz: Cannot close file %s\n", path);
144                     outfile = NULL;
145                     cli_untgz_cleanup(path, infile, outfile, fdd);
146                     return -1;
147                 }
148                 outfile = NULL;
149             }
150 
151             if (!(outfile = fopen(path, "wb"))) {
152                 cli_errmsg("cli_untgz: Cannot create file %s\n", path);
153                 cli_untgz_cleanup(path, infile, outfile, fdd);
154                 return -1;
155             }
156 
157             strncpy(osize, block + 124, 12);
158             osize[12] = '\0';
159 
160             if ((sscanf(osize, "%o", &size)) == 0) {
161                 cli_errmsg("cli_untgz: Invalid size in header\n");
162                 cli_untgz_cleanup(path, infile, outfile, fdd);
163                 return -1;
164             }
165 
166             if (size > 0)
167                 in_block = 1;
168 
169         } else { /* write or continue writing file contents */
170             nbytes   = size > TAR_BLOCKSIZE ? TAR_BLOCKSIZE : size;
171             nwritten = fwrite(block, 1, nbytes, outfile);
172 
173             if (nwritten != nbytes) {
174                 cli_errmsg("cli_untgz: Wrote %d instead of %d (%s)\n", nwritten, nbytes, path);
175                 cli_untgz_cleanup(path, infile, outfile, fdd);
176                 return -1;
177             }
178 
179             size -= nbytes;
180             if (size == 0)
181                 in_block = 0;
182         }
183     }
184 
185     cli_untgz_cleanup(path, infile, outfile, fdd);
186     return 0;
187 }
188 
cli_tgzload_cleanup(int comp,struct cli_dbio * dbio,int fdd)189 static void cli_tgzload_cleanup(int comp, struct cli_dbio *dbio, int fdd)
190 {
191     UNUSEDPARAM(fdd);
192     cli_dbgmsg("in cli_tgzload_cleanup()\n");
193     if (comp) {
194         gzclose(dbio->gzs);
195         dbio->gzs = NULL;
196     } else {
197         fclose(dbio->fs);
198         dbio->fs = NULL;
199     }
200     if (dbio->buf != NULL) {
201         free(dbio->buf);
202         dbio->buf = NULL;
203     }
204 
205     if (dbio->hashctx) {
206         cl_hash_destroy(dbio->hashctx);
207         dbio->hashctx = NULL;
208     }
209 }
210 
cli_tgzload(int fd,struct cl_engine * engine,unsigned int * signo,unsigned int options,struct cli_dbio * dbio,struct cli_dbinfo * dbinfo)211 static int cli_tgzload(int fd, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, struct cli_dbinfo *dbinfo)
212 {
213     char osize[13], name[101];
214     char block[TAR_BLOCKSIZE];
215     int nread, fdd, ret;
216     unsigned int type, size, pad, compr = 1;
217     off_t off;
218     struct cli_dbinfo *db;
219     char hash[32];
220 
221     cli_dbgmsg("in cli_tgzload()\n");
222 
223     if (lseek(fd, 512, SEEK_SET) < 0) {
224         return CL_ESEEK;
225     }
226 
227     if (cli_readn(fd, block, 7) != 7)
228         return CL_EFORMAT; /* truncated file? */
229 
230     if (!strncmp(block, "COPYING", 7))
231         compr = 0;
232 
233     if (lseek(fd, 512, SEEK_SET) < 0) {
234         return CL_ESEEK;
235     }
236 
237     if ((fdd = dup(fd)) == -1) {
238         cli_errmsg("cli_tgzload: Can't duplicate descriptor %d\n", fd);
239         return CL_EDUP;
240     }
241 
242     if (compr) {
243         if ((dbio->gzs = gzdopen(fdd, "rb")) == NULL) {
244             cli_errmsg("cli_tgzload: Can't gzdopen() descriptor %d, errno = %d\n", fdd, errno);
245             if (fdd > -1)
246                 close(fdd);
247             return CL_EOPEN;
248         }
249         dbio->fs = NULL;
250     } else {
251         if ((dbio->fs = fdopen(fdd, "rb")) == NULL) {
252             cli_errmsg("cli_tgzload: Can't fdopen() descriptor %d, errno = %d\n", fdd, errno);
253             if (fdd > -1)
254                 close(fdd);
255             return CL_EOPEN;
256         }
257         dbio->gzs = NULL;
258     }
259 
260     dbio->bufsize = CLI_DEFAULT_DBIO_BUFSIZE;
261     dbio->buf     = cli_malloc(dbio->bufsize);
262     if (!dbio->buf) {
263         cli_errmsg("cli_tgzload: Can't allocate memory for dbio->buf\n");
264         cli_tgzload_cleanup(compr, dbio, fdd);
265         return CL_EMALFDB;
266     }
267     dbio->bufpt  = NULL;
268     dbio->usebuf = 1;
269     dbio->readpt = dbio->buf;
270 
271     while (1) {
272 
273         if (compr)
274             nread = gzread(dbio->gzs, block, TAR_BLOCKSIZE);
275         else
276             nread = fread(block, 1, TAR_BLOCKSIZE, dbio->fs);
277 
278         if (!nread)
279             break;
280 
281         if (nread != TAR_BLOCKSIZE) {
282             cli_errmsg("cli_tgzload: Incomplete block read\n");
283             cli_tgzload_cleanup(compr, dbio, fdd);
284             return CL_EMALFDB;
285         }
286 
287         if (block[0] == '\0') /* We're done */
288             break;
289 
290         strncpy(name, block, 100);
291         name[100] = '\0';
292 
293         if (strchr(name, '/')) {
294             cli_errmsg("cli_tgzload: Slash separators are not allowed in CVD\n");
295             cli_tgzload_cleanup(compr, dbio, fdd);
296             return CL_EMALFDB;
297         }
298 
299         type = block[156];
300 
301         switch (type) {
302             case '0':
303             case '\0':
304                 break;
305             case '5':
306                 cli_errmsg("cli_tgzload: Directories are not supported in CVD\n");
307                 cli_tgzload_cleanup(compr, dbio, fdd);
308                 return CL_EMALFDB;
309             default:
310                 cli_errmsg("cli_tgzload: Unknown type flag '%c'\n", type);
311                 cli_tgzload_cleanup(compr, dbio, fdd);
312                 return CL_EMALFDB;
313         }
314 
315         strncpy(osize, block + 124, 12);
316         osize[12] = '\0';
317 
318         if ((sscanf(osize, "%o", &size)) == 0) {
319             cli_errmsg("cli_tgzload: Invalid size in header\n");
320             cli_tgzload_cleanup(compr, dbio, fdd);
321             return CL_EMALFDB;
322         }
323         dbio->size     = size;
324         dbio->readsize = dbio->size < dbio->bufsize ? dbio->size : dbio->bufsize - 1;
325         dbio->bufpt    = NULL;
326         dbio->readpt   = dbio->buf;
327         if (!(dbio->hashctx)) {
328             dbio->hashctx = cl_hash_init("sha256");
329             if (!(dbio->hashctx)) {
330                 cli_tgzload_cleanup(compr, dbio, fdd);
331                 return CL_EMALFDB;
332             }
333         }
334         dbio->bread = 0;
335 
336         /* cli_dbgmsg("cli_tgzload: Loading %s, size: %u\n", name, size); */
337         if (compr)
338             off = (off_t)gzseek(dbio->gzs, 0, SEEK_CUR);
339         else
340             off = ftell(dbio->fs);
341 
342         if ((!dbinfo && cli_strbcasestr(name, ".info")) || (dbinfo && CLI_DBEXT(name))) {
343             ret = cli_load(name, engine, signo, options, dbio);
344             if (ret) {
345                 cli_errmsg("cli_tgzload: Can't load %s\n", name);
346                 cli_tgzload_cleanup(compr, dbio, fdd);
347                 return CL_EMALFDB;
348             }
349             if (!dbinfo) {
350                 cli_tgzload_cleanup(compr, dbio, fdd);
351                 return CL_SUCCESS;
352             } else {
353                 db = dbinfo;
354                 while (db && strcmp(db->name, name))
355                     db = db->next;
356                 if (!db) {
357                     cli_errmsg("cli_tgzload: File %s not found in .info\n", name);
358                     cli_tgzload_cleanup(compr, dbio, fdd);
359                     return CL_EMALFDB;
360                 }
361                 if (dbio->bread) {
362                     if (db->size != dbio->bread) {
363                         cli_errmsg("cli_tgzload: File %s not correctly loaded\n", name);
364                         cli_tgzload_cleanup(compr, dbio, fdd);
365                         return CL_EMALFDB;
366                     }
367                     cl_finish_hash(dbio->hashctx, hash);
368                     dbio->hashctx = cl_hash_init("sha256");
369                     if (!(dbio->hashctx)) {
370                         cli_tgzload_cleanup(compr, dbio, fdd);
371                         return CL_EMALFDB;
372                     }
373                     if (memcmp(db->hash, hash, 32)) {
374                         cli_errmsg("cli_tgzload: Invalid checksum for file %s\n", name);
375                         cli_tgzload_cleanup(compr, dbio, fdd);
376                         return CL_EMALFDB;
377                     }
378                 }
379             }
380         }
381         pad = size % TAR_BLOCKSIZE ? (TAR_BLOCKSIZE - (size % TAR_BLOCKSIZE)) : 0;
382         if (compr) {
383             if (off == gzseek(dbio->gzs, 0, SEEK_CUR))
384                 gzseek(dbio->gzs, size + pad, SEEK_CUR);
385             else if (pad)
386                 gzseek(dbio->gzs, pad, SEEK_CUR);
387         } else {
388             if (off == ftell(dbio->fs))
389                 fseek(dbio->fs, size + pad, SEEK_CUR);
390             else if (pad)
391                 fseek(dbio->fs, pad, SEEK_CUR);
392         }
393     }
394 
395     cli_tgzload_cleanup(compr, dbio, fdd);
396     return CL_SUCCESS;
397 }
398 
cl_cvdparse(const char * head)399 struct cl_cvd *cl_cvdparse(const char *head)
400 {
401     struct cl_cvd *cvd;
402     char *pt;
403 
404     if (strncmp(head, "ClamAV-VDB:", 11)) {
405         cli_errmsg("cli_cvdparse: Not a CVD file\n");
406         return NULL;
407     }
408 
409     if (!(cvd = (struct cl_cvd *)cli_malloc(sizeof(struct cl_cvd)))) {
410         cli_errmsg("cl_cvdparse: Can't allocate memory for cvd\n");
411         return NULL;
412     }
413 
414     if (!(cvd->time = cli_strtok(head, 1, ":"))) {
415         cli_errmsg("cli_cvdparse: Can't parse the creation time\n");
416         free(cvd);
417         return NULL;
418     }
419 
420     if (!(pt = cli_strtok(head, 2, ":"))) {
421         cli_errmsg("cli_cvdparse: Can't parse the version number\n");
422         free(cvd->time);
423         free(cvd);
424         return NULL;
425     }
426     cvd->version = atoi(pt);
427     free(pt);
428 
429     if (!(pt = cli_strtok(head, 3, ":"))) {
430         cli_errmsg("cli_cvdparse: Can't parse the number of signatures\n");
431         free(cvd->time);
432         free(cvd);
433         return NULL;
434     }
435     cvd->sigs = atoi(pt);
436     free(pt);
437 
438     if (!(pt = cli_strtok(head, 4, ":"))) {
439         cli_errmsg("cli_cvdparse: Can't parse the functionality level\n");
440         free(cvd->time);
441         free(cvd);
442         return NULL;
443     }
444     cvd->fl = atoi(pt);
445     free(pt);
446 
447     if (!(cvd->md5 = cli_strtok(head, 5, ":"))) {
448         cli_errmsg("cli_cvdparse: Can't parse the MD5 checksum\n");
449         free(cvd->time);
450         free(cvd);
451         return NULL;
452     }
453 
454     if (!(cvd->dsig = cli_strtok(head, 6, ":"))) {
455         cli_errmsg("cli_cvdparse: Can't parse the digital signature\n");
456         free(cvd->time);
457         free(cvd->md5);
458         free(cvd);
459         return NULL;
460     }
461 
462     if (!(cvd->builder = cli_strtok(head, 7, ":"))) {
463         cli_errmsg("cli_cvdparse: Can't parse the builder name\n");
464         free(cvd->time);
465         free(cvd->md5);
466         free(cvd->dsig);
467         free(cvd);
468         return NULL;
469     }
470 
471     if ((pt = cli_strtok(head, 8, ":"))) {
472         cvd->stime = atoi(pt);
473         free(pt);
474     } else {
475         cli_dbgmsg("cli_cvdparse: No creation time in seconds (old file format)\n");
476         cvd->stime = 0;
477     }
478 
479     return cvd;
480 }
481 
cl_cvdhead(const char * file)482 struct cl_cvd *cl_cvdhead(const char *file)
483 {
484     FILE *fs;
485     char head[513], *pt;
486     int i;
487     unsigned int bread;
488 
489     if ((fs = fopen(file, "rb")) == NULL) {
490         cli_errmsg("cl_cvdhead: Can't open file %s\n", file);
491         return NULL;
492     }
493 
494     if (!(bread = fread(head, 1, 512, fs))) {
495         cli_errmsg("cl_cvdhead: Can't read CVD header in %s\n", file);
496         fclose(fs);
497         return NULL;
498     }
499 
500     fclose(fs);
501 
502     head[bread] = 0;
503     if ((pt = strpbrk(head, "\n\r")))
504         *pt = 0;
505 
506     for (i = bread - 1; i > 0 && (head[i] == ' ' || head[i] == '\n' || head[i] == '\r'); head[i] = 0, i--)
507         ;
508 
509     return cl_cvdparse(head);
510 }
511 
cl_cvdfree(struct cl_cvd * cvd)512 void cl_cvdfree(struct cl_cvd *cvd)
513 {
514     free(cvd->time);
515     free(cvd->md5);
516     free(cvd->dsig);
517     free(cvd->builder);
518     free(cvd);
519 }
520 
cli_cvdverify(FILE * fs,struct cl_cvd * cvdpt,unsigned int skipsig)521 static int cli_cvdverify(FILE *fs, struct cl_cvd *cvdpt, unsigned int skipsig)
522 {
523     struct cl_cvd *cvd;
524     char *md5, head[513];
525     int i;
526 
527     fseek(fs, 0, SEEK_SET);
528     if (fread(head, 1, 512, fs) != 512) {
529         cli_errmsg("cli_cvdverify: Can't read CVD header\n");
530         return CL_ECVD;
531     }
532 
533     head[512] = 0;
534     for (i = 511; i > 0 && (head[i] == ' ' || head[i] == 10); head[i] = 0, i--)
535         ;
536 
537     if ((cvd = cl_cvdparse(head)) == NULL)
538         return CL_ECVD;
539 
540     if (cvdpt)
541         memcpy(cvdpt, cvd, sizeof(struct cl_cvd));
542 
543     if (skipsig) {
544         cl_cvdfree(cvd);
545         return CL_SUCCESS;
546     }
547 
548     md5 = cli_hashstream(fs, NULL, 1);
549     if (md5 == NULL) {
550         cli_dbgmsg("cli_cvdverify: Cannot generate hash, out of memory\n");
551         cl_cvdfree(cvd);
552         return CL_EMEM;
553     }
554     cli_dbgmsg("MD5(.tar.gz) = %s\n", md5);
555 
556     if (strncmp(md5, cvd->md5, 32)) {
557         cli_dbgmsg("cli_cvdverify: MD5 verification error\n");
558         free(md5);
559         cl_cvdfree(cvd);
560         return CL_EVERIFY;
561     }
562 
563     if (cli_versig(md5, cvd->dsig)) {
564         cli_dbgmsg("cli_cvdverify: Digital signature verification error\n");
565         free(md5);
566         cl_cvdfree(cvd);
567         return CL_EVERIFY;
568     }
569 
570     free(md5);
571     cl_cvdfree(cvd);
572     return CL_SUCCESS;
573 }
574 
cl_cvdverify(const char * file)575 cl_error_t cl_cvdverify(const char *file)
576 {
577     struct cl_engine *engine;
578     FILE *fs;
579     cl_error_t ret;
580     int dbtype = 0;
581 
582     if ((fs = fopen(file, "rb")) == NULL) {
583         cli_errmsg("cl_cvdverify: Can't open file %s\n", file);
584         return CL_EOPEN;
585     }
586 
587     if (!(engine = cl_engine_new())) {
588         cli_errmsg("cld_cvdverify: Can't create new engine\n");
589         fclose(fs);
590         return CL_EMEM;
591     }
592     engine->cb_stats_submit = NULL; /* Don't submit stats if we're just verifying a CVD */
593 
594     if (!!cli_strbcasestr(file, ".cld"))
595         dbtype = 1;
596     else if (!!cli_strbcasestr(file, ".cud"))
597         dbtype = 2;
598 
599     ret = cli_cvdload(fs, engine, NULL, CL_DB_STDOPT | CL_DB_PUA, dbtype, file, 1);
600 
601     cl_engine_free(engine);
602     fclose(fs);
603     return ret;
604 }
605 
cli_cvdload(FILE * fs,struct cl_engine * engine,unsigned int * signo,unsigned int options,unsigned int dbtype,const char * filename,unsigned int chkonly)606 int cli_cvdload(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int options, unsigned int dbtype, const char *filename, unsigned int chkonly)
607 {
608     struct cl_cvd cvd, dupcvd;
609     FILE *dupfs;
610     int ret;
611     time_t s_time;
612     int cfd;
613     struct cli_dbio dbio;
614     struct cli_dbinfo *dbinfo = NULL;
615     char *dupname;
616 
617     dbio.hashctx = NULL;
618 
619     cli_dbgmsg("in cli_cvdload()\n");
620 
621     /* verify */
622     if ((ret = cli_cvdverify(fs, &cvd, dbtype)))
623         return ret;
624 
625     if (dbtype <= 1) {
626         /* check for duplicate db */
627         dupname = cli_strdup(filename);
628         if (!dupname)
629             return CL_EMEM;
630         dupname[strlen(dupname) - 2] = (dbtype == 1 ? 'v' : 'l');
631         if (!access(dupname, R_OK) && (dupfs = fopen(dupname, "rb"))) {
632             if ((ret = cli_cvdverify(dupfs, &dupcvd, !dbtype))) {
633                 fclose(dupfs);
634                 free(dupname);
635                 return ret;
636             }
637             fclose(dupfs);
638             if (dupcvd.version > cvd.version) {
639                 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);
640                 free(dupname);
641                 return CL_SUCCESS;
642             } else if (dupcvd.version == cvd.version && !dbtype) {
643                 cli_warnmsg("Detected duplicate databases %s and %s, please manually remove one of them\n", filename, dupname);
644                 free(dupname);
645                 return CL_SUCCESS;
646             }
647         }
648         free(dupname);
649     }
650 
651     if (strstr(filename, "daily.")) {
652         time(&s_time);
653         if (cvd.stime > s_time) {
654             if (cvd.stime - (unsigned int)s_time > 3600) {
655                 cli_warnmsg("******************************************************\n");
656                 cli_warnmsg("***      Virus database timestamp in the future!   ***\n");
657                 cli_warnmsg("***  Please check the timezone and clock settings  ***\n");
658                 cli_warnmsg("******************************************************\n");
659             }
660         } else if ((unsigned int)s_time - cvd.stime > 604800) {
661             cli_warnmsg("**************************************************\n");
662             cli_warnmsg("***  The virus database is older than 7 days!  ***\n");
663             cli_warnmsg("***   Please update it as soon as possible.    ***\n");
664             cli_warnmsg("**************************************************\n");
665         }
666         engine->dbversion[0] = cvd.version;
667         engine->dbversion[1] = cvd.stime;
668     }
669 
670     if (cvd.fl > cl_retflevel()) {
671         cli_warnmsg("*******************************************************************\n");
672         cli_warnmsg("***  This version of the ClamAV engine is outdated.             ***\n");
673         cli_warnmsg("***   Read https://docs.clamav.net/manual/Installing.html       ***\n");
674         cli_warnmsg("*******************************************************************\n");
675     }
676 
677     cfd          = fileno(fs);
678     dbio.chkonly = 0;
679     if (dbtype == 2)
680         ret = cli_tgzload(cfd, engine, signo, options | CL_DB_UNSIGNED, &dbio, NULL);
681     else
682         ret = cli_tgzload(cfd, engine, signo, options | CL_DB_OFFICIAL, &dbio, NULL);
683     if (ret != CL_SUCCESS)
684         return ret;
685 
686     dbinfo = engine->dbinfo;
687     if (!dbinfo || !dbinfo->cvd || (dbinfo->cvd->version != cvd.version) || (dbinfo->cvd->sigs != cvd.sigs) || (dbinfo->cvd->fl != cvd.fl) || (dbinfo->cvd->stime != cvd.stime)) {
688         cli_errmsg("cli_cvdload: Corrupted CVD header\n");
689         return CL_EMALFDB;
690     }
691     dbinfo = engine->dbinfo ? engine->dbinfo->next : NULL;
692     if (!dbinfo) {
693         cli_errmsg("cli_cvdload: dbinfo error\n");
694         return CL_EMALFDB;
695     }
696 
697     dbio.chkonly = chkonly;
698     if (dbtype == 2)
699         options |= CL_DB_UNSIGNED;
700     else
701         options |= CL_DB_SIGNED | CL_DB_OFFICIAL;
702 
703     ret = cli_tgzload(cfd, engine, signo, options, &dbio, dbinfo);
704 
705     while (engine->dbinfo) {
706         dbinfo         = engine->dbinfo;
707         engine->dbinfo = dbinfo->next;
708         MPOOL_FREE(engine->mempool, dbinfo->name);
709         MPOOL_FREE(engine->mempool, dbinfo->hash);
710         if (dbinfo->cvd)
711             cl_cvdfree(dbinfo->cvd);
712         MPOOL_FREE(engine->mempool, dbinfo);
713     }
714 
715     return ret;
716 }
717 
cli_cvdunpack(const char * file,const char * dir)718 int cli_cvdunpack(const char *file, const char *dir)
719 {
720     int fd, ret;
721 
722     fd = open(file, O_RDONLY | O_BINARY);
723     if (fd == -1)
724         return -1;
725 
726     if (lseek(fd, 512, SEEK_SET) < 0) {
727         close(fd);
728         return -1;
729     }
730 
731     ret = cli_untgz(fd, dir);
732     close(fd);
733     return ret;
734 }
735