From fd0931ff18ea2a35f4c39698174d273f46793b2f Mon Sep 17 00:00:00 2001 From: Micah Snyder Date: Wed, 28 Aug 2024 11:08:19 -0400 Subject: [PATCH 1/5] Bump version to 1.4.1 and FLEVEL to 211 --- CMakeLists.txt | 2 +- Jenkinsfile | 2 +- NEWS.md | 4 ++++ libclamav/bytecode_api.h | 6 +++++- libclamav/others.h | 2 +- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 94a61f0517..93f87b197e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ string(TIMESTAMP TODAY "%Y%m%d") set(VERSION_SUFFIX "") project( ClamAV - VERSION "1.4.0" + VERSION "1.4.1" DESCRIPTION "ClamAV open source email, web, and end-point anti-virus toolkit." ) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) diff --git a/Jenkinsfile b/Jenkinsfile index d54a956b0f..f32e8ba5f1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -10,7 +10,7 @@ properties( parameters( [ string(name: 'VERSION', - defaultValue: '1.4.0', + defaultValue: '1.4.1', description: 'ClamAV version string'), string(name: 'FRAMEWORK_BRANCH', defaultValue: '1.4', diff --git a/NEWS.md b/NEWS.md index c1b7336d53..a00550f9da 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,6 +3,10 @@ Note: This file refers to the official packages. Things described here may differ slightly from third-party binary packages. +## 1.4.1 + +ClamAV 1.4.1 is a critical patch release with the following fixes: + ## 1.4.0 ClamAV 1.4.0 includes the following improvements and changes: diff --git a/libclamav/bytecode_api.h b/libclamav/bytecode_api.h index 63b730f493..fe254fa975 100644 --- a/libclamav/bytecode_api.h +++ b/libclamav/bytecode_api.h @@ -157,6 +157,7 @@ enum FunctionalityLevels { FUNC_LEVEL_0103_9 = 130, /**< LibClamAV release 0.103.9 */ FUNC_LEVEL_0103_10 = 131, /**< LibClamAV release 0.103.10 */ FUNC_LEVEL_0103_11 = 132, /**< LibClamAV release 0.103.11 */ + FUNC_LEVEL_0103_12 = 133, /**< LibClamAV release 0.103.12 */ FUNC_LEVEL_0104 = 140, /**< LibClamAV release 0.104.0 */ FUNC_LEVEL_0104_1 = 141, /**< LibClamAV release 0.104.1 */ @@ -175,6 +176,7 @@ enum FunctionalityLevels { FUNC_LEVEL_1_0_4 = 164, /**< LibClamAV release 1.0.4 */ FUNC_LEVEL_1_0_5 = 165, /**< LibClamAV release 1.0.5 */ FUNC_LEVEL_1_0_6 = 166, /**< LibClamAV release 1.0.6 */ + FUNC_LEVEL_1_0_7 = 167, /**< LibClamAV release 1.0.7 */ FUNC_LEVEL_1_1 = 180, /**< LibClamAV release 1.1.0 */ FUNC_LEVEL_1_1_1 = 181, /**< LibClamAV release 1.1.1 */ @@ -188,8 +190,10 @@ enum FunctionalityLevels { FUNC_LEVEL_1_3 = 200, /**< LibClamAV release 1.3.0 */ FUNC_LEVEL_1_3_1 = 201, /**< LibClamAV release 1.3.1 */ + FUNC_LEVEL_1_3_2 = 202, /**< LibClamAV release 1.3.2 */ - FUNC_LEVEL_1_4 = 210, /**< LibClamAV release 1.4.0 */ + FUNC_LEVEL_1_4 = 210, /**< LibClamAV release 1.4.0 */ + FUNC_LEVEL_1_4_1 = 211, /**< LibClamAV release 1.4.1 */ }; /** diff --git a/libclamav/others.h b/libclamav/others.h index 8cebf78d35..e967354b26 100644 --- a/libclamav/others.h +++ b/libclamav/others.h @@ -69,7 +69,7 @@ * in re-enabling affected modules. */ -#define CL_FLEVEL 210 +#define CL_FLEVEL 211 #define CL_FLEVEL_DCONF CL_FLEVEL #define CL_FLEVEL_SIGTOOL CL_FLEVEL From 4ba48b170c8f9844d1e7421458bd3c6b6774f80d Mon Sep 17 00:00:00 2001 From: Micah Snyder Date: Mon, 26 Aug 2024 14:00:51 -0400 Subject: [PATCH 2/5] Disable following symlinks when opening log files The log module used by clamd and freshclam may follow symlinks. This is a potential security concern since the log may be owned by the unprivileged service but may be opened by the service running as root on startup. For Windows, we'll define O_NOFOLLOW so the code works, though the issue does not affect Windows. Issue reported by Detlef. --- common/output.c | 51 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/common/output.c b/common/output.c index 72e57ea96e..385dad7b15 100644 --- a/common/output.c +++ b/common/output.c @@ -58,6 +58,12 @@ #include "output.h" +// Define O_NOFOLLOW for systems that don't have it. +// Notably, Windows doesn't have O_NOFOLLOW. +#ifndef O_NOFOLLOW +#define O_NOFOLLOW 0 +#endif + #ifdef CL_THREAD_SAFE #include pthread_mutex_t logg_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -304,7 +310,6 @@ int logg(loglevel_t loglevel, const char *str, ...) char buffer[1025], *abuffer = NULL, *buff; time_t currtime; size_t len; - mode_t old_umask; #ifdef F_WRLCK struct flock fl; #endif @@ -338,18 +343,36 @@ int logg(loglevel_t loglevel, const char *str, ...) logg_open(); if (!logg_fp && logg_file) { - old_umask = umask(0037); - if ((logg_fp = fopen(logg_file, "at")) == NULL) { - umask(old_umask); + int logg_file_fd = -1; + + logg_file_fd = open(logg_file, O_WRONLY | O_CREAT | O_APPEND | O_NOFOLLOW, 0640); + if (-1 == logg_file_fd) { + char errbuf[128]; + cli_strerror(errno, errbuf, sizeof(errbuf)); + printf("ERROR: Failed to open log file %s: %s\n", logg_file, errbuf); + #ifdef CL_THREAD_SAFE pthread_mutex_unlock(&logg_mutex); #endif - printf("ERROR: Can't open %s in append mode (check permissions!).\n", logg_file); - if (len > sizeof(buffer)) + if (abuffer) free(abuffer); return -1; - } else - umask(old_umask); + } + + logg_fp = fdopen(logg_file_fd, "at"); + if (NULL == logg_fp) { + char errbuf[128]; + cli_strerror(errno, errbuf, sizeof(errbuf)); + printf("ERROR: Failed to convert the open log file descriptor for %s to a FILE* handle: %s\n", logg_file, errbuf); + + close(logg_file_fd); +#ifdef CL_THREAD_SAFE + pthread_mutex_unlock(&logg_mutex); +#endif + if (abuffer) + free(abuffer); + return -1; + } #ifdef F_WRLCK if (logg_lock) { @@ -362,11 +385,16 @@ int logg(loglevel_t loglevel, const char *str, ...) else #endif { + char errbuf[128]; + cli_strerror(errno, errbuf, sizeof(errbuf)); + printf("ERROR: Failed to lock the log file %s: %s\n", logg_file, errbuf); + #ifdef CL_THREAD_SAFE pthread_mutex_unlock(&logg_mutex); #endif - printf("ERROR: %s is locked by another process\n", logg_file); - if (len > sizeof(buffer)) + fclose(logg_fp); + logg_fp = NULL; + if (abuffer) free(abuffer); return -1; } @@ -441,8 +469,9 @@ int logg(loglevel_t loglevel, const char *str, ...) pthread_mutex_unlock(&logg_mutex); #endif - if (len > sizeof(buffer)) + if (abuffer) free(abuffer); + return 0; } From a7dd1ec28b30ae61ee241798e328fc270a1515db Mon Sep 17 00:00:00 2001 From: Micah Snyder Date: Tue, 16 Jul 2024 11:22:05 -0400 Subject: [PATCH 3/5] Fix possible out of bounds read in PDF parser The `find_length()` function in the PDF parser incorrectly assumes that objects found are located in the main PDF file map, and fails to take into account whether the objects were in fact found in extracted PDF object streams. The resulting pointer is then invalid and may be an out of bounds read. This issue was found by OSS-Fuzz. This fix checks if the object is from an object stream, and then calculates the pointer based on the start of the object stream instead of based on the start of the PDF. I've also added extra checks to verify the calculated pointer and object size are within the stream (or PDF file map). I'm not entirely sure this is necessary, but better safe than sorry. Fixes: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=69617 --- libclamav/pdf.c | 45 +++++++++++++++++++++++++++++++++++++++------ libclamav/pdfng.c | 5 +++++ 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/libclamav/pdf.c b/libclamav/pdf.c index a5dd159e8a..6b2c7d2ff8 100644 --- a/libclamav/pdf.c +++ b/libclamav/pdf.c @@ -1003,8 +1003,26 @@ static size_t find_length(struct pdf_struct *pdf, struct pdf_obj *obj, const cha return 0; } - indirect_obj_start = pdf->map + obj->start; - bytes_remaining = pdf->size - obj->start; + if (NULL == obj->objstm) { + indirect_obj_start = (const char *)(obj->start + pdf->map); + + if (!CLI_ISCONTAINED(pdf->map, pdf->size, indirect_obj_start, obj->size)) { + cli_dbgmsg("find_length: indirect object found, but not contained in PDF\n"); + return 0; + } + + bytes_remaining = pdf->size - obj->start; + + } else { + indirect_obj_start = (const char *)(obj->start + obj->objstm->streambuf); + + if (!CLI_ISCONTAINED(obj->objstm->streambuf, obj->objstm->streambuf_len, indirect_obj_start, obj->size)) { + cli_dbgmsg("find_length: indirect object found, but not contained in PDF streambuf\n"); + return 0; + } + + bytes_remaining = obj->objstm->streambuf_len - obj->start; + } /* Ok so we found the indirect object, lets read the value. */ index = pdf_nextobject(indirect_obj_start, bytes_remaining); @@ -3260,15 +3278,30 @@ void pdf_handle_enc(struct pdf_struct *pdf) obj = find_obj(pdf, pdf->objs[0], pdf->enc_objid); if (!obj) { - cli_dbgmsg("pdf_handle_enc: can't find encrypted object %d %d\n", pdf->enc_objid >> 8, pdf->enc_objid & 0xff); - noisy_warnmsg("pdf_handle_enc: can't find encrypted object %d %d\n", pdf->enc_objid >> 8, pdf->enc_objid & 0xff); + cli_dbgmsg("pdf_handle_enc: can't find encryption object %d %d\n", pdf->enc_objid >> 8, pdf->enc_objid & 0xff); + noisy_warnmsg("pdf_handle_enc: can't find encryption object %d %d\n", pdf->enc_objid >> 8, pdf->enc_objid & 0xff); return; } len = obj->size; - q = (obj->objstm) ? (const char *)(obj->start + obj->objstm->streambuf) - : (const char *)(obj->start + pdf->map); + if (NULL == obj->objstm) { + q = (const char *)(obj->start + pdf->map); + + if (!CLI_ISCONTAINED(pdf->map, pdf->size, q, len)) { + cli_dbgmsg("pdf_handle_enc: encryption object found, but not contained in PDF\n"); + noisy_warnmsg("pdf_handle_enc: encryption object found, but not contained in PDF\n"); + return; + } + } else { + q = (const char *)(obj->start + obj->objstm->streambuf); + + if (!CLI_ISCONTAINED(obj->objstm->streambuf, obj->objstm->streambuf_len, q, len)) { + cli_dbgmsg("pdf_handle_enc: encryption object found, but not contained in PDF streambuf\n"); + noisy_warnmsg("pdf_handle_enc: encryption object found, but not contained in PDF streambuf\n"); + return; + } + } O = U = UE = StmF = StrF = EFF = NULL; diff --git a/libclamav/pdfng.c b/libclamav/pdfng.c index 575cece831..5c3b9a116f 100644 --- a/libclamav/pdfng.c +++ b/libclamav/pdfng.c @@ -518,6 +518,11 @@ char *pdf_parse_string(struct pdf_struct *pdf, struct pdf_obj *obj, const char * if (!(newobj)) return NULL; + if (!CLI_ISCONTAINED(pdf->map, pdf->size, newobj->start, newobj->size)) { + cli_dbgmsg("pdf_parse_string: object not contained in PDF\n"); + return NULL; + } + if (newobj == obj) return NULL; From 30752dacade64587e7740d925f1f2a33af3da63a Mon Sep 17 00:00:00 2001 From: Micah Snyder Date: Wed, 28 Aug 2024 15:07:27 -0400 Subject: [PATCH 4/5] Tests: remove unused Python modules from freshclam tests The 'cgi' module is deprecrated and will be removed in Python 3.13. We weren't using it anyways. Fixes: https://github.com/Cisco-Talos/clamav/issues/1327 --- unit_tests/freshclam_test.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/unit_tests/freshclam_test.py b/unit_tests/freshclam_test.py index bec400f6aa..78de30c028 100644 --- a/unit_tests/freshclam_test.py +++ b/unit_tests/freshclam_test.py @@ -10,14 +10,10 @@ from pathlib import Path import platform import shutil -import subprocess -import sys -import time import unittest from functools import partial from http.server import HTTPServer, BaseHTTPRequestHandler -import cgi import testcase From d898fb4cd7e26d0fda53afda6ad00c34bdcad9f7 Mon Sep 17 00:00:00 2001 From: Micah Snyder Date: Wed, 28 Aug 2024 14:03:52 -0400 Subject: [PATCH 5/5] News: updates prior to 1.4.1 --- NEWS.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/NEWS.md b/NEWS.md index a00550f9da..9092496e79 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,6 +7,34 @@ differ slightly from third-party binary packages. ClamAV 1.4.1 is a critical patch release with the following fixes: +- [CVE-2024-20506](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-20506): + Changed the logging module to disable following symlinks on Linux and Unix + systems so as to prevent an attacker with existing access to the 'clamd' or + 'freshclam' services from using a symlink to corrupt system files. + + This issue affects all currently supported versions. It will be fixed in: + - 1.4.1 + - 1.3.2 + - 1.0.7 + - 0.103.12 + + Thank you to Detlef for identifying this issue. + +- [CVE-2024-20505](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-20505): + Fixed a possible out-of-bounds read bug in the PDF file parser that could + cause a denial-of-service (DoS) condition. + + This issue affects all currently supported versions. It will be fixed in: + - 1.4.1 + - 1.3.2 + - 1.0.7 + - 0.103.12 + + Thank you to OSS-Fuzz for identifying this issue. + +- Removed unused Python modules from freshclam tests including deprecated + 'cgi' module that is expected to cause test failures in Python 3.13. + ## 1.4.0 ClamAV 1.4.0 includes the following improvements and changes: