Package: release.debian.org
User: release.debian....@packages.debian.org
Usertags: pu
Tags: buster
Severity: normal

ClamAV upstream released 0.102.4 fixing three CVEs. From their news:

- [CVE-2020-3350]
  Fix a vulnerability wherein a malicious user could replace a scan target's
  directory with a symlink to another path to trick clamscan, clamdscan, or
  clamonacc into removing or moving a different file (eg. a critical system
  file). The issue would affect users that use the --move or --remove options
  for clamscan, clamdscan, and clamonacc.

  For more information about AV quarantine attacks using links, see the
  [RACK911 Lab's 
report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software).

- [CVE-2020-3327]
  Fix a vulnerability in the ARJ archive parsing module in ClamAV 0.102.3 that
  could cause a Denial-of-Service (DoS) condition. Improper bounds checking
  results in an out-of-bounds read which could cause a crash.
  The previous fix for this CVE in 0.102.3 was incomplete. This fix correctly
  resolves the issue.

- [CVE-2020-3481]
  Fix a vulnerability in the EGG archive module in ClamAV 0.102.0 - 0.102.3
  could cause a Denial-of-Service (DoS) condition. Improper error handling
  may result in a crash due to a NULL pointer dereference.
  This vulnerability is mitigated for those using the official ClamAV
  signature databases because the file type signatures in daily.cvd
  will not enable the EGG archive parser in versions affected by the
  vulnerability.

I prepared the packages and gave it a brief test overnight.

Sebastian
diff -Nru clamav-0.102.3+dfsg/clamdscan/proto.c 
clamav-0.102.4+dfsg/clamdscan/proto.c
--- clamav-0.102.3+dfsg/clamdscan/proto.c       2020-05-12 03:54:49.000000000 
+0200
+++ clamav-0.102.4+dfsg/clamdscan/proto.c       2020-07-15 23:54:36.000000000 
+0200
@@ -262,9 +262,23 @@
     char *bol, *eol;
     struct RCVLN rcv;
     STATBUF sb;
+    cl_error_t ret;
+    char *real_filename = NULL;
+
+    if (filename) {
+        ret = cli_realpath((const char *) filename, &real_filename);
+        if (CL_SUCCESS != ret) {
+            logg("Failed to determine real filename of %s.\n", filename);
+            infected = -1;
+            goto done;
+        }
+        filename = real_filename;
+
+        if (1 == chkpath(filename)) {
+            goto done;
+        }
+    }
 
-    if (filename && chkpath(filename))
-        return 0;
     recvlninit(&rcv, sockd);
 
     switch (scantype) {
@@ -273,17 +287,20 @@
         case ALLMATCH:
             if (!filename) {
                 logg("Filename cannot be NULL for MULTISCAN or CONTSCAN.\n");
-                return -1;
+                infected = -1;
+                goto done;
             }
             len = strlen(filename) + strlen(scancmd[scantype]) + 3;
             if (!(bol = malloc(len))) {
                 logg("!Cannot allocate a command buffer: %s\n", 
strerror(errno));
-                return -1;
+                infected = -1;
+                goto done;
             }
             sprintf(bol, "z%s %s", scancmd[scantype], filename);
             if (sendln(sockd, bol, len)) {
                 free(bol);
-                return -1;
+                infected = -1;
+                goto done;
             }
             free(bol);
             break;
@@ -304,11 +321,15 @@
         *printok = 0;
         if (errors)
             (*errors)++;
-        return len;
+        infected = len;
+        goto done;
     }
 
     while ((len = recvln(&rcv, &bol, &eol))) {
-        if (len == -1) return -1;
+        if (len == -1) {
+            infected = -1;
+            goto done;
+        }
         beenthere = 1;
         if (!filename) logg("~%s\n", bol);
         if (len > 7) {
@@ -328,7 +349,8 @@
                          (scantype < 0 || scantype > MAX_SCANTYPE) ? 
"unidentified" : scancmd[scantype]);
                 else
                     logg("Failed to parse reply: \"%s\"\n", bol);
-                return -1;
+                infected = -1;
+                goto done;
             } else if (!memcmp(eol - 7, " FOUND", 6)) {
                 static char last_filename[PATH_MAX + 1] = {'\0'};
                 *(eol - 7)                              = 0;
@@ -369,18 +391,26 @@
     if (!beenthere) {
         if (!filename) {
             logg("STDIN: noreply from clamd\n.");
-            return -1;
+            infected = -1;
+            goto done;
         }
         if (CLAMSTAT(filename, &sb) == -1) {
             logg("~%s: stat() failed with %s, clamd may not be responding\n",
                  filename, strerror(errno));
-            return -1;
+            infected = -1;
+            goto done;
         }
         if (!S_ISDIR(sb.st_mode)) {
             logg("~%s: no reply from clamd\n", filename);
-            return -1;
+            infected = -1;
+            goto done;
         }
     }
+
+done:
+    if (NULL != real_filename) {
+        free(real_filename);
+    }
     return infected;
 }
 
diff -Nru clamav-0.102.3+dfsg/clamonacc/client/protocol.c 
clamav-0.102.4+dfsg/clamonacc/client/protocol.c
--- clamav-0.102.3+dfsg/clamonacc/client/protocol.c     2020-05-12 
03:54:49.000000000 +0200
+++ clamav-0.102.4+dfsg/clamonacc/client/protocol.c     2020-07-15 
23:54:36.000000000 +0200
@@ -54,6 +54,7 @@
 #endif
 
 #include "libclamav/clamav.h"
+#include "libclamav/others.h"
 #include "shared/actions.h"
 #include "shared/output.h"
 #include "shared/misc.h"
@@ -191,6 +192,22 @@
 
     onas_recvlninit(&rcv, curl);
 
+    cl_error_t ret;
+    char *real_filename = NULL;
+
+    if (filename) {
+        ret = cli_realpath((const char *) filename, &real_filename);
+        if (CL_SUCCESS != ret) {
+            logg("Failed to determine real filename of %s.\n", filename);
+            if (ret_code) {
+                *ret_code = CL_EACCES;
+            }
+            infected = -1;
+            goto done;
+        }
+        filename = real_filename;
+    }
+
     if (ret_code) {
         *ret_code = CL_SUCCESS;
     }
@@ -204,7 +221,8 @@
                 if (ret_code) {
                     *ret_code = CL_ENULLARG;
                 }
-                return -1;
+                infected = -1;
+                goto done;
             }
             len = strlen(filename) + strlen(scancmd[scantype]) + 3;
             if (!(bol = malloc(len))) {
@@ -212,7 +230,8 @@
                 if (ret_code) {
                     *ret_code = CL_EMEM;
                 }
-                return -1;
+                infected = -1;
+                goto done;
             }
             sprintf(bol, "z%s %s", scancmd[scantype], filename);
             if (onas_sendln(curl, bol, len, timeout)) {
@@ -220,7 +239,8 @@
                     *ret_code = CL_EWRITE;
                 }
                 free(bol);
-                return -1;
+                infected = -1;
+                goto done;
             }
             free(bol);
             break;
@@ -241,7 +261,8 @@
         *printok = 0;
         if (errors)
             (*errors)++;
-        return len;
+        infected = len;
+        goto done;
     }
 
     while ((len = onas_recvln(&rcv, &bol, &eol, timeout))) {
@@ -249,7 +270,8 @@
             if (ret_code) {
                 *ret_code = CL_EREAD;
             }
-            return -1;
+            infected = -1;
+            goto done;
         }
         beenthere = 1;
         if (!filename) {
@@ -281,7 +303,8 @@
                 if (ret_code) {
                     *ret_code = CL_EPARSE;
                 }
-                return -1;
+                infected = -1;
+                goto done;
 
             } else if (!memcmp(eol - 7, " FOUND", 6)) {
                 static char last_filename[PATH_MAX + 1] = {'\0'};
@@ -378,7 +401,8 @@
             if (ret_code) {
                 *ret_code = CL_EACCES;
             }
-            return -1;
+            infected = -1;
+            goto done;
         }
         if (CLAMSTAT(filename, &sb) == -1) {
             logg("~%s: stat() failed with %s, clamd may not be responding\n",
@@ -386,15 +410,22 @@
             if (ret_code) {
                 *ret_code = CL_EACCES;
             }
-            return -1;
+            infected = -1;
+            goto done;
         }
         if (!S_ISDIR(sb.st_mode)) {
             logg("~%s: no reply from clamd\n", filename);
             if (ret_code) {
                 *ret_code = CL_EACCES;
             }
-            return -1;
+            infected = -1;
+            goto done;
         }
     }
+
+done:
+    if (NULL != real_filename) {
+        free(real_filename);
+    }
     return infected;
 }
diff -Nru clamav-0.102.3+dfsg/clamscan/manager.c 
clamav-0.102.4+dfsg/clamscan/manager.c
--- clamav-0.102.3+dfsg/clamscan/manager.c      2020-05-12 03:54:49.000000000 
+0200
+++ clamav-0.102.4+dfsg/clamscan/manager.c      2020-07-15 23:54:36.000000000 
+0200
@@ -287,7 +287,8 @@
 
 static void scanfile(const char *filename, struct cl_engine *engine, const 
struct optstruct *opts, struct cl_scan_options *options)
 {
-    int ret = 0, fd, included;
+    cl_error_t ret = CL_SUCCESS;
+    int fd, included;
     unsigned i;
     const struct optstruct *opt;
     const char *virname = NULL;
@@ -295,13 +296,28 @@
     struct metachain chain;
     struct clamscan_cb_data data;
 
+    char *real_filename = NULL;
+
+    if (NULL == filename || NULL == engine || NULL == opts || NULL == options) 
{
+        logg("scanfile: Invalid args.\n");
+        ret = CL_EARG;
+        goto done;
+    }
+
+    ret = cli_realpath((const char *) filename, &real_filename);
+    if (CL_SUCCESS != ret) {
+        logg("Failed to determine real filename of %s.\n", filename);
+        goto done;
+    }
+    filename = real_filename;
+
     if ((opt = optget(opts, "exclude"))->enabled) {
         while (opt) {
             if (match_regex(filename, opt->strarg) == 1) {
                 if (!printinfected)
                     logg("~%s: Excluded\n", filename);
 
-                return;
+                goto done;
             }
 
             opt = opt->nextarg;
@@ -324,7 +340,7 @@
             if (!printinfected)
                 logg("~%s: Excluded\n", filename);
 
-            return;
+            goto done;
         }
     }
 
@@ -335,14 +351,14 @@
             if (!printinfected)
                 logg("~%s: Excluded (/proc)\n", filename);
 
-            return;
+            goto done;
         }
 #endif
         if (!sb.st_size) {
             if (!printinfected)
                 logg("~%s: Empty file\n", filename);
 
-            return;
+            goto done;
         }
 
         info.rblocks += sb.st_size / CL_COUNT_PRECISION;
@@ -355,7 +371,7 @@
                 logg("~%s: Access denied\n", filename);
 
             info.errors++;
-            return;
+            goto done;
         }
     }
 #endif
@@ -369,7 +385,7 @@
                 free(chain.chains);
                 logg("Unable to allocate memory in scanfile()\n");
                 info.errors++;
-                return;
+                goto done;
             }
             chain.nchains = 1;
         }
@@ -380,7 +396,7 @@
     if ((fd = safe_open(filename, O_RDONLY | O_BINARY)) == -1) {
         logg("^Can't open file %s: %s\n", filename, strerror(errno));
         info.errors++;
-        return;
+        goto done;
     }
 
     data.chain    = &chain;
@@ -421,6 +437,12 @@
 
     if (ret == CL_VIRUS && action)
         action(filename);
+
+done:
+    if (NULL != real_filename) {
+        free(real_filename);
+    }
+    return;
 }
 
 static void scandirs(const char *dirname, struct cl_engine *engine, const 
struct optstruct *opts, struct cl_scan_options *options, unsigned int depth, 
dev_t dev)
diff -Nru clamav-0.102.3+dfsg/configure clamav-0.102.4+dfsg/configure
--- clamav-0.102.3+dfsg/configure       2020-05-12 03:54:49.000000000 +0200
+++ clamav-0.102.4+dfsg/configure       2020-07-15 23:54:36.000000000 +0200
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for ClamAV 0.102.3.
+# Generated by GNU Autoconf 2.69 for ClamAV 0.102.4.
 #
 # Report bugs to <https://bugzilla.clamav.net/>.
 #
@@ -592,8 +592,8 @@
 # Identity of this package.
 PACKAGE_NAME='ClamAV'
 PACKAGE_TARNAME='clamav'
-PACKAGE_VERSION='0.102.3'
-PACKAGE_STRING='ClamAV 0.102.3'
+PACKAGE_VERSION='0.102.4'
+PACKAGE_STRING='ClamAV 0.102.4'
 PACKAGE_BUGREPORT='https://bugzilla.clamav.net/'
 PACKAGE_URL='https://www.clamav.net/'
 
@@ -1601,7 +1601,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures ClamAV 0.102.3 to adapt to many kinds of systems.
+\`configure' configures ClamAV 0.102.4 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1682,7 +1682,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of ClamAV 0.102.3:";;
+     short | recursive ) echo "Configuration of ClamAV 0.102.4:";;
    esac
   cat <<\_ACEOF
   --enable-dependency-tracking
@@ -1911,7 +1911,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-ClamAV configure 0.102.3
+ClamAV configure 0.102.4
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2539,7 +2539,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by ClamAV $as_me 0.102.3, which was
+It was created by ClamAV $as_me 0.102.4, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -4297,7 +4297,7 @@
 
 # Define the identity of the package.
  PACKAGE='clamav'
- VERSION='0.102.3'
+ VERSION='0.102.4'
 
 
 # Some tools Automake needs.
@@ -6025,7 +6025,7 @@
 $as_echo "#define PACKAGE PACKAGE_NAME" >>confdefs.h
 
 
-VERSION="0.102.3"
+VERSION="0.102.4"
 
 major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/^0-9//g"`
 minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/^0-9//g"`
@@ -31630,7 +31630,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by ClamAV $as_me 0.102.3, which was
+This file was extended by ClamAV $as_me 0.102.4, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -31697,7 +31697,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; 
s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-ClamAV config.status 0.102.3
+ClamAV config.status 0.102.4
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
@@ -34548,7 +34548,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by ClamAV $as_me 0.102.3, which was
+This file was extended by ClamAV $as_me 0.102.4, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -34615,7 +34615,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; 
s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-ClamAV config.status 0.102.3
+ClamAV config.status 0.102.4
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff -Nru clamav-0.102.3+dfsg/configure.ac clamav-0.102.4+dfsg/configure.ac
--- clamav-0.102.3+dfsg/configure.ac    2020-05-16 11:23:53.000000000 +0200
+++ clamav-0.102.4+dfsg/configure.ac    2020-07-17 20:19:54.000000000 +0200
@@ -22,7 +22,7 @@
 
 dnl For a release change [devel] to the real version [0.xy]
 dnl also change VERSION below
-AC_INIT([ClamAV], [0.102.3], [https://bugzilla.clamav.net/], [clamav], 
[https://www.clamav.net/])
+AC_INIT([ClamAV], [0.102.4], [https://bugzilla.clamav.net/], [clamav], 
[https://www.clamav.net/])
 
 dnl put configure auxiliary into config
 AC_CONFIG_AUX_DIR([config])
diff -Nru clamav-0.102.3+dfsg/debian/changelog 
clamav-0.102.4+dfsg/debian/changelog
--- clamav-0.102.3+dfsg/debian/changelog        2020-05-30 00:07:05.000000000 
+0200
+++ clamav-0.102.4+dfsg/debian/changelog        2020-07-18 00:22:32.000000000 
+0200
@@ -1,3 +1,13 @@
+clamav (0.102.4+dfsg-0+deb10u1) buster; urgency=medium
+
+  * Import 0.102.4
+    - CVE-2020-3350 (A malicious user trick clamav into moving a different 
file).
+    - CVE-2020-3327 (A vulnerability in the ARJ archive parsing module).
+    - CVE-2020-3481 (A vulnerability in the EGG archive module).
+  * Update symbol file.
+
+ -- Sebastian Andrzej Siewior <sebast...@breakpoint.cc>  Sat, 18 Jul 2020 
00:22:32 +0200
+
 clamav (0.102.3+dfsg-0+deb10u1) buster; urgency=medium
 
   [ Sebastian Andrzej Siewior ]
diff -Nru clamav-0.102.3+dfsg/debian/.git-dpm 
clamav-0.102.4+dfsg/debian/.git-dpm
--- clamav-0.102.3+dfsg/debian/.git-dpm 2020-05-30 00:03:59.000000000 +0200
+++ clamav-0.102.4+dfsg/debian/.git-dpm 2020-07-18 00:19:32.000000000 +0200
@@ -1,8 +1,8 @@
 # see git-dpm(1) from git-dpm package
-04fd79ea5eace5273a13bd66b095e2fef0ea3bff
-04fd79ea5eace5273a13bd66b095e2fef0ea3bff
-07c9b9ef63bc584a39143a6cd002d199d1d46397
-07c9b9ef63bc584a39143a6cd002d199d1d46397
-clamav_0.102.3+dfsg.orig.tar.xz
-694c77d0aed527d3d135a3ccd7e30729fff55404
-5018320
+c07899f43b92f63e9ad0ccefa5379ca649603d4a
+c07899f43b92f63e9ad0ccefa5379ca649603d4a
+2e5f12d74d7065a47a1cf072e703445b81878e07
+2e5f12d74d7065a47a1cf072e703445b81878e07
+clamav_0.102.4+dfsg.orig.tar.xz
+a139e4b00726fbd97ad88c7b65e88000ebee38ab
+5023528
diff -Nru clamav-0.102.3+dfsg/debian/libclamav9.symbols 
clamav-0.102.4+dfsg/debian/libclamav9.symbols
--- clamav-0.102.3+dfsg/debian/libclamav9.symbols       2020-05-24 
13:13:40.000000000 +0200
+++ clamav-0.102.4+dfsg/debian/libclamav9.symbols       2020-07-18 
00:22:08.000000000 +0200
@@ -1,20 +1,20 @@
 libclamav.so.9 libclamav9 #MINVER#
 * Build-Depends-Package: libclamav-dev
- CLAMAV_PRIVATE@CLAMAV_PRIVATE 0.102.3
+ CLAMAV_PRIVATE@CLAMAV_PRIVATE 0.102.4
  CLAMAV_PUBLIC@CLAMAV_PUBLIC 0.101.0
- __cli_strcasestr@CLAMAV_PRIVATE 0.102.3
- __cli_strndup@CLAMAV_PRIVATE 0.102.3
- __cli_strnlen@CLAMAV_PRIVATE 0.102.3
- __cli_strnstr@CLAMAV_PRIVATE 0.102.3
- base64Flush@CLAMAV_PRIVATE 0.102.3
- blobAddData@CLAMAV_PRIVATE 0.102.3
- blobCreate@CLAMAV_PRIVATE 0.102.3
- blobDestroy@CLAMAV_PRIVATE 0.102.3
- cl_ASN1_GetTimeT@CLAMAV_PRIVATE 0.102.3
+ __cli_strcasestr@CLAMAV_PRIVATE 0.102.4
+ __cli_strndup@CLAMAV_PRIVATE 0.102.4
+ __cli_strnlen@CLAMAV_PRIVATE 0.102.4
+ __cli_strnstr@CLAMAV_PRIVATE 0.102.4
+ base64Flush@CLAMAV_PRIVATE 0.102.4
+ blobAddData@CLAMAV_PRIVATE 0.102.4
+ blobCreate@CLAMAV_PRIVATE 0.102.4
+ blobDestroy@CLAMAV_PRIVATE 0.102.4
+ cl_ASN1_GetTimeT@CLAMAV_PRIVATE 0.102.4
  cl_always_gen_section_hash@CLAMAV_PUBLIC 0.101.0
- cl_base64_decode@CLAMAV_PRIVATE 0.102.3
- cl_base64_encode@CLAMAV_PRIVATE 0.102.3
- cl_cleanup_crypto@CLAMAV_PRIVATE 0.102.3
+ cl_base64_decode@CLAMAV_PRIVATE 0.102.4
+ cl_base64_encode@CLAMAV_PRIVATE 0.102.4
+ cl_cleanup_crypto@CLAMAV_PRIVATE 0.102.4
  cl_countsigs@CLAMAV_PUBLIC 0.101.0
  cl_cvdfree@CLAMAV_PUBLIC 0.101.0
  cl_cvdhead@CLAMAV_PUBLIC 0.101.0
@@ -54,19 +54,19 @@
  cl_fmap_close@CLAMAV_PUBLIC 0.101.0
  cl_fmap_open_handle@CLAMAV_PUBLIC 0.101.0
  cl_fmap_open_memory@CLAMAV_PUBLIC 0.101.0
- cl_get_pkey_file@CLAMAV_PRIVATE 0.102.3
- cl_get_x509_from_mem@CLAMAV_PRIVATE 0.102.3
- cl_hash_data@CLAMAV_PRIVATE 0.102.3
+ cl_get_pkey_file@CLAMAV_PRIVATE 0.102.4
+ cl_get_x509_from_mem@CLAMAV_PRIVATE 0.102.4
+ cl_hash_data@CLAMAV_PRIVATE 0.102.4
  cl_hash_destroy@CLAMAV_PUBLIC 0.101.0
- cl_hash_file_fd@CLAMAV_PRIVATE 0.102.3
- cl_hash_file_fd_ctx@CLAMAV_PRIVATE 0.102.3
- cl_hash_file_fp@CLAMAV_PRIVATE 0.102.3
+ cl_hash_file_fd@CLAMAV_PRIVATE 0.102.4
+ cl_hash_file_fd_ctx@CLAMAV_PRIVATE 0.102.4
+ cl_hash_file_fp@CLAMAV_PRIVATE 0.102.4
  cl_hash_init@CLAMAV_PUBLIC 0.101.0
  cl_init@CLAMAV_PUBLIC 0.101.0
- cl_initialize_crypto@CLAMAV_PRIVATE 0.102.3
+ cl_initialize_crypto@CLAMAV_PRIVATE 0.102.4
  cl_load@CLAMAV_PUBLIC 0.101.0
- cl_load_cert@CLAMAV_PRIVATE 0.102.3
- cl_load_crl@CLAMAV_PRIVATE 0.102.3
+ cl_load_cert@CLAMAV_PRIVATE 0.102.4
+ cl_load_crl@CLAMAV_PRIVATE 0.102.4
  cl_retdbdir@CLAMAV_PUBLIC 0.101.0
  cl_retflevel@CLAMAV_PUBLIC 0.102.3
  cl_retver@CLAMAV_PUBLIC 0.101.0
@@ -76,188 +76,191 @@
  cl_scanfile_callback@CLAMAV_PUBLIC 0.101.0
  cl_scanmap_callback@CLAMAV_PUBLIC 0.101.0
  cl_set_clcb_msg@CLAMAV_PUBLIC 0.101.0
- cl_sha1@CLAMAV_PRIVATE 0.102.3
- cl_sha256@CLAMAV_PRIVATE 0.102.3
- cl_sign_data@CLAMAV_PRIVATE 0.102.3
- cl_sign_data_keyfile@CLAMAV_PRIVATE 0.102.3
- cl_sign_file_fd@CLAMAV_PRIVATE 0.102.3
- cl_sign_file_fp@CLAMAV_PRIVATE 0.102.3
+ cl_sha1@CLAMAV_PRIVATE 0.102.4
+ cl_sha256@CLAMAV_PRIVATE 0.102.4
+ cl_sign_data@CLAMAV_PRIVATE 0.102.4
+ cl_sign_data_keyfile@CLAMAV_PRIVATE 0.102.4
+ cl_sign_file_fd@CLAMAV_PRIVATE 0.102.4
+ cl_sign_file_fp@CLAMAV_PRIVATE 0.102.4
  cl_statchkdir@CLAMAV_PUBLIC 0.101.0
  cl_statfree@CLAMAV_PUBLIC 0.101.0
  cl_statinidir@CLAMAV_PUBLIC 0.101.0
  cl_strerror@CLAMAV_PUBLIC 0.101.0
  cl_update_hash@CLAMAV_PUBLIC 0.101.0
- cl_validate_certificate_chain@CLAMAV_PRIVATE 0.102.3
- cl_validate_certificate_chain_ts_dir@CLAMAV_PRIVATE 0.102.3
- cl_verify_signature@CLAMAV_PRIVATE 0.102.3
- cl_verify_signature_fd@CLAMAV_PRIVATE 0.102.3
- cl_verify_signature_fd_x509@CLAMAV_PRIVATE 0.102.3
- cl_verify_signature_fd_x509_keyfile@CLAMAV_PRIVATE 0.102.3
- cl_verify_signature_hash@CLAMAV_PRIVATE 0.102.3
- cl_verify_signature_hash_x509@CLAMAV_PRIVATE 0.102.3
- cl_verify_signature_hash_x509_keyfile@CLAMAV_PRIVATE 0.102.3
- cl_verify_signature_x509@CLAMAV_PRIVATE 0.102.3
- cl_verify_signature_x509_keyfile@CLAMAV_PRIVATE 0.102.3
- cli_ac_buildtrie@CLAMAV_PRIVATE 0.102.3
- cli_ac_chklsig@CLAMAV_PRIVATE 0.102.3
- cli_ac_free@CLAMAV_PRIVATE 0.102.3
- cli_ac_freedata@CLAMAV_PRIVATE 0.102.3
- cli_ac_init@CLAMAV_PRIVATE 0.102.3
- cli_ac_initdata@CLAMAV_PRIVATE 0.102.3
- cli_ac_scanbuff@CLAMAV_PRIVATE 0.102.3
- cli_bm_free@CLAMAV_PRIVATE 0.102.3
- cli_bm_init@CLAMAV_PRIVATE 0.102.3
- cli_bm_scanbuff@CLAMAV_PRIVATE 0.102.3
- cli_build_regex_list@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_context_alloc@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_context_clear@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_context_destroy@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_context_getresult_int@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_context_set_trace@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_context_setfile@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_context_setfuncid@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_context_setparam_int@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_context_setparam_ptr@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_debug@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_debug_printsrc@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_describe@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_destroy@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_done@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_init@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_load@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_prepare2@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_printversion@CLAMAV_PRIVATE 0.102.3
- cli_bytecode_run@CLAMAV_PRIVATE 0.102.3
- cli_bytefunc_describe@CLAMAV_PRIVATE 0.102.3
- cli_byteinst_describe@CLAMAV_PRIVATE 0.102.3
- cli_bytetype_describe@CLAMAV_PRIVATE 0.102.3
- cli_bytevalue_describe@CLAMAV_PRIVATE 0.102.3
- cli_calloc@CLAMAV_PRIVATE 0.102.3
- cli_check_auth_header@CLAMAV_PRIVATE 0.102.3
- cli_chomp@CLAMAV_PRIVATE 0.102.3
- cli_ctime@CLAMAV_PRIVATE 0.102.3
- cli_cvdunpack@CLAMAV_PRIVATE 0.102.3
- cli_dbgmsg_internal@CLAMAV_PRIVATE 0.102.3
- cli_dconf_init@CLAMAV_PRIVATE 0.102.3
- cli_debug_flag@CLAMAV_PRIVATE 0.102.3
- cli_detect_environment@CLAMAV_PRIVATE 0.102.3
- cli_disasm_one@CLAMAV_PRIVATE 0.102.3
- cli_errmsg@CLAMAV_PRIVATE 0.102.3
- cli_filecopy@CLAMAV_PRIVATE 0.102.3
- cli_fmap_scandesc@CLAMAV_PRIVATE 0.102.3
- cli_free_vba_project@CLAMAV_PRIVATE 0.102.3
- cli_ftw@CLAMAV_PRIVATE 0.102.3
- cli_genhash_pe@CLAMAV_PRIVATE 0.102.3
- cli_gentemp@CLAMAV_PRIVATE 0.102.3
- cli_gentemp_with_prefix@CLAMAV_PRIVATE 0.102.3
- cli_gentempfd@CLAMAV_PRIVATE 0.102.3
- cli_gettmpdir@CLAMAV_PRIVATE 0.102.3
- cli_hashfile@CLAMAV_PRIVATE 0.102.3
- cli_hashset_destroy@CLAMAV_PRIVATE 0.102.3
- cli_hashstream@CLAMAV_PRIVATE 0.102.3
- cli_hex2str@CLAMAV_PRIVATE 0.102.3
- cli_hex2ui@CLAMAV_PRIVATE 0.102.3
- cli_initroots@CLAMAV_PRIVATE 0.102.3
- cli_isnumber@CLAMAV_PRIVATE 0.102.3
- cli_js_destroy@CLAMAV_PRIVATE 0.102.3
- cli_js_init@CLAMAV_PRIVATE 0.102.3
- cli_js_output@CLAMAV_PRIVATE 0.102.3
- cli_js_parse_done@CLAMAV_PRIVATE 0.102.3
- cli_js_process_buffer@CLAMAV_PRIVATE 0.102.3
- cli_ldbtokenize@CLAMAV_PRIVATE 0.102.3
- cli_malloc@CLAMAV_PRIVATE 0.102.3
- cli_memstr@CLAMAV_PRIVATE 0.102.3
- cli_ole2_extract@CLAMAV_PRIVATE 0.102.3
- cli_parse_add@CLAMAV_PRIVATE 0.102.3
- cli_pcre_build@CLAMAV_PRIVATE 0.102.3
- cli_pcre_freeoff@CLAMAV_PRIVATE 0.102.3
- cli_pcre_init@CLAMAV_PRIVATE 0.102.3
- cli_pcre_perf_events_destroy@CLAMAV_PRIVATE 0.102.3
- cli_pcre_perf_print@CLAMAV_PRIVATE 0.102.3
- cli_pcre_recaloff@CLAMAV_PRIVATE 0.102.3
- cli_pcre_scanbuf@CLAMAV_PRIVATE 0.102.3
- cli_ppt_vba_read@CLAMAV_PRIVATE 0.102.3
- cli_printcxxver@CLAMAV_PRIVATE 0.102.3
- cli_readn@CLAMAV_PRIVATE 0.102.3
- cli_realloc@CLAMAV_PRIVATE 0.102.3
- cli_regcomp@CLAMAV_PRIVATE 0.102.3
- cli_regex2suffix@CLAMAV_PRIVATE 0.102.3
- cli_regexec@CLAMAV_PRIVATE 0.102.3
- cli_regfree@CLAMAV_PRIVATE 0.102.3
- cli_rmdirs@CLAMAV_PRIVATE 0.102.3
- cli_rndnum@CLAMAV_PRIVATE 0.102.3
- cli_sanitize_filepath@CLAMAV_PRIVATE 0.102.3
- cli_scanbuff@CLAMAV_PRIVATE 0.102.3
- cli_sigopts_handler@CLAMAV_PRIVATE 0.102.3
- cli_sigperf_events_destroy@CLAMAV_PRIVATE 0.102.3
- cli_sigperf_print@CLAMAV_PRIVATE 0.102.3
- cli_str2hex@CLAMAV_PRIVATE 0.102.3
- cli_strbcasestr@CLAMAV_PRIVATE 0.102.3
- cli_strdup@CLAMAV_PRIVATE 0.102.3
- cli_strerror@CLAMAV_PRIVATE 0.102.3
- cli_strlcat@CLAMAV_PRIVATE 0.102.3
- cli_strlcpy@CLAMAV_PRIVATE 0.102.3
- cli_strrcpy@CLAMAV_PRIVATE 0.102.3
- cli_strtok@CLAMAV_PRIVATE 0.102.3
- cli_strtokbuf@CLAMAV_PRIVATE 0.102.3
- cli_strtokenize@CLAMAV_PRIVATE 0.102.3
- cli_textbuffer_append_normalize@CLAMAV_PRIVATE 0.102.3
- cli_unescape@CLAMAV_PRIVATE 0.102.3
- cli_unlink@CLAMAV_PRIVATE 0.102.3
- cli_url_canon@CLAMAV_PRIVATE 0.102.3
- cli_utf16_to_utf8@CLAMAV_PRIVATE 0.102.3
- cli_utf16toascii@CLAMAV_PRIVATE 0.102.3
- cli_vba_inflate@CLAMAV_PRIVATE 0.102.3
- cli_vba_readdir@CLAMAV_PRIVATE 0.102.3
- cli_versig2@CLAMAV_PRIVATE 0.102.3
- cli_versig@CLAMAV_PRIVATE 0.102.3
- cli_warnmsg@CLAMAV_PRIVATE 0.102.3
- cli_wm_decrypt_macro@CLAMAV_PRIVATE 0.102.3
- cli_wm_readdir@CLAMAV_PRIVATE 0.102.3
- cli_writen@CLAMAV_PRIVATE 0.102.3
- decodeLine@CLAMAV_PRIVATE 0.102.3
- disasmbuf@CLAMAV_PRIVATE 0.102.3
- fmap@CLAMAV_PRIVATE 0.102.3
- get_fpu_endian@CLAMAV_PRIVATE 0.102.3
- have_clamjit@CLAMAV_PRIVATE 0.102.3
- have_rar@CLAMAV_PRIVATE 0.102.3
- html_normalise_map@CLAMAV_PRIVATE 0.102.3
- html_normalise_mem@CLAMAV_PRIVATE 0.102.3
- html_screnc_decode@CLAMAV_PRIVATE 0.102.3
- html_tag_arg_free@CLAMAV_PRIVATE 0.102.3
- init_domainlist@CLAMAV_PRIVATE 0.102.3
- init_regex_list@CLAMAV_PRIVATE 0.102.3
- init_whitelist@CLAMAV_PRIVATE 0.102.3
- is_regex_ok@CLAMAV_PRIVATE 0.102.3
- load_regex_matcher@CLAMAV_PRIVATE 0.102.3
+ cl_validate_certificate_chain@CLAMAV_PRIVATE 0.102.4
+ cl_validate_certificate_chain_ts_dir@CLAMAV_PRIVATE 0.102.4
+ cl_verify_signature@CLAMAV_PRIVATE 0.102.4
+ cl_verify_signature_fd@CLAMAV_PRIVATE 0.102.4
+ cl_verify_signature_fd_x509@CLAMAV_PRIVATE 0.102.4
+ cl_verify_signature_fd_x509_keyfile@CLAMAV_PRIVATE 0.102.4
+ cl_verify_signature_hash@CLAMAV_PRIVATE 0.102.4
+ cl_verify_signature_hash_x509@CLAMAV_PRIVATE 0.102.4
+ cl_verify_signature_hash_x509_keyfile@CLAMAV_PRIVATE 0.102.4
+ cl_verify_signature_x509@CLAMAV_PRIVATE 0.102.4
+ cl_verify_signature_x509_keyfile@CLAMAV_PRIVATE 0.102.4
+ cli_ac_buildtrie@CLAMAV_PRIVATE 0.102.4
+ cli_ac_chklsig@CLAMAV_PRIVATE 0.102.4
+ cli_ac_free@CLAMAV_PRIVATE 0.102.4
+ cli_ac_freedata@CLAMAV_PRIVATE 0.102.4
+ cli_ac_init@CLAMAV_PRIVATE 0.102.4
+ cli_ac_initdata@CLAMAV_PRIVATE 0.102.4
+ cli_ac_scanbuff@CLAMAV_PRIVATE 0.102.4
+ cli_basename@CLAMAV_PRIVATE 0.102.4
+ cli_bm_free@CLAMAV_PRIVATE 0.102.4
+ cli_bm_init@CLAMAV_PRIVATE 0.102.4
+ cli_bm_scanbuff@CLAMAV_PRIVATE 0.102.4
+ cli_build_regex_list@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_context_alloc@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_context_clear@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_context_destroy@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_context_getresult_int@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_context_set_trace@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_context_setfile@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_context_setfuncid@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_context_setparam_int@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_context_setparam_ptr@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_debug@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_debug_printsrc@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_describe@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_destroy@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_done@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_init@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_load@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_prepare2@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_printversion@CLAMAV_PRIVATE 0.102.4
+ cli_bytecode_run@CLAMAV_PRIVATE 0.102.4
+ cli_bytefunc_describe@CLAMAV_PRIVATE 0.102.4
+ cli_byteinst_describe@CLAMAV_PRIVATE 0.102.4
+ cli_bytetype_describe@CLAMAV_PRIVATE 0.102.4
+ cli_bytevalue_describe@CLAMAV_PRIVATE 0.102.4
+ cli_calloc@CLAMAV_PRIVATE 0.102.4
+ cli_check_auth_header@CLAMAV_PRIVATE 0.102.4
+ cli_chomp@CLAMAV_PRIVATE 0.102.4
+ cli_codepage_to_utf8@CLAMAV_PRIVATE 0.102.4
+ cli_ctime@CLAMAV_PRIVATE 0.102.4
+ cli_cvdunpack@CLAMAV_PRIVATE 0.102.4
+ cli_dbgmsg_internal@CLAMAV_PRIVATE 0.102.4
+ cli_dconf_init@CLAMAV_PRIVATE 0.102.4
+ cli_debug_flag@CLAMAV_PRIVATE 0.102.4
+ cli_detect_environment@CLAMAV_PRIVATE 0.102.4
+ cli_disasm_one@CLAMAV_PRIVATE 0.102.4
+ cli_errmsg@CLAMAV_PRIVATE 0.102.4
+ cli_filecopy@CLAMAV_PRIVATE 0.102.4
+ cli_fmap_scandesc@CLAMAV_PRIVATE 0.102.4
+ cli_free_vba_project@CLAMAV_PRIVATE 0.102.4
+ cli_ftw@CLAMAV_PRIVATE 0.102.4
+ cli_genhash_pe@CLAMAV_PRIVATE 0.102.4
+ cli_gentemp@CLAMAV_PRIVATE 0.102.4
+ cli_gentemp_with_prefix@CLAMAV_PRIVATE 0.102.4
+ cli_gentempfd@CLAMAV_PRIVATE 0.102.4
+ cli_gettmpdir@CLAMAV_PRIVATE 0.102.4
+ cli_hashfile@CLAMAV_PRIVATE 0.102.4
+ cli_hashset_destroy@CLAMAV_PRIVATE 0.102.4
+ cli_hashstream@CLAMAV_PRIVATE 0.102.4
+ cli_hex2str@CLAMAV_PRIVATE 0.102.4
+ cli_hex2ui@CLAMAV_PRIVATE 0.102.4
+ cli_initroots@CLAMAV_PRIVATE 0.102.4
+ cli_isnumber@CLAMAV_PRIVATE 0.102.4
+ cli_js_destroy@CLAMAV_PRIVATE 0.102.4
+ cli_js_init@CLAMAV_PRIVATE 0.102.4
+ cli_js_output@CLAMAV_PRIVATE 0.102.4
+ cli_js_parse_done@CLAMAV_PRIVATE 0.102.4
+ cli_js_process_buffer@CLAMAV_PRIVATE 0.102.4
+ cli_ldbtokenize@CLAMAV_PRIVATE 0.102.4
+ cli_malloc@CLAMAV_PRIVATE 0.102.4
+ cli_memstr@CLAMAV_PRIVATE 0.102.4
+ cli_ole2_extract@CLAMAV_PRIVATE 0.102.4
+ cli_parse_add@CLAMAV_PRIVATE 0.102.4
+ cli_pcre_build@CLAMAV_PRIVATE 0.102.4
+ cli_pcre_freeoff@CLAMAV_PRIVATE 0.102.4
+ cli_pcre_init@CLAMAV_PRIVATE 0.102.4
+ cli_pcre_perf_events_destroy@CLAMAV_PRIVATE 0.102.4
+ cli_pcre_perf_print@CLAMAV_PRIVATE 0.102.4
+ cli_pcre_recaloff@CLAMAV_PRIVATE 0.102.4
+ cli_pcre_scanbuf@CLAMAV_PRIVATE 0.102.4
+ cli_ppt_vba_read@CLAMAV_PRIVATE 0.102.4
+ cli_printcxxver@CLAMAV_PRIVATE 0.102.4
+ cli_readn@CLAMAV_PRIVATE 0.102.4
+ cli_realloc@CLAMAV_PRIVATE 0.102.4
+ cli_realpath@CLAMAV_PRIVATE 0.102.4
+ cli_regcomp@CLAMAV_PRIVATE 0.102.4
+ cli_regex2suffix@CLAMAV_PRIVATE 0.102.4
+ cli_regexec@CLAMAV_PRIVATE 0.102.4
+ cli_regfree@CLAMAV_PRIVATE 0.102.4
+ cli_rmdirs@CLAMAV_PRIVATE 0.102.4
+ cli_rndnum@CLAMAV_PRIVATE 0.102.4
+ cli_sanitize_filepath@CLAMAV_PRIVATE 0.102.4
+ cli_scanbuff@CLAMAV_PRIVATE 0.102.4
+ cli_sigopts_handler@CLAMAV_PRIVATE 0.102.4
+ cli_sigperf_events_destroy@CLAMAV_PRIVATE 0.102.4
+ cli_sigperf_print@CLAMAV_PRIVATE 0.102.4
+ cli_str2hex@CLAMAV_PRIVATE 0.102.4
+ cli_strbcasestr@CLAMAV_PRIVATE 0.102.4
+ cli_strdup@CLAMAV_PRIVATE 0.102.4
+ cli_strerror@CLAMAV_PRIVATE 0.102.4
+ cli_strlcat@CLAMAV_PRIVATE 0.102.4
+ cli_strlcpy@CLAMAV_PRIVATE 0.102.4
+ cli_strrcpy@CLAMAV_PRIVATE 0.102.4
+ cli_strtok@CLAMAV_PRIVATE 0.102.4
+ cli_strtokbuf@CLAMAV_PRIVATE 0.102.4
+ cli_strtokenize@CLAMAV_PRIVATE 0.102.4
+ cli_textbuffer_append_normalize@CLAMAV_PRIVATE 0.102.4
+ cli_unescape@CLAMAV_PRIVATE 0.102.4
+ cli_unlink@CLAMAV_PRIVATE 0.102.4
+ cli_url_canon@CLAMAV_PRIVATE 0.102.4
+ cli_utf16_to_utf8@CLAMAV_PRIVATE 0.102.4
+ cli_utf16toascii@CLAMAV_PRIVATE 0.102.4
+ cli_vba_inflate@CLAMAV_PRIVATE 0.102.4
+ cli_vba_readdir@CLAMAV_PRIVATE 0.102.4
+ cli_versig2@CLAMAV_PRIVATE 0.102.4
+ cli_versig@CLAMAV_PRIVATE 0.102.4
+ cli_warnmsg@CLAMAV_PRIVATE 0.102.4
+ cli_wm_decrypt_macro@CLAMAV_PRIVATE 0.102.4
+ cli_wm_readdir@CLAMAV_PRIVATE 0.102.4
+ cli_writen@CLAMAV_PRIVATE 0.102.4
+ decodeLine@CLAMAV_PRIVATE 0.102.4
+ disasmbuf@CLAMAV_PRIVATE 0.102.4
+ fmap@CLAMAV_PRIVATE 0.102.4
+ get_fpu_endian@CLAMAV_PRIVATE 0.102.4
+ have_clamjit@CLAMAV_PRIVATE 0.102.4
+ have_rar@CLAMAV_PRIVATE 0.102.4
+ html_normalise_map@CLAMAV_PRIVATE 0.102.4
+ html_normalise_mem@CLAMAV_PRIVATE 0.102.4
+ html_screnc_decode@CLAMAV_PRIVATE 0.102.4
+ html_tag_arg_free@CLAMAV_PRIVATE 0.102.4
+ init_domainlist@CLAMAV_PRIVATE 0.102.4
+ init_regex_list@CLAMAV_PRIVATE 0.102.4
+ init_whitelist@CLAMAV_PRIVATE 0.102.4
+ is_regex_ok@CLAMAV_PRIVATE 0.102.4
+ load_regex_matcher@CLAMAV_PRIVATE 0.102.4
  lsig_sub_matched@CLAMAV_PUBLIC 0.101.0
- messageCreate@CLAMAV_PRIVATE 0.102.3
- messageDestroy@CLAMAV_PRIVATE 0.102.3
- mpool_calloc@CLAMAV_PRIVATE 0.102.3
- mpool_create@CLAMAV_PRIVATE 0.102.3
- mpool_destroy@CLAMAV_PRIVATE 0.102.3
- mpool_free@CLAMAV_PRIVATE 0.102.3
- mpool_getstats@CLAMAV_PRIVATE 0.102.3
- phishingScan@CLAMAV_PRIVATE 0.102.3
- phishing_done@CLAMAV_PRIVATE 0.102.3
- phishing_init@CLAMAV_PRIVATE 0.102.3
- regex_list_add_pattern@CLAMAV_PRIVATE 0.102.3
- regex_list_done@CLAMAV_PRIVATE 0.102.3
- regex_list_match@CLAMAV_PRIVATE 0.102.3
- tableCreate@CLAMAV_PRIVATE 0.102.3
- tableDestroy@CLAMAV_PRIVATE 0.102.3
- tableFind@CLAMAV_PRIVATE 0.102.3
- tableInsert@CLAMAV_PRIVATE 0.102.3
- tableIterate@CLAMAV_PRIVATE 0.102.3
- tableRemove@CLAMAV_PRIVATE 0.102.3
- tableUpdate@CLAMAV_PRIVATE 0.102.3
- text_normalize_init@CLAMAV_PRIVATE 0.102.3
- text_normalize_map@CLAMAV_PRIVATE 0.102.3
- text_normalize_reset@CLAMAV_PRIVATE 0.102.3
- uniq_add@CLAMAV_PRIVATE 0.102.3
- uniq_free@CLAMAV_PRIVATE 0.102.3
- uniq_get@CLAMAV_PRIVATE 0.102.3
- uniq_init@CLAMAV_PRIVATE 0.102.3
+ messageCreate@CLAMAV_PRIVATE 0.102.4
+ messageDestroy@CLAMAV_PRIVATE 0.102.4
+ mpool_calloc@CLAMAV_PRIVATE 0.102.4
+ mpool_create@CLAMAV_PRIVATE 0.102.4
+ mpool_destroy@CLAMAV_PRIVATE 0.102.4
+ mpool_free@CLAMAV_PRIVATE 0.102.4
+ mpool_getstats@CLAMAV_PRIVATE 0.102.4
+ phishingScan@CLAMAV_PRIVATE 0.102.4
+ phishing_done@CLAMAV_PRIVATE 0.102.4
+ phishing_init@CLAMAV_PRIVATE 0.102.4
+ regex_list_add_pattern@CLAMAV_PRIVATE 0.102.4
+ regex_list_done@CLAMAV_PRIVATE 0.102.4
+ regex_list_match@CLAMAV_PRIVATE 0.102.4
+ tableCreate@CLAMAV_PRIVATE 0.102.4
+ tableDestroy@CLAMAV_PRIVATE 0.102.4
+ tableFind@CLAMAV_PRIVATE 0.102.4
+ tableInsert@CLAMAV_PRIVATE 0.102.4
+ tableIterate@CLAMAV_PRIVATE 0.102.4
+ tableRemove@CLAMAV_PRIVATE 0.102.4
+ tableUpdate@CLAMAV_PRIVATE 0.102.4
+ text_normalize_init@CLAMAV_PRIVATE 0.102.4
+ text_normalize_map@CLAMAV_PRIVATE 0.102.4
+ text_normalize_reset@CLAMAV_PRIVATE 0.102.4
+ uniq_add@CLAMAV_PRIVATE 0.102.4
+ uniq_free@CLAMAV_PRIVATE 0.102.4
+ uniq_get@CLAMAV_PRIVATE 0.102.4
+ uniq_init@CLAMAV_PRIVATE 0.102.4
 libfreshclam.so.2 libclamav9 #MINVER#
  FRESHCLAM_PRIVATE@FRESHCLAM_PRIVATE 0.102.1
  FRESHCLAM_PUBLIC@FRESHCLAM_PUBLIC 0.102.1
diff -Nru clamav-0.102.3+dfsg/debian/patches/Add-support-for-LLVM-3.7.patch 
clamav-0.102.4+dfsg/debian/patches/Add-support-for-LLVM-3.7.patch
--- clamav-0.102.3+dfsg/debian/patches/Add-support-for-LLVM-3.7.patch   
2020-05-30 00:03:59.000000000 +0200
+++ clamav-0.102.4+dfsg/debian/patches/Add-support-for-LLVM-3.7.patch   
2020-07-18 00:19:32.000000000 +0200
@@ -1,4 +1,4 @@
-From 13e4f6183203d196555ebdadfcabcf19429c8f4a Mon Sep 17 00:00:00 2001
+From 036ff01c13e7d7473e3d4d645d831a15f3791fe2 Mon Sep 17 00:00:00 2001
 From: Andreas Cadhalpun <andreas.cadhal...@googlemail.com>
 Date: Fri, 14 Oct 2016 20:24:39 +0200
 Subject: Add support for LLVM 3.7
diff -Nru clamav-0.102.3+dfsg/debian/patches/Add-support-for-LLVM-3.8.patch 
clamav-0.102.4+dfsg/debian/patches/Add-support-for-LLVM-3.8.patch
--- clamav-0.102.3+dfsg/debian/patches/Add-support-for-LLVM-3.8.patch   
2020-05-30 00:03:59.000000000 +0200
+++ clamav-0.102.4+dfsg/debian/patches/Add-support-for-LLVM-3.8.patch   
2020-07-18 00:19:32.000000000 +0200
@@ -1,4 +1,4 @@
-From cad467b71ed31a10793bdac88e4c0bb0caa54991 Mon Sep 17 00:00:00 2001
+From 24dd86fa2b6a3bf629bad49897a0bc409f658ed7 Mon Sep 17 00:00:00 2001
 From: Andreas Cadhalpun <andreas.cadhal...@googlemail.com>
 Date: Fri, 14 Oct 2016 20:24:48 +0200
 Subject: Add support for LLVM 3.8
diff -Nru clamav-0.102.3+dfsg/debian/patches/Add-support-for-LLVM-3.9.patch 
clamav-0.102.4+dfsg/debian/patches/Add-support-for-LLVM-3.9.patch
--- clamav-0.102.3+dfsg/debian/patches/Add-support-for-LLVM-3.9.patch   
2020-05-30 00:03:59.000000000 +0200
+++ clamav-0.102.4+dfsg/debian/patches/Add-support-for-LLVM-3.9.patch   
2020-07-18 00:19:32.000000000 +0200
@@ -1,4 +1,4 @@
-From f921741f4223abac2066e067495d16311bb8a655 Mon Sep 17 00:00:00 2001
+From 6260c20ec271d77e84b7a32f03a7ebc02bdc43ca Mon Sep 17 00:00:00 2001
 From: Andreas Cadhalpun <andreas.cadhal...@googlemail.com>
 Date: Fri, 14 Oct 2016 20:24:56 +0200
 Subject: Add support for LLVM 3.9
diff -Nru 
clamav-0.102.3+dfsg/debian/patches/add-support-for-system-tomsfastmath.patch 
clamav-0.102.4+dfsg/debian/patches/add-support-for-system-tomsfastmath.patch
--- 
clamav-0.102.3+dfsg/debian/patches/add-support-for-system-tomsfastmath.patch    
    2020-05-30 00:03:59.000000000 +0200
+++ 
clamav-0.102.4+dfsg/debian/patches/add-support-for-system-tomsfastmath.patch    
    2020-07-18 00:19:32.000000000 +0200
@@ -1,4 +1,4 @@
-From 11ee2f472776b01b8ae4de78a1e1e52b1814079c Mon Sep 17 00:00:00 2001
+From 26c7b4bab716aec23e97e34b3cac919d91853439 Mon Sep 17 00:00:00 2001
 From: Andreas Cadhalpun <andreas.cadhal...@googlemail.com>
 Date: Wed, 11 Mar 2015 20:03:15 +0100
 Subject: add support for system tomsfastmath
@@ -14,7 +14,7 @@
  create mode 100644 m4/reorganization/libs/tomsfastmath.m4
 
 diff --git a/configure.ac b/configure.ac
-index 8375971..3cacfb8 100644
+index c3bd7fc..513398d 100644
 --- a/configure.ac
 +++ b/configure.ac
 @@ -98,6 +98,7 @@ m4_include([m4/reorganization/libs/libmspack.m4])
diff -Nru 
clamav-0.102.3+dfsg/debian/patches/Change-paths-in-sample-conf-file-to-match-Debian.patch
 
clamav-0.102.4+dfsg/debian/patches/Change-paths-in-sample-conf-file-to-match-Debian.patch
--- 
clamav-0.102.3+dfsg/debian/patches/Change-paths-in-sample-conf-file-to-match-Debian.patch
   2020-05-30 00:03:59.000000000 +0200
+++ 
clamav-0.102.4+dfsg/debian/patches/Change-paths-in-sample-conf-file-to-match-Debian.patch
   2020-07-18 00:19:32.000000000 +0200
@@ -1,4 +1,4 @@
-From d6669ed24e9e80dc91b787ab9734bf563f1ef628 Mon Sep 17 00:00:00 2001
+From c21a1e0040b47aff611b92416f9f39a6bb3ed139 Mon Sep 17 00:00:00 2001
 From: Scott Kitterman <sc...@kitterman.com>
 Date: Mon, 10 Mar 2014 19:20:18 -0400
 Subject: Change paths in sample conf file to match Debian
diff -Nru 
clamav-0.102.3+dfsg/debian/patches/clamd_dont_depend_on_clamav_demon_socket.patch
 
clamav-0.102.4+dfsg/debian/patches/clamd_dont_depend_on_clamav_demon_socket.patch
--- 
clamav-0.102.3+dfsg/debian/patches/clamd_dont_depend_on_clamav_demon_socket.patch
   2020-05-30 00:03:59.000000000 +0200
+++ 
clamav-0.102.4+dfsg/debian/patches/clamd_dont_depend_on_clamav_demon_socket.patch
   2020-07-18 00:19:32.000000000 +0200
@@ -1,4 +1,4 @@
-From 23dcb9a9f268a46c7df2d27c79c5adf341671ec4 Mon Sep 17 00:00:00 2001
+From eaa7e8852612270e87c7419601fca621fbd8b444 Mon Sep 17 00:00:00 2001
 From: Sebastian Andrzej Siewior <sebast...@breakpoint.cc>
 Date: Thu, 11 Aug 2016 21:54:10 +0200
 Subject: clamd: don't depend on clamav-demon.socket
diff -Nru 
clamav-0.102.3+dfsg/debian/patches/clamsubmit-libfreshclam-Use-CURL_CA_BUNDLE.patch
 
clamav-0.102.4+dfsg/debian/patches/clamsubmit-libfreshclam-Use-CURL_CA_BUNDLE.patch
--- 
clamav-0.102.3+dfsg/debian/patches/clamsubmit-libfreshclam-Use-CURL_CA_BUNDLE.patch
 2020-05-30 00:03:59.000000000 +0200
+++ 
clamav-0.102.4+dfsg/debian/patches/clamsubmit-libfreshclam-Use-CURL_CA_BUNDLE.patch
 2020-07-18 00:19:32.000000000 +0200
@@ -1,4 +1,4 @@
-From 04fd79ea5eace5273a13bd66b095e2fef0ea3bff Mon Sep 17 00:00:00 2001
+From c07899f43b92f63e9ad0ccefa5379ca649603d4a Mon Sep 17 00:00:00 2001
 From: Sebastian Andrzej Siewior <sebast...@breakpoint.cc>
 Date: Sun, 16 Feb 2020 17:09:37 +0100
 Subject: clamsubmit / libfreshclam: Use CURL_CA_BUNDLE
diff -Nru clamav-0.102.3+dfsg/debian/rules clamav-0.102.4+dfsg/debian/rules
--- clamav-0.102.3+dfsg/debian/rules    2020-05-30 00:03:59.000000000 +0200
+++ clamav-0.102.4+dfsg/debian/rules    2020-07-18 00:22:08.000000000 +0200
@@ -88,7 +88,7 @@
          fi;\
        done; \
        # Check for library features which may have been upgraded.
-       if ! grep -q "CL_FLEVEL 114" libclamav/others.h ; then \
+       if ! grep -q "CL_FLEVEL 115" libclamav/others.h ; then \
                echo "cl_retflevel needs boosting in symbol file"; \
                touch debian/exit; \
        fi;
diff -Nru clamav-0.102.3+dfsg/docs/html/UserManual/Installation-Windows.html 
clamav-0.102.4+dfsg/docs/html/UserManual/Installation-Windows.html
--- clamav-0.102.3+dfsg/docs/html/UserManual/Installation-Windows.html  
2020-05-12 03:54:58.000000000 +0200
+++ clamav-0.102.4+dfsg/docs/html/UserManual/Installation-Windows.html  
2020-07-15 23:54:53.000000000 +0200
@@ -15,9 +15,9 @@
 <h2 id="install-using-the-clamav-windows-installer">Install using the ClamAV 
Windows Installer</h2>
 <p>Important: Installing ClamAV using the Installer will require Administrator 
privileges.</p>
 <ol>
-<li>Download: <a 
href="http://www.clamav.net/downloads/production/ClamAV-0.102.2.exe"; 
class="uri">http://www.clamav.net/downloads/production/ClamAV-0.102.2.exe</a></li>
+<li>Download: <a 
href="http://www.clamav.net/downloads/production/ClamAV-0.102.3.exe"; 
class="uri">http://www.clamav.net/downloads/production/ClamAV-0.102.3.exe</a></li>
 <li>Locate the file in your Downloads directory.</li>
-<li>Right-click on <code>ClamAV-0.102.2.exe</code> and select <code>Run as 
administrator</code>. You may receive a warning message along the lines of 
&quot;Windows protected your PC&quot;. Select <code>More info</code> and then 
select <code>Run anyway</code>.</li>
+<li>Right-click on <code>ClamAV-0.102.3.exe</code> and select <code>Run as 
administrator</code>. You may receive a warning message along the lines of 
&quot;Windows protected your PC&quot;. Select <code>More info</code> and then 
select <code>Run anyway</code>.</li>
 <li>Select <code>I accept the agreement</code> and click 
<code>Next</code>.</li>
 <li>Click <code>Next</code> again. If you've removed a previous installation 
of ClamAV, you may receive the prompt &quot;The folder ... already 
exists...&quot;. If you do, select <code>Yes</code>.</li>
 <li>Click <code>Install</code>.</li>
@@ -36,9 +36,9 @@
 <hr />
 <h2 id="install-using-the-clamav-portable-install-package">Install using the 
ClamAV Portable Install Package</h2>
 <ol>
-<li>Download: <a 
href="https://www.clamav.net/downloads/production/clamav-0.102.2-win-x64-portable.zip";
 
class="uri">https://www.clamav.net/downloads/production/clamav-0.102.2-win-x64-portable.zip</a></li>
+<li>Download: <a 
href="https://www.clamav.net/downloads/production/clamav-0.102.3-win-x64-portable.zip";
 
class="uri">https://www.clamav.net/downloads/production/clamav-0.102.3-win-x64-portable.zip</a></li>
 <li>Unzip it.</li>
-<li>Open the <code>clamav-0.102.2-win-x64-portable</code> directory.</li>
+<li>Open the <code>clamav-0.102.3-win-x64-portable</code> directory.</li>
 <li>Hold down Shift and then right-click on the background in the current 
directory (but not on one of the files). Select <code>&quot;Open PowerShell 
window here&quot;</code>. If that option doesn't appear, try again.</li>
 </ol>
 <p>Continue on to &quot;First Time Set-Up&quot;...</p>
diff -Nru clamav-0.102.3+dfsg/libclamav/blob.c 
clamav-0.102.4+dfsg/libclamav/blob.c
--- clamav-0.102.3+dfsg/libclamav/blob.c        2020-05-12 03:54:49.000000000 
+0200
+++ clamav-0.102.4+dfsg/libclamav/blob.c        2020-07-15 23:54:36.000000000 
+0200
@@ -176,7 +176,7 @@
 int blobAddData(blob *b, const unsigned char *data, size_t len)
 {
 #if HAVE_CLI_GETPAGESIZE
-    static int pagesize;
+    static int pagesize = 0;
     int growth;
 #endif
 
@@ -225,6 +225,10 @@
 
         b->size = growth;
         b->data = cli_malloc(growth);
+        if (NULL == b->data){
+            b->size = 0;
+            return -1;
+        }
     } else if (b->size < b->len + (off_t)len) {
         unsigned char *p = cli_realloc(b->data, b->size + growth);
 
@@ -241,6 +245,10 @@
 
         b->size = (off_t)len * 4;
         b->data = cli_malloc(b->size);
+        if (NULL == b->data){
+            b->size = 0;
+            return -1;
+        }
     } else if (b->size < b->len + (off_t)len) {
         unsigned char *p = cli_realloc(b->data, b->size + (len * 4));
 
@@ -255,6 +263,9 @@
     if (b->data) {
         memcpy(&b->data[b->len], data, len);
         b->len += (off_t)len;
+    } else {
+        b->size = 0;
+        return -1;
     }
     return 0;
 }
diff -Nru clamav-0.102.3+dfsg/libclamav/bytecode_api.h 
clamav-0.102.4+dfsg/libclamav/bytecode_api.h
--- clamav-0.102.3+dfsg/libclamav/bytecode_api.h        2020-05-12 
03:54:49.000000000 +0200
+++ clamav-0.102.4+dfsg/libclamav/bytecode_api.h        2020-07-15 
23:54:36.000000000 +0200
@@ -145,6 +145,7 @@
     FUNC_LEVEL_0102_1    = 112, /**< LibClamAV release 0.102.1 */
     FUNC_LEVEL_0102_2    = 113, /**< LibClamAV release 0.102.2 */
     FUNC_LEVEL_0102_3    = 114, /**< LibClamAV release 0.102.3 */
+    FUNC_LEVEL_0102_4    = 115, /**< LibClamAV release 0.102.4 */
 };
 
 /**
diff -Nru clamav-0.102.3+dfsg/libclamav/egg.c 
clamav-0.102.4+dfsg/libclamav/egg.c
--- clamav-0.102.3+dfsg/libclamav/egg.c 2020-05-12 03:54:49.000000000 +0200
+++ clamav-0.102.4+dfsg/libclamav/egg.c 2020-07-15 23:54:36.000000000 +0200
@@ -547,7 +547,7 @@
                     goto done;
                 }
 
-                lpWideCharStr = malloc((cchWideChar + 1) * sizeof(WCHAR));
+                lpWideCharStr = cli_malloc((cchWideChar + 1) * sizeof(WCHAR));
                 if (NULL == lpWideCharStr) {
                     cli_dbgmsg("egg_filename_to_utf8: failed to allocate 
memory for wide char string.\n");
                     status = CL_EMEM;
@@ -589,7 +589,7 @@
                 goto done;
             }
 
-            out_utf8 = malloc(out_utf8_size + 1);
+            out_utf8 = cli_malloc(out_utf8_size + 1);
             if (NULL == lpWideCharStr) {
                 cli_dbgmsg("egg_filename_to_utf8: failed to allocate memory 
for wide char to utf-8 string.\n");
                 status = CL_EMEM;
@@ -625,8 +625,16 @@
                 }
             }
 
+            if (NULL == encoding) {
+                cli_dbgmsg("egg_filename_to_utf8: Invalid codepage parameter 
passed in.\n");
+                goto done;
+            }
+
             for (attempt = 1; attempt <= 3; attempt++) {
-                char* out_utf8_tmp;
+                char* out_utf8_index = NULL;
+                char* out_utf8_tmp   = NULL;
+                char* inbuf          = in;
+                size_t iconvRet      = -1;
 
                 /* Charset to UTF-8 should never exceed in_size * 6;
                  * We can shrink final buffer after the conversion, if needed. 
*/
@@ -635,7 +643,7 @@
                 inbytesleft  = in_size;
                 outbytesleft = out_utf8_size;
 
-                out_utf8 = cli_calloc(1, out_utf8_size + 1);
+                out_utf8 = out_utf8_index = cli_calloc(1, out_utf8_size + 1);
                 if (NULL == out_utf8) {
                     cli_errmsg("egg_filename_to_utf8: Failure allocating 
buffer for utf8 data.\n");
                     status = CL_EMEM;
@@ -647,7 +655,10 @@
                     goto done;
                 }
 
-                if ((size_t)-1 == iconv(conv, &in, &inbytesleft, &out_utf8, 
&outbytesleft)) {
+                iconvRet = iconv(conv, &inbuf, &inbytesleft, &out_utf8_index, 
&outbytesleft);
+                iconv_close(conv);
+                conv = (iconv_t)-1;
+                if ((size_t)-1 == iconvRet) {
                     switch (errno) {
                         case E2BIG:
                             cli_warnmsg("egg_filename_to_utf8: iconv error: 
There is not sufficient room at *outbuf.\n");
@@ -676,6 +687,7 @@
                 }
                 out_utf8      = out_utf8_tmp;
                 out_utf8_size = out_utf8_size - outbytesleft;
+                break;
             }
 
 #else
diff -Nru clamav-0.102.3+dfsg/libclamav/hwp.c 
clamav-0.102.4+dfsg/libclamav/hwp.c
--- clamav-0.102.3+dfsg/libclamav/hwp.c 2020-05-12 03:54:49.000000000 +0200
+++ clamav-0.102.4+dfsg/libclamav/hwp.c 2020-07-15 23:54:36.000000000 +0200
@@ -1810,6 +1810,8 @@
         new_offset = offset + (2 + nfonts * 40);
         if ((new_offset <= offset) || (new_offset >= map->len)) {
             cli_errmsg("HWP3.x: Font Entry: number of fonts is too high, 
invalid. %u\n", nfonts);
+            if (dmap)
+                funmap(dmap);
             return CL_EPARSE;
         }
         offset = new_offset;
@@ -1831,6 +1833,8 @@
     new_offset = offset + (2 + nstyles * 238);
     if ((new_offset <= offset) || (new_offset >= map->len)) {
         cli_errmsg("HWP3.x: Font Entry: number of font styles is too high, 
invalid. %u\n", nstyles);
+        if (dmap)
+            funmap(dmap);
         return CL_EPARSE;
     }
     offset += (2 + nstyles * 238);
diff -Nru clamav-0.102.3+dfsg/libclamav/libclamav.map 
clamav-0.102.4+dfsg/libclamav/libclamav.map
--- clamav-0.102.3+dfsg/libclamav/libclamav.map 2020-05-12 03:54:49.000000000 
+0200
+++ clamav-0.102.4+dfsg/libclamav/libclamav.map 2020-07-15 23:54:36.000000000 
+0200
@@ -258,6 +258,9 @@
     cl_base64_encode;
     cli_sanitize_filepath;
     cli_gentemp_with_prefix;
+    cli_basename;
+    cli_realpath;
+    cli_codepage_to_utf8;
 
     __cli_strcasestr;
     __cli_strndup;
diff -Nru clamav-0.102.3+dfsg/libclamav/others_common.c 
clamav-0.102.4+dfsg/libclamav/others_common.c
--- clamav-0.102.3+dfsg/libclamav/others_common.c       2020-05-12 
03:54:49.000000000 +0200
+++ clamav-0.102.4+dfsg/libclamav/others_common.c       2020-07-15 
23:54:36.000000000 +0200
@@ -1104,12 +1104,8 @@
 
 cl_error_t cli_get_filepath_from_filedesc(int desc, char **filepath)
 {
-    cl_error_t status = CL_EARG;
-
-    if (NULL == filepath) {
-        cli_errmsg("cli_get_filepath_from_filedesc: Invalid args.\n");
-        goto done;
-    }
+    cl_error_t status        = CL_EARG;
+    char *evaluated_filepath = NULL;
 
 #ifdef __linux__
     char fname[PATH_MAX];
@@ -1119,6 +1115,11 @@
 
     memset(&fname, 0, PATH_MAX);
 
+    if (NULL == filepath) {
+        cli_errmsg("cli_get_filepath_from_filedesc: Invalid args.\n");
+        goto done;
+    }
+
     snprintf(link, sizeof(link), "/proc/self/fd/%u", desc);
     link[sizeof(link) - 1] = '\0';
 
@@ -1131,8 +1132,8 @@
     /* Success. Add null terminator */
     fname[linksz] = '\0';
 
-    *filepath = CLI_STRNDUP(fname, CLI_STRNLEN(fname, PATH_MAX));
-    if (NULL == *filepath) {
+    evaluated_filepath = CLI_STRNDUP(fname, CLI_STRNLEN(fname, PATH_MAX));
+    if (NULL == evaluated_filepath) {
         cli_errmsg("cli_get_filepath_from_filedesc: Failed to allocate memory 
to store filename\n");
         status = CL_EMEM;
         goto done;
@@ -1142,59 +1143,135 @@
     char fname[PATH_MAX];
     memset(&fname, 0, PATH_MAX);
 
+    if (NULL == filepath) {
+        cli_errmsg("cli_get_filepath_from_filedesc: Invalid args.\n");
+        goto done;
+    }
+
     if (fcntl(desc, F_GETPATH, &fname) < 0) {
         printf("cli_get_filepath_from_filedesc: Failed to resolve filename for 
descriptor %d\n", desc);
         status = CL_EOPEN;
         goto done;
     }
 
-    *filepath = CLI_STRNDUP(fname, CLI_STRNLEN(fname, PATH_MAX));
-    if (NULL == *filepath) {
+    evaluated_filepath = CLI_STRNDUP(fname, CLI_STRNLEN(fname, PATH_MAX));
+    if (NULL == evaluated_filepath) {
         cli_errmsg("cli_get_filepath_from_filedesc: Failed to allocate memory 
to store filename\n");
         status = CL_EMEM;
         goto done;
     }
 
 #elif _WIN32
-    DWORD dwRet    = 0;
-    intptr_t hFile = _get_osfhandle(desc);
+    DWORD dwRet                   = 0;
+    intptr_t hFile                = _get_osfhandle(desc);
+    char *long_evaluated_filepath = NULL;
 
-    dwRet = GetFinalPathNameByHandleA((HANDLE)hFile, NULL, 0, VOLUME_NAME_NT);
+    if (NULL == filepath) {
+        cli_errmsg("cli_get_filepath_from_filedesc: Invalid args.\n");
+        goto done;
+    }
+
+    dwRet = GetFinalPathNameByHandleA((HANDLE)hFile, NULL, 0, VOLUME_NAME_DOS);
     if (dwRet == 0) {
         cli_errmsg("cli_get_filepath_from_filedesc: Failed to resolve filename 
for descriptor %d\n", desc);
         status = CL_EOPEN;
         goto done;
     }
 
-    *filepath = calloc(dwRet + 1, 1);
-    if (NULL == *filepath) {
+    long_evaluated_filepath = calloc(dwRet + 1, 1);
+    if (NULL == long_evaluated_filepath) {
         cli_errmsg("cli_get_filepath_from_filedesc: Failed to allocate %u 
bytes to store filename\n", dwRet + 1);
         status = CL_EMEM;
         goto done;
     }
 
-    dwRet = GetFinalPathNameByHandleA((HANDLE)hFile, *filepath, dwRet + 1, 
VOLUME_NAME_NT);
+    dwRet = GetFinalPathNameByHandleA((HANDLE)hFile, long_evaluated_filepath, 
dwRet + 1, VOLUME_NAME_DOS);
     if (dwRet == 0) {
         cli_errmsg("cli_get_filepath_from_filedesc: Failed to resolve filename 
for descriptor %d\n", desc);
-        free(*filepath);
-        *filepath = NULL;
-        status    = CL_EOPEN;
+        free(long_evaluated_filepath);
+        long_evaluated_filepath = NULL;
+        status                  = CL_EOPEN;
+        goto done;
+    }
+
+    evaluated_filepath = calloc(strlen(long_evaluated_filepath) - 
strlen("\\\\?\\") + 1, 1);
+    if (NULL == evaluated_filepath) {
+        cli_errmsg("cli_get_filepath_from_filedesc: Failed to allocate %u 
bytes to store filename\n", dwRet + 1);
+        status = CL_EMEM;
         goto done;
     }
+    memcpy(evaluated_filepath,
+           long_evaluated_filepath + strlen("\\\\?\\"),
+           strlen(long_evaluated_filepath) - strlen("\\\\?\\"));
 
 #else
 
     cli_dbgmsg("cli_get_filepath_from_filedesc: No mechanism implemented to 
determine filename from file descriptor.\n");
-    *filepath = NULL;
-    status    = CL_BREAK;
+    status = CL_BREAK;
     goto done;
 
 #endif
 
     cli_dbgmsg("cli_get_filepath_from_filedesc: File path for fd [%d] is: 
%s\n", desc, *filepath);
+    status    = CL_SUCCESS;
+    *filepath = evaluated_filepath;
+
+done:
+
+#ifdef _WIN32
+    if (NULL != long_evaluated_filepath) {
+        free(long_evaluated_filepath);
+    }
+#endif
+    return status;
+}
+
+cl_error_t cli_realpath(const char *file_name, char **real_filename)
+{
+    char *real_file_path = NULL;
+    cl_error_t status    = CL_EARG;
+#ifdef _WIN32
+    int desc = -1;
+#endif
+
+    cli_dbgmsg("Checking realpath of %s\n", file_name);
+
+    if (NULL == file_name || NULL == real_filename) {
+        cli_warnmsg("cli_realpath: Invalid arguments.\n");
+        goto done;
+    }
+
+#ifndef _WIN32
+
+    real_file_path = realpath(file_name, NULL);
+    if (NULL == real_file_path) {
+        status = CL_EMEM;
+        goto done;
+    }
+
     status = CL_SUCCESS;
 
+#else
+
+    if ((desc = safe_open(file_name, O_RDONLY | O_BINARY)) == -1) {
+        cli_warnmsg("Can't open file %s: %s\n", file_name, strerror(errno));
+        status = CL_EOPEN;
+        goto done;
+    }
+
+    status = cli_get_filepath_from_filedesc(desc, &real_file_path);
+
+#endif
+
+    *real_filename = real_file_path;
+
 done:
 
+#ifdef _WIN32
+    if (-1 != desc) {
+        close(desc);
+    }
+#endif
+
     return status;
 }
diff -Nru clamav-0.102.3+dfsg/libclamav/others.h 
clamav-0.102.4+dfsg/libclamav/others.h
--- clamav-0.102.3+dfsg/libclamav/others.h      2020-05-12 03:54:49.000000000 
+0200
+++ clamav-0.102.4+dfsg/libclamav/others.h      2020-07-15 23:54:36.000000000 
+0200
@@ -71,7 +71,7 @@
  * in re-enabling affected modules.
  */
 
-#define CL_FLEVEL 114
+#define CL_FLEVEL 115
 #define CL_FLEVEL_DCONF CL_FLEVEL
 #define CL_FLEVEL_SIGTOOL CL_FLEVEL
 
@@ -903,4 +903,18 @@
  */
 cl_error_t cli_get_filepath_from_filedesc(int desc, char **filepath);
 
+/**
+ * @brief   Attempt to get the real path of a provided path (evaluating 
symlinks).
+ *
+ * Caller is responsible for free'ing the file path.
+ * On posix systems this just calls realpath() under the hood.
+ * On Win32, it opens a handle and uses cli_get_filepath_from_filedesc()
+ * to get the real path.
+ *
+ * @param desc          A file path to evaluate.
+ * @param char*         [out] A malloced string containing the real path.
+ * @return cl_error_t   CL_SUCCESS if found, else an error code.
+ */
+cl_error_t cli_realpath(const char *file_name, char **real_filename);
+
 #endif
diff -Nru clamav-0.102.3+dfsg/libclamav/unarj.c 
clamav-0.102.4+dfsg/libclamav/unarj.c
--- clamav-0.102.3+dfsg/libclamav/unarj.c       2020-05-12 03:54:49.000000000 
+0200
+++ clamav-0.102.4+dfsg/libclamav/unarj.c       2020-07-15 23:54:36.000000000 
+0200
@@ -840,11 +840,17 @@
     unsigned char *comnorm = NULL;
     uint32_t ret           = TRUE;
 
+    size_t filename_max_len = 0;
+    size_t filename_len     = 0;
+    size_t comment_max_len  = 0;
+    size_t comment_len      = 0;
+    size_t orig_offset      = metadata->offset;
+
     if (fmap_readn(metadata->map, &header_size, metadata->offset, 2) != 2)
         return FALSE;
 
     metadata->offset += 2;
-    header_size   = le16_to_host(header_size);
+    header_size = le16_to_host(header_size);
     cli_dbgmsg("Header Size: %d\n", header_size);
     if (header_size == 0) {
         /* End of archive */
@@ -856,6 +862,11 @@
         ret = FALSE;
         goto done;
     }
+    if ((header_size + sizeof(header_size)) > (metadata->map->real_len - 
metadata->offset)) {
+        cli_dbgmsg("arj_read_header: invalid header_size: %u, exceeds length 
of file.\n", header_size);
+        ret = FALSE;
+        goto done;
+    }
     if (fmap_readn(metadata->map, &main_hdr, metadata->offset, 30) != 30) {
         ret = FALSE;
         goto done;
@@ -880,29 +891,47 @@
         metadata->offset += main_hdr.first_hdr_size - 30;
     }
 
-    fnnorm   = cli_calloc(sizeof(unsigned char), header_size + 1);
-    filename = fmap_need_offstr(metadata->map, metadata->offset, header_size + 
1);
-    if (!filename) {
-        cli_dbgmsg("UNARJ: Unable to allocate memory for filename\n");
+    filename_max_len = (header_size + sizeof(header_size)) - (metadata->offset 
- orig_offset);
+    if (filename_max_len > header_size) {
+        cli_dbgmsg("UNARJ: Format error. First Header Size invalid");
         ret = FALSE;
         goto done;
     }
-    metadata->offset += CLI_STRNLEN(filename, header_size) + 1;
+    if (filename_max_len > 0) {
+        fnnorm   = cli_calloc(sizeof(unsigned char), filename_max_len + 1);
+        filename = fmap_need_offstr(metadata->map, metadata->offset, 
filename_max_len + 1);
+        if (!filename || !fnnorm) {
+            cli_dbgmsg("UNARJ: Unable to allocate memory for filename\n");
+            ret = FALSE;
+            goto done;
+        }
+        filename_len = CLI_STRNLEN(filename, filename_max_len);
+    }
+    metadata->offset += filename_len + 1;
 
-    comnorm = cli_calloc(sizeof(unsigned char), header_size + 1);
-    comment = fmap_need_offstr(metadata->map, metadata->offset, header_size + 
1);
-    if (!comment || !comnorm) {
-        cli_dbgmsg("UNARJ: Unable to allocate memory for comment\n");
+    comment_max_len = (header_size + sizeof(header_size)) - (metadata->offset 
- orig_offset);
+    if (comment_max_len > header_size) {
+        cli_dbgmsg("UNARJ: Format error. First Header Size invalid");
         ret = FALSE;
         goto done;
     }
-    metadata->offset += CLI_STRNLEN(comment, header_size) + 1;
+    if (comment_max_len > 0) {
+        comnorm = cli_calloc(sizeof(unsigned char), comment_max_len + 1);
+        comment = fmap_need_offstr(metadata->map, metadata->offset, 
comment_max_len + 1);
+        if (!comment || !comnorm) {
+            cli_dbgmsg("UNARJ: Unable to allocate memory for comment\n");
+            ret = FALSE;
+            goto done;
+        }
+        comment_len = CLI_STRNLEN(comment, comment_max_len);
+    }
+    metadata->offset += comment_len + 1;
 
-    text_normalize_init(&fnstate, fnnorm, header_size);
-    text_normalize_init(&comstate, comnorm, header_size);
+    text_normalize_init(&fnstate, fnnorm, filename_max_len);
+    text_normalize_init(&comstate, comnorm, comment_max_len);
 
-    text_normalize_buffer(&fnstate, (const unsigned char *)filename, 
header_size);
-    text_normalize_buffer(&comstate, (const unsigned char *)comment, 
header_size);
+    text_normalize_buffer(&fnstate, (const unsigned char *)filename, 
filename_len);
+    text_normalize_buffer(&comstate, (const unsigned char *)comment, 
comment_len);
 
     cli_dbgmsg("Filename: %s\n", fnnorm);
     cli_dbgmsg("Comment: %s\n", comnorm);
@@ -949,6 +978,12 @@
     unsigned char *comnorm = NULL;
     uint32_t ret           = CL_SUCCESS;
 
+    size_t filename_max_len = 0;
+    size_t filename_len     = 0;
+    size_t comment_max_len  = 0;
+    size_t comment_len      = 0;
+    size_t orig_offset      = metadata->offset;
+
     if (fmap_readn(metadata->map, &header_size, metadata->offset, 2) != 2)
         return CL_EFORMAT;
     header_size = le16_to_host(header_size);
@@ -965,7 +1000,11 @@
         ret = CL_EFORMAT;
         goto done;
     }
-
+    if ((header_size + sizeof(header_size)) > (metadata->map->real_len - 
metadata->offset)) {
+        cli_dbgmsg("arj_read_file_header: invalid header_size: %u, exceeds 
length of file.\n", header_size);
+        ret = FALSE;
+        goto done;
+    }
     if (fmap_readn(metadata->map, &file_hdr, metadata->offset, 30) != 30) {
         ret = CL_EFORMAT;
         goto done;
@@ -997,33 +1036,51 @@
         metadata->offset += file_hdr.first_hdr_size - 30;
     }
 
-    fnnorm   = cli_calloc(sizeof(unsigned char), header_size + 1);
-    filename = fmap_need_offstr(metadata->map, metadata->offset, header_size + 
1);
-    if (!filename) {
-        cli_dbgmsg("UNARJ: Unable to allocate memory for filename\n");
+    filename_max_len = (header_size + sizeof(header_size)) - (metadata->offset 
- orig_offset);
+    if (filename_max_len > header_size) {
+        cli_dbgmsg("UNARJ: Format error. First Header Size invalid");
         ret = FALSE;
         goto done;
     }
-    metadata->offset += CLI_STRNLEN(filename, header_size) + 1;
+    if (filename_max_len > 0) {
+        fnnorm   = cli_calloc(sizeof(unsigned char), filename_max_len + 1);
+        filename = fmap_need_offstr(metadata->map, metadata->offset, 
filename_max_len + 1);
+        if (!filename || !fnnorm) {
+            cli_dbgmsg("UNARJ: Unable to allocate memory for filename\n");
+            ret = FALSE;
+            goto done;
+        }
+        filename_len = CLI_STRNLEN(filename, filename_max_len);
+    }
+    metadata->offset += filename_len + 1;
 
-    comnorm = cli_calloc(sizeof(unsigned char), header_size + 1);
-    comment = fmap_need_offstr(metadata->map, metadata->offset, header_size + 
1);
-    if (!comment) {
-        cli_dbgmsg("UNARJ: Unable to allocate memory for comment\n");
+    comment_max_len = (header_size + sizeof(header_size)) - (metadata->offset 
- orig_offset);
+    if (comment_max_len > header_size) {
+        cli_dbgmsg("UNARJ: Format error. First Header Size invalid");
         ret = FALSE;
         goto done;
     }
-    metadata->offset += CLI_STRNLEN(comment, header_size) + 1;
+    if (comment_max_len > 0) {
+        comnorm = cli_calloc(sizeof(unsigned char), comment_max_len + 1);
+        comment = fmap_need_offstr(metadata->map, metadata->offset, 
comment_max_len + 1);
+        if (!comment || !comnorm) {
+            cli_dbgmsg("UNARJ: Unable to allocate memory for comment\n");
+            ret = FALSE;
+            goto done;
+        }
+        comment_len += CLI_STRNLEN(comment, comment_max_len);
+    }
+    metadata->offset += comment_len + 1;
 
-    text_normalize_init(&fnstate, fnnorm, header_size);
-    text_normalize_init(&comstate, comnorm, header_size);
+    text_normalize_init(&fnstate, fnnorm, filename_max_len);
+    text_normalize_init(&comstate, comnorm, comment_max_len);
 
-    text_normalize_buffer(&fnstate, (const unsigned char *)filename, 
header_size);
-    text_normalize_buffer(&comstate, (const unsigned char *)comment, 
header_size);
+    text_normalize_buffer(&fnstate, (const unsigned char *)filename, 
filename_len);
+    text_normalize_buffer(&comstate, (const unsigned char *)comment, 
comment_len);
 
     cli_dbgmsg("Filename: %s\n", fnnorm);
     cli_dbgmsg("Comment: %s\n", comnorm);
-    metadata->filename = CLI_STRNDUP(filename, header_size);
+    metadata->filename = CLI_STRNDUP(filename, filename_len);
 
     /* Skip CRC */
     metadata->offset += 4;
diff -Nru clamav-0.102.3+dfsg/m4/reorganization/version.m4 
clamav-0.102.4+dfsg/m4/reorganization/version.m4
--- clamav-0.102.3+dfsg/m4/reorganization/version.m4    2020-05-12 
03:54:49.000000000 +0200
+++ clamav-0.102.4+dfsg/m4/reorganization/version.m4    2020-07-15 
23:54:36.000000000 +0200
@@ -3,7 +3,7 @@
 dnl For beta,                  set: VERSION="<version>-beta"
 dnl For release candidate,     set: VERSION="<version>-rc"
 dnl For release,               set: VERSION="<version>"
-VERSION="0.102.3"
+VERSION="0.102.4"
 
 major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
 minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
diff -Nru clamav-0.102.3+dfsg/NEWS.md clamav-0.102.4+dfsg/NEWS.md
--- clamav-0.102.3+dfsg/NEWS.md 2020-05-12 03:54:49.000000000 +0200
+++ clamav-0.102.4+dfsg/NEWS.md 2020-07-15 23:54:36.000000000 +0200
@@ -3,6 +3,36 @@
 Note: This file refers to the source tarball. Things described here may differ
  slightly from the binary packages.
 
+## 0.102.4
+
+ClamAV 0.102.4 is a bug patch release to address the following issues.
+
+- 
[CVE-2020-3350](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-3350):
+  Fix a vulnerability wherein a malicious user could replace a scan target's
+  directory with a symlink to another path to trick clamscan, clamdscan, or
+  clamonacc into removing or moving a different file (eg. a critical system
+  file). The issue would affect users that use the --move or --remove options
+  for clamscan, clamdscan, and clamonacc.
+
+  For more information about AV quarantine attacks using links, see the
+  [RACK911 Lab's 
report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software).
+
+- 
[CVE-2020-3327](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-3327):
+  Fix a vulnerability in the ARJ archive parsing module in ClamAV 0.102.3 that
+  could cause a Denial-of-Service (DoS) condition. Improper bounds checking
+  results in an out-of-bounds read which could cause a crash.
+  The previous fix for this CVE in 0.102.3 was incomplete. This fix correctly
+  resolves the issue.
+
+- 
[CVE-2020-3481](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-3481):
+  Fix a vulnerability in the EGG archive module in ClamAV 0.102.0 - 0.102.3
+  could cause a Denial-of-Service (DoS) condition. Improper error handling
+  may result in a crash due to a NULL pointer dereference.
+  This vulnerability is mitigated for those using the official ClamAV
+  signature databases because the file type signatures in daily.cvd
+  will not enable the EGG archive parser in versions affected by the
+  vulnerability.
+
 ## 0.102.3
 
 ClamAV 0.102.3 is a bug patch release to address the following issues.
diff -Nru clamav-0.102.3+dfsg/shared/actions.c 
clamav-0.102.4+dfsg/shared/actions.c
--- clamav-0.102.3+dfsg/shared/actions.c        2020-05-12 03:54:49.000000000 
+0200
+++ clamav-0.102.4+dfsg/shared/actions.c        2020-07-15 23:54:36.000000000 
+0200
@@ -2,7 +2,7 @@
  *  Copyright (C) 2013-2020 Cisco Systems, Inc. and/or its affiliates. All 
rights reserved.
  *  Copyright (C) 2009-2013 Sourcefire, Inc.
  *
- *  Author: aCaB
+ *  Author: aCaB, Micah Snyder
  *
  *  These functions are actions that may be taken when a sample alerts.
  *  The user may wish to:
@@ -25,6 +25,11 @@
  *  MA 02110-1301, USA.
  */
 
+#ifdef _WIN32
+#include <windows.h>
+#include <winternl.h>
+#endif
+
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
@@ -36,11 +41,14 @@
 #if HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#include <stdbool.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <libgen.h>
 
 #include "libclamav/clamav.h"
+#include "libclamav/str.h"
+#include "libclamav/others.h"
 #include "shared/optparser.h"
 #include "shared/output.h"
 #include "shared/misc.h"
@@ -84,24 +92,554 @@
     return -1;
 }
 
+#ifdef _WIN32
+
+typedef LONG (*PNTCF)(
+    PHANDLE FileHandle, // OUT
+    ACCESS_MASK DesiredAccess,
+    POBJECT_ATTRIBUTES ObjectAttributes,
+    PIO_STATUS_BLOCK IoStatusBlock, // OUT
+    PLARGE_INTEGER AllocationSize,
+    ULONG FileAttributes,
+    ULONG ShareAccess,
+    ULONG CreateDisposition,
+    ULONG CreateOptions,
+    PVOID EaBuffer,
+    ULONG EaLength);
+
+typedef void (*PRIUS)(
+    PUNICODE_STRING DestinationString,
+    PCWSTR SourceString);
+
+/**
+ * @brief A openat equivalent for Win32 with a check to NOFOLLOW soft-links.
+ *
+ * The caller is resposible for closing the HANDLE.
+ *
+ * For the desiredAccess, fileAttributes, createOptions, and shareAccess 
parameters
+ * see 
https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
+ *
+ * @param current_handle        The current handle. If set to NULL, then 
filename should be a drive letter.
+ * @param filename              The directory to open. If current_handle is 
valid, should be a directory found in the current directory.
+ * @param pNtCreateFile         A function pointer to the NtCreateFile Win32 
Native API.
+ * @param pRtlInitUnicodeString A function pointer to the RtlInitUnicodeString 
Win32 Native API.
+ * @param desiredAccess         The DesiredAccess option for NtCreateFile
+ * @param fileAttributes        The FileAttributes option for NtCreateFile
+ * @param createOptions         The CreateOptions option for NtCreateFile
+ * @param shareAccess           The ShareAccess option for NtCreateFile
+ * @return HANDLE               A handle on success, NULL on failure.
+ */
+static HANDLE win32_openat(
+    HANDLE current_handle,
+    const char *filename,
+    PNTCF pNtCreateFile,
+    PRIUS pRtlInitUnicodeString,
+    ACCESS_MASK desiredAccess,
+    ULONG fileAttributes,
+    ULONG createOptions,
+    ULONG shareAccess)
+{
+    HANDLE next_handle = NULL;
+
+    LONG ntStatus;
+    WCHAR *filenameW = NULL;
+    UNICODE_STRING filenameU;
+    int cchNextDirectoryName        = 0;
+    IO_STATUS_BLOCK ioStatusBlock   = {0};
+    OBJECT_ATTRIBUTES objAttributes = {0};
+    FILE_ATTRIBUTE_TAG_INFO tagInfo = {0};
+
+    /* Convert filename to a UNICODE_STRING, required by the native API 
NtCreateFile() */
+    cchNextDirectoryName = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 
0);
+    filenameW            = malloc(cchNextDirectoryName * sizeof(WCHAR));
+    if (NULL == filenameW) {
+        logg("win32_openat: failed to allocate memory for next directory name 
UTF16LE string\n");
+        goto done;
+    }
+    if (0 == MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, 
cchNextDirectoryName)) {
+        logg("win32_openat: failed to allocate buffer for unicode version of 
intermediate directory name.\n");
+        goto done;
+    }
+    pRtlInitUnicodeString(&filenameU, filenameW);
+
+    InitializeObjectAttributes(
+        &objAttributes,       // ObjectAttributes
+        &filenameU,           // ObjectName
+        OBJ_CASE_INSENSITIVE, // Attributes
+        current_handle,       // Root directory
+        NULL);                // SecurityDescriptor
+
+    ntStatus = pNtCreateFile(
+        &next_handle,   // FileHandle
+        desiredAccess,  // DesiredAccess
+        &objAttributes, // ObjectAttributes
+        &ioStatusBlock, // [out] status
+        0,              // AllocationSize
+        fileAttributes, // FileAttributes
+        shareAccess,    // ShareAccess
+        FILE_OPEN,      // CreateDisposition
+        createOptions,  // CreateOptions
+        NULL,           // EaBuffer
+        0);             // EaLength
+    if (!NT_SUCCESS(ntStatus) || (NULL == next_handle)) {
+        logg("win32_openat: Failed to open file '%s'. \nError: 0x%x 
\nioStatusBlock: 0x%x\n", filename, ntStatus, ioStatusBlock.Information);
+        goto done;
+    }
+    logg("*win32_openat: Opened file \"%s\"\n", filename);
+
+    if (0 == GetFileInformationByHandleEx(
+                 next_handle,                        // hFile,
+                 FileAttributeTagInfo,               // FileInformationClass
+                 &tagInfo,                           // lpFileInformation
+                 sizeof(FILE_ATTRIBUTE_TAG_INFO))) { // dwBufferSize
+        logg("win32_openat: Failed to get file information by handle '%s'.  
Error: %d.\n", filename, GetLastError());
+
+        CloseHandle(next_handle);
+        next_handle = NULL;
+        goto done;
+    }
+    logg("*win32_openat: tagInfo.FileAttributes: 0x%0x\n", 
tagInfo.FileAttributes);
+    logg("*win32_openat: tagInfo.ReparseTag:     0x%0x\n", tagInfo.ReparseTag);
+    if (0 != (tagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+        logg("win32_openat: File is a soft link: '%s' Aborting path 
traversal.\n\n", filename);
+
+        CloseHandle(next_handle);
+        next_handle = NULL;
+        goto done;
+    }
+    logg("*win32_openat: File or directory is not a soft link.\n\n");
+
+done:
+    if (NULL != filenameW) {
+        free(filenameW);
+    }
+
+    return next_handle;
+}
+#endif
+
+/**
+ * @brief Traverse from root to the specified directory without following 
symlinks.
+ *
+ * The intention is so you can use `unlinkat` or `rename_at` to safely move or
+ * delete the target directory.
+ *
+ * The caller is responsible for closing the output file descriptor if the
+ * traversal succeeded.
+ *
+ * @param directory             The directory to traverse to (must be NULL 
terminated).
+ * @param want_directory_handle Set to true to get the directory handle 
containing the file, false to get the file handle.
+ * @param[out] out_handle       An open file descriptor or HANDLE (win32) for 
the directory.
+ * @return 0                    Traverse succeeded.
+ * @return -1                   Traverse failed.
+ */
+#ifndef _WIN32
+static int traverse_to(const char *directory, bool want_directory_handle, int 
*out_handle)
+#else
+static int traverse_to(const char *directory, bool want_directory_handle, 
HANDLE *out_handle)
+#endif
+{
+    int status = -1;
+    size_t tokens_count;
+    const char *tokens[PATH_MAX / 2];
+    size_t i;
+    char *tokenized_directory = NULL;
+#ifndef _WIN32
+    int current_handle = -1;
+    int next_handle    = -1;
+#else
+    bool bNeedDeleteFileAccess = false;
+
+    HMODULE ntdll               = NULL;
+    PNTCF pNtCreateFile         = NULL;
+    PRIUS pRtlInitUnicodeString = NULL;
+
+    PHANDLE current_handle = NULL;
+    PHANDLE next_handle    = NULL;
+
+    ACCESS_MASK desiredAccess = STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | 
SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_READ_EA;
+    ULONG fileAttributes      = FILE_ATTRIBUTE_DIRECTORY;
+    ULONG createOptions       = FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT;
+    ULONG shareAccess         = FILE_SHARE_READ;
+#endif
+
+    if (NULL == directory || NULL == out_handle) {
+        logg("traverse_to: Invalid arguments!\n");
+        goto done;
+    }
+
+#ifdef _WIN32
+    ntdll = LoadLibraryA("ntdll.dll");
+    if (NULL == ntdll) {
+        logg("traverse_to: failed to load ntdll!\n");
+        goto done;
+    }
+    pNtCreateFile = (PNTCF)GetProcAddress(ntdll, "NtCreateFile");
+    if (NULL == pNtCreateFile) {
+        logg("traverse_to: failed to get NtCreateFile proc address!\n");
+        goto done;
+    }
+    pRtlInitUnicodeString = (PRIUS)GetProcAddress(ntdll, 
"RtlInitUnicodeString");
+    if (NULL == pRtlInitUnicodeString) {
+        logg("traverse_to: failed to get pRtlInitUnicodeString proc 
address!\n");
+        goto done;
+    }
+#endif
+
+    tokenized_directory = strdup(directory);
+    if (NULL == tokenized_directory) {
+        logg("traverse_to: Failed to get copy of directory path to be 
tokenized!\n");
+        goto done;
+    }
+
+    tokens_count = cli_strtokenize(tokenized_directory, *PATHSEP, PATH_MAX / 
2, tokens);
+    if (0 == tokens_count) {
+        logg("traverse_to: tokenize of target directory returned 0 tokens!\n");
+        goto done;
+    }
+
+#ifndef _WIN32
+    /*
+     * Open the root(/) directory, because it won't be the first token like a
+     * drive letter (i.e. "C:") would be on Windows.
+     */
+    current_handle = open("/", O_RDONLY | O_NOFOLLOW);
+    if (-1 == current_handle) {
+        logg("traverse_to: Failed to open file descriptor for '/' 
directory.\n");
+        goto done;
+    }
+#endif
+
+    if (true == want_directory_handle) {
+        tokens_count -= 1;
+    }
+
+    if (0 == tokens_count) {
+        logg("traverse_to: Failed to get copy of directory path to be 
tokenized!\n");
+        goto done;
+    }
+
+    for (i = 0; i < tokens_count; i++) {
+        if (0 == strlen(tokens[i])) {
+            /* Empty token, likely first / or double // */
+            continue;
+        }
+#ifndef _WIN32
+        next_handle = openat(current_handle, tokens[i], O_RDONLY | O_NOFOLLOW);
+        if (-1 == next_handle) {
+            logg("traverse_to: Failed open %s\n", tokens[i]);
+            goto done;
+        }
+        close(current_handle);
+        current_handle = next_handle;
+        next_handle    = -1;
+#else
+        if (true != want_directory_handle) {
+            if (i == tokens_count - 1) {
+                /* Change createfile options for our target file instead of an 
intermediate directory. */
+                desiredAccess  = FILE_ALL_ACCESS | DELETE;
+                fileAttributes = FILE_ATTRIBUTE_NORMAL;
+                createOptions  = FILE_NON_DIRECTORY_FILE;
+                shareAccess    = FILE_SHARE_READ | FILE_SHARE_WRITE | 
FILE_SHARE_DELETE;
+            }
+        }
+        if (i == 0) {
+            /* NtCreateFile requires the \???\ prefix on drive letters. Eg: 
\???\C:\ */
+            size_t driveroot_len = strlen("\\??\\\\") + strlen(tokens[0]) + 1;
+            char *driveroot      = malloc(driveroot_len);
+            snprintf(driveroot, driveroot_len + 1, "\\??\\%s\\", tokens[0]);
+            next_handle = win32_openat(current_handle,
+                                       driveroot,
+                                       pNtCreateFile,
+                                       pRtlInitUnicodeString,
+                                       desiredAccess,
+                                       fileAttributes,
+                                       createOptions,
+                                       shareAccess);
+            free(driveroot);
+        } else {
+            next_handle = win32_openat(current_handle,
+                                       tokens[i],
+                                       pNtCreateFile,
+                                       pRtlInitUnicodeString,
+                                       desiredAccess,
+                                       fileAttributes,
+                                       createOptions,
+                                       shareAccess);
+        }
+        if (NULL == next_handle) {
+            logg("traverse_to: Failed open %s\n", tokens[i]);
+            goto done;
+        }
+        CloseHandle(current_handle);
+        current_handle = next_handle;
+        next_handle    = NULL;
+#endif
+        logg("*traverse_to: Handle opened for '%s' directory.\n", tokens[i]);
+    }
+
+    status      = 0;
+    *out_handle = current_handle;
+
+done:
+#ifndef _WIN32
+    if ((-1 == status) && (-1 != current_handle)) {
+        close(current_handle);
+    }
+#else
+    if ((-1 == status) && (NULL != current_handle)) {
+        CloseHandle(current_handle);
+    }
+#endif
+    if (NULL != tokenized_directory) {
+        free(tokenized_directory);
+    }
+
+    return status;
+}
+
+/**
+ * @brief Rename (move) a file from Source to Destination without following 
symlinks.
+ *
+ * This approach mitigates the possibility that one of the directories
+ * in the path has been replaces with a malicious symlink.
+ *
+ * @param source        Source pathname.
+ * @param destination   Destination pathname (including file name)
+ * @return 0            Rename succeeded.
+ * @return -1           Rename failed.
+ */
+static int traverse_rename(const char *source, const char *destination)
+{
+    int status = -1;
+#ifndef _WIN32
+    cl_error_t ret;
+    int source_directory_fd = -1;
+    char *source_basename   = NULL;
+#else
+    FILE_RENAME_INFO *fileInfo    = NULL;
+    HANDLE source_file_handle     = NULL;
+    HANDLE destination_dir_handle = NULL;
+    WCHAR *destFilepathW          = NULL;
+    int cchDestFilepath           = 0;
+#endif
+
+    if (NULL == source || NULL == destination) {
+        logg("traverse_rename: Invalid arguments!\n");
+        goto done;
+    }
+
+#ifndef _WIN32
+    if (0 != traverse_to(source, true, &source_directory_fd)) {
+        logg("traverse_rename: Failed to open file descriptor for source 
directory!\n");
+        goto done;
+    }
+#else
+    if (0 != traverse_to(source, false, &source_file_handle)) {
+        logg("traverse_rename: Failed to open file descriptor for source 
file!\n");
+        goto done;
+    }
+    if (0 != traverse_to(destination, true, &destination_dir_handle)) {
+        logg("traverse_rename: Failed to open file descriptor for destination 
directory!\n");
+        goto done;
+    }
+#endif
+
+#ifndef _WIN32
+    ret = cli_basename(source, strlen(source), &source_basename);
+    if (CL_SUCCESS != ret) {
+        logg("traverse_rename: Failed to get basename of source 
path:%s\n\tError: %d\n", source, (int)ret);
+        goto done;
+    }
+
+    if (0 != renameat(source_directory_fd, source_basename, -1, destination)) {
+        logg("traverse_rename: Failed to rename: %s\n\tto: %s\nError:%s\n", 
source, destination, strerror(errno));
+        goto done;
+    }
+#else
+    /* Convert destination filepath to a PWCHAR */
+    cchDestFilepath = MultiByteToWideChar(CP_UTF8, 0, destination, 
strlen(destination), NULL, 0);
+    destFilepathW   = calloc(cchDestFilepath * sizeof(WCHAR), 1);
+    if (NULL == destFilepathW) {
+        logg("traverse_rename: failed to allocate memory for destination 
basename UTF16LE string\n");
+        goto done;
+    }
+    if (0 == MultiByteToWideChar(CP_UTF8, 0, destination, strlen(destination), 
destFilepathW, cchDestFilepath)) {
+        logg("traverse_rename: failed to allocate buffer for UTF16LE version 
of destination file basename.\n");
+        goto done;
+    }
+
+    fileInfo = calloc(1, sizeof(FILE_RENAME_INFO) + cchDestFilepath * 
sizeof(WCHAR));
+    if (NULL == fileInfo) {
+        logg("traverse_rename: failed to allocate memory for fileInfo 
struct\n");
+        goto done;
+    }
+
+    fileInfo->ReplaceIfExists = TRUE;
+    fileInfo->RootDirectory   = NULL;
+    memcpy(fileInfo->FileName, destFilepathW, cchDestFilepath * sizeof(WCHAR));
+    fileInfo->FileNameLength = cchDestFilepath;
+    if (FALSE == SetFileInformationByHandle(
+                     source_file_handle,                                       
     // FileHandle
+                     FileRenameInfo,                                           
     // FileInformationClass
+                     fileInfo,                                                 
     // FileInformation
+                     sizeof(FILE_RENAME_INFO) + cchDestFilepath * 
sizeof(WCHAR))) { // Length
+
+        logg("traverse_rename: Failed to set file rename info for '%s' to 
'%s'.\nError: %d\n", source, destination, GetLastError());
+        goto done;
+    }
+#endif
+
+    status = 0;
+
+done:
+
+#ifndef _WIN32
+    if (NULL != source_basename) {
+        free(source_basename);
+    }
+
+    if (-1 != source_directory_fd) {
+        close(source_directory_fd);
+    }
+#else
+    if (NULL != fileInfo) {
+        free(fileInfo);
+    }
+    if (NULL != destFilepathW) {
+        free(destFilepathW);
+    }
+    if (NULL != source_file_handle) {
+        CloseHandle(source_file_handle);
+    }
+    if (NULL != destination_dir_handle) {
+        CloseHandle(destination_dir_handle);
+    }
+#endif
+
+    return status;
+}
+
+/**
+ * @brief Unlink (delete) a target file without following symlinks.
+ *
+ * This approach mitigates the possibility that one of the directories
+ * in the path has been replaces with a malicious symlink.
+ *
+ * @param target    A file to be deleted.
+ * @return 0        Unlink succeeded.
+ * @return -1       Unlink failed.
+ */
+static int traverse_unlink(const char *target)
+{
+    int status = -1;
+    cl_error_t ret;
+#ifndef _WIN32
+    int target_directory_fd = -1;
+#else
+    FILE_DISPOSITION_INFO fileInfo = {0};
+    HANDLE target_file_handle      = NULL;
+#endif
+    char *target_basename = NULL;
+
+    if (NULL == target) {
+        logg("traverse_unlink: Invalid arguments!\n");
+        goto done;
+    }
+
+#ifndef _WIN32
+    /* On posix, we want a file descriptor for the directory */
+    if (0 != traverse_to(target, true, &target_directory_fd)) {
+#else
+    /* On Windows, we want a handle to the file, not the directory */
+    if (0 != traverse_to(target, false, &target_file_handle)) {
+#endif
+        logg("traverse_unlink: Failed to open file descriptor for target 
directory!\n");
+        goto done;
+    }
+
+    ret = cli_basename(target, strlen(target), &target_basename);
+    if (CL_SUCCESS != ret) {
+        logg("traverse_unlink: Failed to get basename of target path: 
%s\n\tError: %d\n", target, (int)ret);
+        goto done;
+    }
+
+#ifndef _WIN32
+    if (0 != unlinkat(target_directory_fd, target_basename, 0)) {
+        logg("traverse_unlink: Failed to unlink: %s\nError:%s\n", target, 
strerror(errno));
+        goto done;
+    }
+#else
+    fileInfo.DeleteFileA = TRUE;
+    if (FALSE == SetFileInformationByHandle(
+                     target_file_handle,               // FileHandle
+                     FileDispositionInfo,              // FileInformationClass
+                     &fileInfo,                        // FileInformation
+                     sizeof(FILE_DISPOSITION_INFO))) { // Length
+
+        logg("traverse_unlink: Failed to set file disposition to 'DELETE' for 
'%s'.\n", target);
+        goto done;
+    }
+    if (FALSE == CloseHandle(target_file_handle)) {
+        logg("traverse_unlink: Failed to set close & delete file '%s'.\n", 
target);
+        goto done;
+    }
+    target_file_handle = NULL;
+#endif
+
+    status = 0;
+
+done:
+
+    if (NULL != target_basename) {
+        free(target_basename);
+    }
+
+#ifndef _WIN32
+    if (-1 != target_directory_fd) {
+        close(target_directory_fd);
+    }
+#else
+    if (NULL != target_file_handle) {
+        CloseHandle(target_file_handle);
+    }
+#endif
+    return status;
+}
+
 static void action_move(const char *filename)
 {
-    char *nuname;
-    int fd = getdest(filename, &nuname), copied = 0;
+    char *nuname        = NULL;
+    char *real_filename = NULL;
+    int fd              = -1;
+    int copied          = 0;
+
+    if (NULL == filename) {
+        goto done;
+    }
+
+    fd = getdest(filename, &nuname);
 
-    if (fd < 0 || (rename(filename, nuname) && ((copied = 1)) && 
filecopy(filename, nuname))) {
-        logg("!Can't move file %s\n", filename);
+#ifndef _WIN32
+    if (fd < 0 || (0 != traverse_rename(filename, nuname) && ((copied = 1)) && 
filecopy(filename, nuname))) {
+#else
+    if (fd < 0 || (((copied = 1)) && filecopy(filename, nuname))) {
+#endif
+        logg("!Can't move file %s to %s\n", filename, nuname);
         notmoved++;
-        if (nuname) unlink(nuname);
+        if (nuname) traverse_unlink(nuname);
     } else {
-        if (copied && unlink(filename))
-            logg("!Can't unlink '%s': %s\n", filename, strerror(errno));
+        if (copied && (0 != traverse_unlink(filename)))
+            logg("!Can't unlink '%s' after copy: %s\n", filename, 
strerror(errno));
         else
             logg("~%s: moved to '%s'\n", filename, nuname);
     }
 
+done:
+    if (NULL != real_filename) free(real_filename);
     if (fd >= 0) close(fd);
-    if (nuname) free(nuname);
+    if (NULL != nuname) free(nuname);
+    return;
 }
 
 static void action_copy(const char *filename)
@@ -112,7 +650,7 @@
     if (fd < 0 || filecopy(filename, nuname)) {
         logg("!Can't copy file '%s'\n", filename);
         notmoved++;
-        if (nuname) unlink(nuname);
+        if (nuname) traverse_unlink(nuname);
     } else
         logg("~%s: copied to '%s'\n", filename, nuname);
 
@@ -122,12 +660,22 @@
 
 static void action_remove(const char *filename)
 {
-    if (unlink(filename)) {
-        logg("!Can't remove file '%s'.\n", filename);
+    char *real_filename = NULL;
+
+    if (NULL == filename) {
+        goto done;
+    }
+
+    if (0 != traverse_unlink(filename)) {
+        logg("!Can't remove file '%s'\n", filename);
         notremoved++;
     } else {
         logg("~%s: Removed.\n", filename);
     }
+
+done:
+    if (NULL != real_filename) free(real_filename);
+    return;
 }
 
 static int isdir(void)
@@ -148,7 +696,15 @@
 {
     int move = optget(opts, "move")->enabled;
     if (move || optget(opts, "copy")->enabled) {
+        cl_error_t ret;
         actarget = optget(opts, move ? "move" : "copy")->strarg;
+#ifndef _WIN32
+        ret = cli_realpath((const char *)actarget, &actarget);
+        if (CL_SUCCESS != ret || NULL == actarget) {
+            logg("action_setup: Failed to get realpath of %s\n", actarget);
+            return 0;
+        }
+#endif
         if (!isdir()) return 1;
         action  = move ? action_move : action_copy;
         targlen = strlen(actarget);
diff -Nru clamav-0.102.3+dfsg/unit_tests/check_clamav.c 
clamav-0.102.4+dfsg/unit_tests/check_clamav.c
--- clamav-0.102.3+dfsg/unit_tests/check_clamav.c       2020-05-12 
03:54:50.000000000 +0200
+++ clamav-0.102.4+dfsg/unit_tests/check_clamav.c       2020-07-15 
23:54:36.000000000 +0200
@@ -973,6 +973,26 @@
 }
 END_TEST
 
+cl_error_t cli_codepage_to_utf8(char* in, size_t in_size, uint16_t codepage, 
char** out, size_t* out_size);
+
+START_TEST(test_cli_codepage_to_utf8)
+{
+    cl_error_t ret;
+    char *utf8       = NULL;
+    size_t utf8_size = 0;
+
+    ret = 
cli_codepage_to_utf8("\x00\x48\x00\x65\x00\x6c\x00\x6c\x00\x6f\x00\x20\x00\x77\x00\x6f\x00\x72\x00\x6c\x00\x64\x00\x21\x00\x00",
 26, 1201, &utf8, &utf8_size);
+    ck_assert_msg(CL_SUCCESS == ret, "test_cli_codepage_to_utf8: Failed to 
convert CODEPAGE_UTF16_LE to UTF8: ret != SUCCESS!");
+    ck_assert_msg(NULL != utf8, "sanitize_path: Failed to convert 
CODEPAGE_UTF16_LE to UTF8: utf8 pointer is NULL!");
+    ck_assert_msg(0 == strcmp(utf8, "Hello world!"), "sanitize_path: '%s' 
doesn't match '%s'", utf8, "Hello world!");
+
+    if (NULL != utf8) {
+        free(utf8);
+        utf8 = NULL;
+    }
+}
+END_TEST
+
 static Suite *test_cli_suite(void)
 {
     Suite *s               = suite_create("cli");
@@ -992,6 +1012,7 @@
 
     suite_add_tcase(s, tc_cli_assorted);
     tcase_add_test(tc_cli_assorted, test_sanitize_path);
+    tcase_add_test(tc_cli_assorted, test_cli_codepage_to_utf8);
 
     return s;
 }
diff -Nru clamav-0.102.3+dfsg/unit_tests/valgrind.supp 
clamav-0.102.4+dfsg/unit_tests/valgrind.supp
--- clamav-0.102.3+dfsg/unit_tests/valgrind.supp        2020-05-12 
03:54:50.000000000 +0200
+++ clamav-0.102.4+dfsg/unit_tests/valgrind.supp        2020-07-15 
23:54:36.000000000 +0200
@@ -265,7 +265,6 @@
    fun:internal_ascii_loop
    fun:__gconv_transform_internal_ascii
    fun:wcsrtombs
-   fun:wcsrtombs
    ...
 }
 {
@@ -273,7 +272,6 @@
    Memcheck:Cond
    fun:__wcsnlen_avx2
    fun:wcsrtombs
-   fun:wcsrtombs
    ...
 }
 {
@@ -281,7 +279,6 @@
    Memcheck:Cond
    fun:__wcsnlen_sse4_1
    fun:wcsrtombs
-   fun:wcsrtombs
    ...
 }
 {

Reply via email to