Compare commits

..

46 Commits
1.0 ... master

Author SHA1 Message Date
f2ac486d24 去除未使用的头文件定义 2024-07-19 13:59:16 +08:00
ab10389228 Merge branch 'master' into dev 2024-07-18 17:58:26 +08:00
99893d8c3d 优化 2024-07-18 17:04:00 +08:00
6f25fb3672 修改配置 2024-07-18 16:29:32 +08:00
967922012c 优化 2024-07-18 16:27:53 +08:00
d8f7c47faa 修复内存泄漏 2024-07-05 09:25:44 +08:00
4aa0d3eac7 Merge branch 'dev' 2024-05-28 11:19:38 +08:00
0b5e40d5c4 优化 2024-05-28 11:08:55 +08:00
d1ae9fc12c 修改文档 2024-05-27 16:14:13 +08:00
8d613b3e54 重新架构 2024-05-27 16:05:31 +08:00
3aa4d63799 优化磁盘告警 2024-05-23 18:12:04 +08:00
130dad7ffa 修改错误 2024-05-22 15:35:14 +08:00
fa2ea1f83c Merge branch 'dev' 2024-05-22 15:26:08 +08:00
2722e9b2e1 修改帮助文件 2024-05-22 15:18:13 +08:00
f7f7d7136a 优化获取磁盘使用率 2024-05-22 15:10:19 +08:00
00567557ba 添加Nginx日志文件 2024-05-22 11:36:50 +08:00
778c9d5fff 增加Nginx规则 2024-05-21 15:28:20 +08:00
b5bd70ec71 增加Ng防护 2024-05-21 09:08:14 +08:00
950bd28f60 修改帮助文件 2023-10-14 09:02:29 +08:00
bc29a0b1a9 支持 Debian 12 2023-07-27 14:40:40 +08:00
a8fb2a8dd3 修改配置文件 rhost.conf 2023-07-04 15:11:23 +08:00
ed3fb48ce2 更新 ip2region/ip2region.xdb 2023-07-03 17:20:39 +08:00
bcb5a7fb5b 添加 ssh key 2023-07-03 14:57:09 +08:00
c949f3c04b Lowered spin from 0.9.8 to 0.9.4 in /clamav/libclamav_rust 2023-04-04 15:46:49 +08:00
315a368012 增加arm架构构建 2023-04-04 15:37:42 +08:00
ce349a46a6 日志格式输出 2023-03-10 13:21:46 +08:00
bc6e6b253c 优化读取配置文件正确性! 2023-02-28 11:04:47 +08:00
17f06235df 增加判断配置文件内容是否正确! 2023-02-27 16:33:03 +08:00
bd6793499f 修改配置文件 2023-02-22 13:56:41 +08:00
2af92f8144 优化 2023-02-21 11:43:14 +08:00
333f0ff2d2 去除aliyun IP位置API, 优化地域解析 2023-02-15 13:48:08 +08:00
da8473a3ec 修改病毒扫描时间为凌晨 2023-02-14 09:30:32 +08:00
5f1c1af36b 修改DatabaseMirror镜像地址为大陆 2023-02-05 17:52:43 +08:00
dafc57b769 修改README.md 2023-02-01 11:16:10 +08:00
72bd738a1d 使用 ip2region 地址定位库判断Ip地域 2023-01-30 15:55:22 +08:00
c2024e15f5 增加ARM64构建 2023-01-28 11:37:36 +08:00
faccc6426c 优化Makefile 2023-01-16 14:42:14 +08:00
be6b92fc60 优化Makefile 2023-01-15 11:24:32 +08:00
7478732ed8 优化Makefile 2023-01-15 11:02:57 +08:00
e424cc1c04 优化Makefil 2023-01-15 10:33:54 +08:00
45fe15f472 更新libclamav库1.0.0版本 2023-01-14 18:28:39 +08:00
b879ee0b2e 增加ip2region离线IP地址定位库,测试阶段未使用 2023-01-10 12:54:53 +08:00
5aa7e4aa06 处理地域白名单错误,添加cJSON解析,添加阿里云API解析IP地域 2023-01-09 17:59:23 +08:00
ce37ef75d6 增加进程优先级 2022-12-30 11:07:44 +08:00
a47d429273 增加帮助信息 2022-12-08 17:13:32 +08:00
13c1d09385 修改build.sh构建脚本 2022-12-08 15:49:50 +08:00
8775 changed files with 1360048 additions and 312241 deletions

11
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,11 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
]
}

21
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,21 @@
{
"files.associations": {
"assert.h": "c",
"ip2region.h": "c",
"conf.h": "c",
"ccronexpr.h": "c",
"clamscan.h": "c",
"libiptc.h": "c",
"stdio.h": "c",
"nginx.h": "c",
"cjson.h": "c",
"stdlib.h": "c",
"stddef.h": "c",
"signal.h": "c",
"time.h": "c",
"stdarg.h": "c",
"libclamav.h": "c",
"rhost.h": "c",
"warning.h": "c"
}
}

104
Makefile
View File

@ -1,61 +1,87 @@
CROSS_COMPILE ?=
CC := $(CROSS_COMPILE)gcc
LIB += -lcurl -lip4tc clamscan/clamscan.o clamscan/manager.o -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 ./clamscan/shared/libshared.a -lssl -lcrypto -lz -lpthread
IPTC_CFLAGS += -DHAVE_CONFIG_H -I./libiptc -D_LARGEFILE_SOURCE=1 -D_LARGE_FILES -D_FILE_OFFSET_BITS=64 -D_REENTRANT
AR := $(CROSS_COMPILE)ar
RANLIB := $(CROSS_COMPILE)ranlib
OBG = rhost
CLAMSCAN_CFLAGS += -Wall -Os -DHAVE_CONFIG_H -I./clamscan -I./clamscan/shared -I./clamscan/libclamav -I./clamscan/libclamunrar_iface -I/usr/include/json-c -Wdate-time -D_FORTIFY_SOURCE=2 -fstack-protector-strong -Wall -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
SHARED_CFLAGS += -Wall -Os -DHAVE_CONFIG_H -I./clamscan -I./clamscan/shared -I./clamscan/libclamav -I./clamscan/libclamunrar_iface -I/usr/include/json-c -Wdate-time -D_FORTIFY_SOURCE=2 -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wall -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
CFLAGS += -Os -g -Wall -DCRON_USE_LOCAL_TIME -DCRON_TEST_MALLOC
freshclam_CFLAGS += -g -O2 -Wall -DHAVE_CONFIG_H -I./clamscan -I./clamscan/shared -I./clamscan/libclamav -I./clamscan/libclamunrar_iface -I/usr/include/json-c -Wdate-time -D_FORTIFY_SOURCE=2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
libfreshclam_CFLAGS += -g -Os -Wall -DHAVE_CONFIG_H -I./clamscan -I./clamscan/freshclam -I./clamscan/shared -I./clamscan/libclamav -I./clamscan/libclamunrar_iface -I/usr/include/json-c -Wdate-time -D_FORTIFY_SOURCE=2
freshclam_LIB += clamscan/freshclam/freshclam.o clamscan/freshclam/notify.o clamscan/freshclam/execute.o clamscan/freshclam/libfreshclam.o clamscan/freshclam/libfreshclam_internal.o clamscan/freshclam/dns.o ./clamscan/shared/libshared.a -lssl -lcrypto -lclamav -lresolv -lcurl -lz -ljson-c -lltdl -lpthread -lm
all: conf.o rhost.o libiptc.o ccronexpr.o
$(CC) $(freshclam_CFLAGS) -c clamscan/freshclam/freshclam.c -fPIC -DPIC -o clamscan/freshclam/freshclam.o
$(CC) $(freshclam_CFLAGS) -c clamscan/freshclam/notify.c -fPIC -DPIC -o clamscan/freshclam/notify.o
$(CC) $(freshclam_CFLAGS) -c clamscan/freshclam/execute.c -fPIC -DPIC -o clamscan/freshclam/execute.o
CMAKE = $(shell if test -f /etc/centos-release; then echo "1"; else echo "0"; fi)
ifeq ("$(CMAKE)", "1")
CMAKE := cmake3
else
CMAKE := cmake
endif
$(CC) $(libfreshclam_CFLAGS) -c clamscan/freshclam/libfreshclam.c -fPIC -DPIC -o clamscan/freshclam/libfreshclam.o
$(CC) $(libfreshclam_CFLAGS) -c clamscan/freshclam/libfreshclam_internal.c -fPIC -DPIC -o clamscan/freshclam/libfreshclam_internal.o
$(CC) $(libfreshclam_CFLAGS) -c clamscan/freshclam/dns.c -fPIC -DPIC -o clamscan/freshclam/dns.o
ARCH := $(shell bash get_architecture.sh)
$(CC) $(SHARED_CFLAGS) -c ./clamscan/shared/actions.c -fPIC -DPIC -o ./clamscan/shared/actions.o
$(CC) $(SHARED_CFLAGS) -c ./clamscan/shared/cdiff.c -fPIC -DPIC -o ./clamscan/shared/cdiff.o
$(CC) $(SHARED_CFLAGS) -c ./clamscan/shared/cert_util.c -fPIC -DPIC -o ./clamscan/shared/cert_util.o
$(CC) $(SHARED_CFLAGS) -c ./clamscan/shared/clamdcom.c -fPIC -DPIC -o ./clamscan/shared/clamdcom.o
$(CC) $(SHARED_CFLAGS) -c ./clamscan/shared/getopt.c -fPIC -DPIC -o ./clamscan/shared/getopt.o
$(CC) $(SHARED_CFLAGS) -c ./clamscan/shared/hostid.c -fPIC -DPIC -o ./clamscan/shared/hostid.o
$(CC) $(SHARED_CFLAGS) -c ./clamscan/shared/idmef_logging.c -fPIC -DPIC -o ./clamscan/shared/idmef_logging.o
$(CC) $(SHARED_CFLAGS) -c ./clamscan/shared/misc.c -fPIC -DPIC -o ./clamscan/shared/misc.o
$(CC) $(SHARED_CFLAGS) -c ./clamscan/shared/optparser.c -fPIC -DPIC -o ./clamscan/shared/optparser.o
$(CC) $(SHARED_CFLAGS) -c ./clamscan/shared/output.c -fPIC -DPIC -o ./clamscan/shared/output.o
$(CC) $(SHARED_CFLAGS) -c ./clamscan/shared/tar.c -fPIC -DPIC -o ./clamscan/shared/tar.o
$(CC) $(SHARED_CFLAGS) -c ./clamscan/shared/linux/cert_util_linux.c -fPIC -DPIC -o ./clamscan/shared/linux/cert_util_linux.o
ar cr ./clamscan/shared/libshared.a ./clamscan/shared/actions.o ./clamscan/shared/cdiff.o ./clamscan/shared/cert_util.o ./clamscan/shared/clamdcom.o ./clamscan/shared/getopt.o ./clamscan/shared/hostid.o ./clamscan/shared/idmef_logging.o ./clamscan/shared/misc.o ./clamscan/shared/optparser.o ./clamscan/shared/output.o ./clamscan/shared/tar.o ./clamscan/shared/linux/cert_util_linux.o
$(CC) $(CLAMSCAN_CFLAGS) -c clamscan/clamscan.c -o clamscan/clamscan.o
$(CC) $(CLAMSCAN_CFLAGS) -c clamscan/manager.c -o clamscan/manager.o
IPTC_CFLAGS += -DHAVE_CONFIG_H -I./libiptc -D_LARGEFILE_SOURCE=1 -D_LARGE_FILES -D_FILE_OFFSET_BITS=64 -D_REENTRANT
IPTC_LIB += -lip4tc
$(CC) $(CFLAGS) $^ -o $(OBG) $(LIB) $(freshclam_LIB)
ip2region_CFLAGS += -Os -g -Wall -I/ip2region -c
ip2region_LIB += ip2region.o xdb_searcher.o
CLAMAV_CFLAGS += -DHAVE_CONFIG_H -I./clamav/libclamav -I./clamav/ -I./clamav/libclamunrar_iface -I./clamav/common -isystem ./clamav/ -isystem /usr/include/libxml2 -isystem /usr/include/json-c -fPIE -Wall -Wextra -Wformat-security -std=gnu90
CLAMAV_LIB += ./clamav/clamscan/clamscan.c.o ./clamav/clamscan/manager.c.o ./clamav/libcommon.a -Wl,-rpath,/lib /lib/libclamav.so.11.0.0 -lmspack ./clamav/libclamav_rust/$(ARCH)/debug/libclamav_rust.a -lgcc_s -lutil -lrt -lpthread -lm -ldl -lc -lbz2 -lpcre2-8 -lxml2 -ljson-c -lc -ldl -lm -lssl -lcrypto -lz -lcurl -lsystemd
FRESHCLAM_CFLAGS += -DHAVE_CONFIG_H -I./clamav/libfreshclam -I./clamav/common -I./clamav/libclamav -I./clamav/libclamunrar_iface -isystem ./clamav/ -isystem ./clamav/libclamav_rust -isystem /usr/include/libxml2 -isystem /usr/include/json-c -fPIE -Wall -Wextra -Wformat-security -std=gnu90
FRESHCLAM_LIB += ./clamav/freshclam/freshclam.c.o ./clamav/freshclam/execute.c.o ./clamav/freshclam/notify.c.o -Wl,-rpath,/lib /lib/libfreshclam.so.2.0.2 ./clamav/libcommon.a -Wl,-rpath,/lib /lib/libclamav.so.11.0.0 -lmspack ./clamav/libclamav_rust/$(ARCH)/debug/libclamav_rust.a -lgcc_s -lutil -lrt -lpthread -lm -ldl -lc -lbz2 -lpcre2-8 -lxml2 -ljson-c -lc -ldl -lm -lz -lsystemd -lcurl -lssl -lcrypto -lresolv
LIBCOMMON__CFLAGS += -DHAVE_CONFIG_H -I./clamav/common -I./clamav/libclamav -I./clamav/ -I./clamav/libclamunrar_iface -isystem ./clamav/ -isystem /usr/include/libxml2 -isystem /usr/include/json-c -fPIC -Wall -Wextra -Wformat-security
LIBCOMMON_LIB += ./clamav/common/cert_util.c.o ./clamav/common/actions.c.o ./clamav/common/clamdcom.c.o ./clamav/common/getopt.c.o ./clamav/common/hostid.c.o ./clamav/common/idmef_logging.c.o ./clamav/common/misc.c.o ./clamav/common/optparser.c.o ./clamav/common/output.c.o ./clamav/common/tar.c.o ./clamav/common/linux/cert_util_linux.c.o
all: libclamav_rust libclamav rhost nginx.o
rhost: conf.o common.o rhost.o libiptc.o ccronexpr.o rule.o nginx.o disk.o ip.o warning.o
$(CC) $(ip2region_CFLAGS) ip2region/ip2region.c
$(CC) $(ip2region_CFLAGS) ip2region/xdb_searcher.c
$(CC) $(CLAMAV_CFLAGS) -o ./clamav/clamscan/clamscan.c.o -c ./clamav/clamscan/clamscan.c
$(CC) $(CLAMAV_CFLAGS) -o ./clamav/clamscan/manager.c.o -c ./clamav/clamscan/manager.c
$(CC) $(FRESHCLAM_CFLAGS) -o ./clamav/freshclam/freshclam.c.o -c ./clamav/freshclam/freshclam.c
$(CC) $(FRESHCLAM_CFLAGS) -o ./clamav/freshclam/execute.c.o -c ./clamav/freshclam/execute.c
$(CC) $(FRESHCLAM_CFLAGS) -o ./clamav/freshclam/notify.c.o -c ./clamav/freshclam/notify.c
$(CC) $(CFLAGS) $^ -o $(OBG) $(ip2region_LIB) $(CLAMAV_LIB) $(IPTC_LIB) $(FRESHCLAM_LIB)
chmod +x $(OBG)
libclamav_rust:
cd clamav/libclamav_rust && $(CMAKE) -E env CARGO_CMD=build CARGO_TARGET_DIR=./ MAINTAINER_MODE=OFF RUSTFLAGS="" \
cargo build --target $(ARCH) --target-dir ./
cd ..
libclamav:
cd clamav/ && bash -x build-libclamav.sh
cp ${PWD}/clamav/libclamav.so.11.0.0 /lib/
cp ${PWD}/clamav/libfreshclam.so.2.0.2 /lib/
rm -rf /lib/libclamav.so.11 && ln -s /lib/libclamav.so.11.0.0 /lib/libclamav.so.11
rm -rf /lib/libfreshclam.so.2 && ln -s /lib/libfreshclam.so.2.0.2 /lib/libfreshclam.so.2
cd ..
test:
echo $(CMAKE) $(ARCH) $(CFLAGS)
static: conf.o rhost.o libiptc.o
$(CC) $(IPTC_CFLAGS) -c libiptc/libip4tc.c -o libiptc/libip4tc.o
ar crs libiptc/libip4tc.a libiptc/libip4tc.o
#$(CC) $(CFLAGS) $^ libiptc/libip4tc.o -o $(OBG) `/data/curl/bin/curl-config --cflags --static-libs` `pkg-config --libs --static libbrotlidec`
.PHONY: clean
clean:
rm -rf *.o
rm -rf libiptc/*.o libiptc/*.a
rm -rf clamscan/shared/*.o clamscan/shared/libshared.a clamscan/*.o
rm -rf clamscan/freshclam/*.o
rm -rf `find clamscan/libclamav/ -name *.lo`
rm -rf `find clamscan/libclamav/ -name *.o`
rm -rf $(OBG)
rm -rf $(OBG) $(cJSON_LIB) $(ip2region_LIB)
rm -rf `find ./clamav/ -name *.o`
rm -rf ./clamav/*.a
rm -rf ./clamav/libclamav.so.11.0.0
rm -rf ./clamav/libfreshclam.so.2.0.2
rm -rf ${PWD}/clamav/libclamav.so.11
rm -rf ${PWD}/clamav/libfreshclam.so.2
rm -rf clamav/libclamav_rust/debug
rm -rf clamav/libclamav_rust/$(ARCH)

View File

@ -1,23 +1,27 @@
# denyhosts
- 拒绝主机&杀毒
```
```text
适用系统:
Debian 11
Debian 11、12
Centos 7
支持系统病毒扫描
支持一次运行检测、后台运行检测
支持钉钉告警和邮件告
支持第三方QQ邮箱告警
```
```
```text
Debian
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
apt -y install libclamav-dev libip4tc-dev libcurl4-openssl-dev #(或者libcurl4-gnutls-dev)
apt -y install libsystemd-dev libjson-c-dev libpcre2-dev clamav-freshclam
apt -y install libltdl-dev
apt -y install libltdl-dev libmspack-dev cmake
freshclam # 更新病毒库(必要)
Debian系统使用libiptc库需要nftables切换到iptables
Debian系统使用libiptc库需要nftables切换到iptables (使用了libip4tc-dev库)
Switching to the legacy version:(切换到 iptables)
update-alternatives --set iptables /usr/sbin/iptables-legacy
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
@ -25,11 +29,13 @@ Debian
update-alternatives --set ebtables /usr/sbin/ebtables-legacy
```
```
```text
Centos 7
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
yum install clamav clamav-update clamav-lib clamav-devel json-c-devel pcre2-devel
yum install iptables-devel libcurl-devel
yum install systemd-devel libtool-ltdl-devel
yum install systemd-devel libtool-ltdl-devel libmspack-devel
yum install cmake3
yum -y install centos-release-scl
yum -y install devtoolset-11-gcc
@ -41,15 +47,13 @@ Centos 7
freshclam # 更新病毒库(必要)
```
```
```text
用法
cd /root
git clone https://git.aixiao.me/aixiao/denyhosts
cd denyhosts
make clean; make
make clean
make all
bash build.sh
@ -61,54 +65,63 @@ make clean; make
./rhost # 只处理非法攻击
关闭
关闭:
killall rhost
```
帮助:
./rhost -h
Rhost Reject host&scan for viruses (Rhost 拒绝主机并扫描病毒)
Author: AIXIAO@AIXIAO.ME
Version: 1.0
Usage: [-?h] [-d]
Options:
-d : Background running
-? -h --help : help information
The configuration file needs to be in the same directory as the executable file!
配置文件需要与可执行文件位于同一目录中!
May 22 2024 15:05:59 Compile、link.
```
```text
配置文件
global {
DAEMON = "off"; // on开启后台运行,off不开启
TIME = "10"; // 睡眠时间(大于等于1,单位秒)
TIME = "1"; // 睡眠时间(大于等于1,单位秒)
PUBLIC_IP = "http://inet-ip.info"; // 获取公网IP
PUBLIC_IP = "http://inet-ip.aixiao.me/"; // 获取公网IP
IS_DISK = 1; // 磁盘使用率(1开启,非1关闭)
DISK_USE = 50; // 任意某块磁盘使用率告警(大于等于1)
CLAMAV = 1; // clamav 是否扫描病毒(1开启,非1关闭)
CLAMAV_ARG = "-r / --exclude-dir=^/sys|^/dev|^/proc|^/opt/infected|^/root|^/home --move=/opt/infected --max-filesize 1024M -l clamscan.log";
CLAMAV_TIME = "* 45 11 * * *"; // clamav 扫描时间(Cron格式, 秒 分 时 天 月 周)
IS_BLOCKED = 1; // 是否封禁攻击IP(1开启,非1关闭)
REFUSE_NUMBER = 3; // 拒绝攻击次数
CLAMAV = 1; // clamav 是否扫描病毒(测试阶段)(1开启,非1关闭)
CLAMAV_ARG = "-r / --exclude-dir="^/sys|^/dev|^/proc|^/opt/infected|^/root|^/home|^/mnt" --move=/opt/infected --max-filesize 1024M -l clamscan.log";
CLAMAV_TIME = "* 17 13 * * *"; // clamav 扫描时间(Cron格式, 秒 分 时 天 月 周)
IPV4_RESTRICTION = 1; // 是否启用IP白名单(1开启,非1关闭)
IPV4_WHITE_LIST = "1.1.1.1 "; // IP白名单(空格隔开)
IPV4_WHITE_LIST = "1.1.1.1 2.2.2.2 "; // IP白名单(空格隔开)
REGION = 1; // 是否启用地域白名单(1开启,非1关闭)
REGION_URL = "http://opendata.baidu.com/api.php?query=%s&co=&resource_id=6006&oe=utf8"; // 获取IP地域
REGION_LIST = "河南 郑州"; // 地域列表(空格隔开)
IS_MAIL = 0; // 开启邮件告警(1开启,非1关闭)
IP2REGION = 1; // 是否使用本地 ip2region 地址定位库(1使用,非1不使用)
REGION_LIST = "河南 郑州 上海"; // 地域列表(空格隔开)
NGINX = 1; // 是否启用Nginx白名单
NGINX_LOG_FILE= "/usr/local/nginx/logs/access.log"; // Nginx 日志文件
NGINX_REGION_LIST = "中国 河南 郑州 上海 内网"; // 地域列表(空格隔开)
IS_DING_WEBHOOK = 0; // 开启叮叮告警(1开启,非1关闭)
PHONE = "15565979082"; // @的人手机号
DING_WEBHOOK = "https://oapi.dingtalk.com/robot/send?access_token=7f069c672cb878987aa6772cca336740eece4ce36bde12b51b45e9f440e0565a"; // 钉钉WEBHOOK
DING_WEBHOOK = "https://oapi.dingtalk.com/robot/send?access_token=396bce0384cded025087cff3c176ea5e9afb9bd8fcaa46d6fa8c51dd172ba513"; // 钉钉WEBHOOK
IS_QQMAIL = 1; // 开启QQ邮箱告警(默认使用gomailhttps://git.aixiao.me/aixiao/gomail.git)(1开启,非1关闭)
RECV_MAIL = "1605227279@qq.com"; // 接收者QQ
IS_MAIL = 0; // 开启QQ邮箱告警(默认使用gomail: https://git.aixiao.me/aixiao/gomail.git)(1开启,非1关闭)
RECV_MAIL = "1605227279@qq.com"; // 接收者邮箱
}
```

View File

@ -23,12 +23,13 @@ check_os()
pkg_install()
{
if test "$OS" = "ubuntu" -o "$OS" = "debian"; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh;
apt -y install build-essential
apt -y install make
apt -y install tmux
apt -y install libclamav-dev libip4tc-dev libcurl4-openssl-dev #(或者libcurl4-gnutls-dev)
apt -y install libsystemd-dev libjson-c-dev libpcre2-dev clamav-freshclam
apt -y install libltdl-dev
apt -y install libltdl-dev libmspack-dev
# Debian系统使用libiptc库需要nftables切换到iptables
# Switching to the legacy version:(切换到 iptables)
@ -39,6 +40,7 @@ pkg_install()
freshclam # 更新病毒库(必要)
else
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh;
yum -y groupinstall "Development Tools"
yum -y install make
yum -y install tmux
@ -48,7 +50,7 @@ pkg_install()
yum -y install clamav clamav-update clamav-lib clamav-devel json-c-devel pcre2-devel
yum -y install iptables-devel libcurl-devel
yum -y install systemd-devel libtool-ltdl-devel
yum -y install systemd-devel libtool-ltdl-devel libmspack-devel
yum -y install centos-release-scl
yum -y install devtoolset-11-gcc
@ -71,7 +73,10 @@ main()
#cd ../..
make clean; make
make clean
make libclamav_rust
make libclamav
make all
if test "$OS" = "centos"; then
if test -f /etc/cron.d/clamav-update; then # 去除自动更新病毒库
@ -79,35 +84,81 @@ main()
fi
if test -f /etc/freshclam.conf; then # 更改病毒库镜像
sed -i "s/DatabaseMirror .*/DatabaseMirror clamavdb.c3sl.ufpr.br/g" /etc/freshclam.conf
sed -i "s/DatabaseMirror .*/DatabaseMirror db.cn.clamav.net/g" /etc/freshclam.conf
fi
fi
if test -f /etc/clamav/freshclam.conf; then
sed -i "s/DatabaseMirror .*/DatabaseMirror db.cn.clamav.net/g" /etc/clamav/freshclam.conf
else
:
fi
tmux new-session -s main -d; tmux send -t main 'cd ~/RHOST/; killall rhost; ./rhost -d' ENTER
tmux at -t main
}
binary()
{
cd ~
if ! test -f RHOST; then
mkdir -p RHOST
fi
if test "$OS" = "centos"; then
wget -T 30 https://git.aixiao.me/attachments/18147490-6d10-4407-8537-03b3c1eeb357 -O RHOST/rhost
wget -T 30 https://git.aixiao.me/attachments/e6309b7b-0e98-4a11-8fc1-7c624649f3b8 -O RHOST/rhost.conf
wget -T 30 https://git.aixiao.me/attachments/1ddf1579-b660-4d91-821d-82fe5f0ec8c0 -O RHOST/freshclam.conf
if test -f /etc/cron.d/clamav-update; then # 去除自动更新病毒库
mv /etc/cron.d/clamav-update /root
fi
if test -f /etc/freshclam.conf; then # 更改病毒库镜像
sed -i "s/DatabaseMirror .*/DatabaseMirror clamavdb.c3sl.ufpr.br/g" /etc/freshclam.conf
fi
if test -f /etc/clamav/freshclam.conf; then
sed -i "s/DatabaseMirror .*/DatabaseMirror clamavdb.c3sl.ufpr.br/g" /etc/clamav/freshclam.conf
else
:
fi
chmod +x RHOST/rhost
if test "$OS" = "debian"; then
condition=$(awk -v num1=${OS_VER} -v num2=11 'BEGIN{print(num1>num2)?"0":"1"}')
if test "$condition" = "1"; then # Debian系统版本小于11时, libclamav库版本低时
cd clamscan/libclamav/; bash build.sh
cp /usr/lib/x86_64-linux-gnu/libclamav.so.9.0.5 /usr/lib/x86_64-linux-gnu/libclamav.so.9.0.5.backup
cp .libs/libclamav.so.9.0.5 /usr/lib/x86_64-linux-gnu/
cd ../../
fi
fi
tmux new-session -s main -d; tmux send -t main 'killall rhost; ./rhost -d' ENTER
cd ~/RHOST/
tmux new-session -s main -d; tmux send -t main 'cd ~/RHOST/; killall rhost; ./rhost -d' ENTER
tmux at -t main
elif test "$OS" = "debian"; then
wget -T 30 https://git.aixiao.me/attachments/40a3317f-48eb-4465-9ae3-fc46251c5bcc -O RHOST/rhost
wget -T 30 https://git.aixiao.me/attachments/e6309b7b-0e98-4a11-8fc1-7c624649f3b8 -O RHOST/rhost.conf
wget -T 30 https://git.aixiao.me/attachments/1ddf1579-b660-4d91-821d-82fe5f0ec8c0 -O RHOST/freshclam.conf
chmod +x RHOST/rhost
cd ~/RHOST/
tmux new-session -s main -d; tmux send -t main 'cd ~/RHOST/; killall rhost; ./rhost -d' ENTER
tmux at -t main
fi
}
check_os
pkg_install
if test "${1}" = "binary"; then
binary
exit 0
fi
main

327
clamav/build-libclamav.sh Normal file

File diff suppressed because one or more lines are too long

617
clamav/clamav-config.h Normal file
View File

@ -0,0 +1,617 @@
/* clamav-config.h.cmake.in. Autoconf compatibility layer for CMake. */
/* Define if building universal (internal helper macro) */
/* #undef AC_APPLE_UNIVERSAL_BUILD */
/* mmap flag for anonymous maps */
#define ANONYMOUS_MAP MAP_ANONYMOUS
/* bind 8 compatibility mode, required on some systems to get T_TXT, etc from nameser_compat.h */
/* #undef BIND_8_COMPAT */
/* name of the clamav group */
#define CLAMAVGROUP "clamav"
/* name of the clamav user */
#define CLAMAVUSER "clamav"
/* enable debugging */
/* #undef CL_DEBUG */
/* enable experimental code */
/* #undef CL_EXPERIMENTAL */
/* thread safe */
#define CL_THREAD_SAFE 1
/* curses header location */
#define CURSES_INCLUDE <ncurses.h>
/* os is aix */
/* #undef C_AIX */
/* os is beos */
/* #undef C_BEOS */
/* Increase thread stack size. */
/* #undef C_BIGSTACK */
/* os is bsd flavor */
/* #undef C_BSD */
/* os is darwin */
/* #undef C_DARWIN */
/* target is gnu-hurd */
/* #undef C_GNU_HURD */
/* os is hpux */
/* #undef C_HPUX */
/* os is interix */
/* #undef C_INTERIX */
/* os is irix */
/* #undef C_IRIX */
/* target is kfreebsd-gnu */
/* #undef C_KFREEBSD_GNU */
/* target is linux */
#define C_LINUX 1
/* os is OS/2 */
/* #undef C_OS2 */
/* os is osf/tru64 */
/* #undef C_OSF */
/* os is QNX 6.x.x */
/* #undef C_QNX6 */
/* os is solaris */
/* #undef C_SOLARIS */
#ifndef _WIN32
/* Path to virus database directory. */
#define DATADIR "/var/lib/clamav"
/* where to look for the config file */
#define CONFDIR "/etc/clamav"
#endif
/* Have sys/fanotify.h */
#define HAVE_SYS_FANOTIFY_H 1
/* whether _XOPEN_SOURCE needs to be defined for fd passing to work */
/* #undef FDPASS_NEED_XOPEN */
/* file i/o buffer size */
#define FILEBUFF 8192
/* scan buffer size */
#define SCANBUFF 131072
/* enable workaround for broken DNS servers */
/* #undef FRESHCLAM_DNS_FIX */
/* use "Cache-Control: no-cache" in freshclam */
/* #undef FRESHCLAM_NO_CACHE */
/* attrib aligned */
#define HAVE_ATTRIB_ALIGNED 1
/* attrib packed */
#define HAVE_ATTRIB_PACKED 1
/* have bzip2 */
#define HAVE_BZLIB_H 1
/* Define to 1 if you have the `ctime_r' function. */
/* #undef HAVE_CTIME_R */
/* ctime_r takes 2 arguments */
/* #undef HAVE_CTIME_R_2 */
/* ctime_r takes 3 arguments */
/* #undef HAVE_CTIME_R_3 */
/* Define to 1 if you have the declaration of `cygwin_conv_path', and to 0 if
you don't. */
/* #undef HAVE_DECL_CYGWIN_CONV_PATH */
/* Define to 1 if you have a deprecated version of the 'libjson' library
(-ljson). */
/* #undef HAVE_DEPRECATED_JSON */
/* Define to 1 if you have the <dirent.h> header file. */
#define HAVE_DIRENT_H 1
/* Define if you have the GNU dld library. */
/* #undef HAVE_DLD */
/* Define to 1 if you have the <dld.h> header file. */
/* #undef HAVE_DLD_H */
/* Define to 1 if you have the `dlerror' function. */
/* #undef HAVE_DLERROR */
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define to 1 if you have the <dl.h> header file. */
/* #undef HAVE_DL_H */
/* Define if you have the _dyld_func_lookup function. */
/* #undef HAVE_DYLD */
/* Define to 1 if you have the `enable_extended_FILE_stdio' function. */
/* #undef HAVE_ENABLE_EXTENDED_FILE_STDIO */
/* Define to 1 if the system has the type `error_t'. */
/* #undef HAVE_ERROR_T */
/* have working file descriptor passing support */
#define HAVE_FD_PASSING 1
/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
#define HAVE_FSEEKO 1
/* have getaddrinfo() */
#define HAVE_GETADDRINFO 1
/* Define to 1 if you have the `getnameinfo' function. */
/* #undef HAVE_GETNAMEINFO */
/* Define to 1 if getpagesize() is available */
#define HAVE_GETPAGESIZE 1
/* Define to 1 if you have the <grp.h> header file. */
#define HAVE_GRP_H 1
/* Define if you have the iconv() function and it works. */
#define HAVE_ICONV 1
/* Define to 1 if you have the `initgroups' function. */
#define HAVE_INITGROUPS 1
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the <inttypes.h> header file (for libjson-c). */
#define JSON_C_HAVE_INTTYPES_H 1
/* Define to 1 if you have the 'libjson' library (-ljson). */
#define HAVE_JSON 1
/* Define to '1' if you have the check.h library */
/* #undef HAVE_LIBCHECK */
/* Define to '1' if you have the ncurses.h library */
#define HAVE_LIBNCURSES 1
/* Define to '1' if you have the curses.h library */
/* #undef HAVE_LIBPDCURSES */
/* Define to 1 if you have the `ssl' library (-lssl). */
#define HAVE_LIBSSL 1
/* Define to 1 if you have the 'libxml2' library (-lxml2). */
#define HAVE_LIBXML2 1
/* Define to 1 if you have the `z' library (-lz). */
#define HAVE_LIBZ 1
/* Define to 1 if you have the <limits.h> header file. */
#define HAVE_LIMITS_H 1
/* Define to 1 if you have the `madvise' function. */
/* #undef HAVE_MADVISE */
/* Define to 1 if you have the `mallinfo' function. */
/* #undef HAVE_MALLINFO */
/* Define to 1 if you have the <malloc.h> header file. */
#define HAVE_MALLOC_H 1
/* Define to 1 if you have the `mkstemp' function. */
/* #undef HAVE_MKSTEMP */
/* Define to 1 if you have a working `mmap' system call that supports
MAP_PRIVATE. */
#define HAVE_MMAP 1
/* Define to 1 if you have a pcre library (-lpcre). */
#define HAVE_PCRE 1
/* Define to 1 if you using the pcre2 library. */
#define USING_PCRE2 1
/* Define to 1 if you have the `poll' function. */
#define HAVE_POLL 1
/* Define to 1 if you have the <poll.h> header file. */
#define HAVE_POLL_H 1
/* "pragma pack" */
/* #undef HAVE_PRAGMA_PACK */
/* "pragma pack hppa/hp-ux style" */
/* #undef HAVE_PRAGMA_PACK_HPPA */
/* Define if libtool can extract symbol lists from object files. */
/* #undef HAVE_PRELOADED_SYMBOLS */
/* Define to 1 if you have the <pthread.h> header file */
#define HAVE_PTHREAD_H 1
/* Define to 1 if you have the <pwd.h> header file. */
#define HAVE_PWD_H 1
/* Define to 1 if you have the `readdir' function. */
/* #undef HAVE_READDIR */
/* Define to 1 if you have the `recvmsg' function. */
#define HAVE_RECVMSG 1
/* have resolv.h */
#define HAVE_RESOLV_H 1
/* Define signed right shift implementation */
#define HAVE_SAR 1
/* Define to 1 if you have the `sched_yield' function. */
/* #undef HAVE_SCHED_YIELD */
/* Define to 1 if you have the `sendmsg' function. */
#define HAVE_SENDMSG 1
/* Define to 1 if you have the `setgroups' function. */
/* #undef HAVE_SETGROUPS */
/* Define to 1 if you have the `setsid' function. */
#define HAVE_SETSID 1
/* Define to 1 if you have the `snprintf' function. */
#define HAVE_SNPRINTF 1
/* enable stat64 */
/* #undef HAVE_STAT64 */
/* Define to 1 if you have the <stdbool.h> header file. */
#define HAVE_STDBOOL_H 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the `strcasestr' function. */
/* #undef HAVE_STRCASESTR */
/* Define to 1 if you have the `strerror_r' function. */
#define HAVE_STRERROR_R 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the `strlcat' function. */
/* #undef HAVE_STRLCAT */
/* Define to 1 if you have the `strlcpy' function. */
/* #undef HAVE_STRLCPY */
/* Define to 1 if you have the `strndup' function. */
#define HAVE_STRNDUP 1
/* using internal strn functions */
/* #undef HAVE_STRNI */
/* Define to 1 if you have the `strnlen' function. */
#define HAVE_STRNLEN 1
/* Define to 1 if you have the `strnstr' function. */
/* #undef HAVE_STRNSTR */
/* Define to 1 if sysconf(_SC_PAGESIZE) is available */
#define HAVE_SYSCONF_SC_PAGESIZE 1
/* Define to 1 if you have the `sysctlbyname' function. */
/* #undef HAVE_SYSCTLBYNAME */
/* systemd is supported */
#define HAVE_SYSTEMD 1
/* Use private fts() implementation which is LFS safe */
#define HAVE_SYSTEM_LFS_FTS 1
/* Define to 1 if you have the <sys/cdefs.h> header file. */
#define HAVE_SYS_CDEFS_H 1
/* Define to 1 if you have the <sys/dl.h> header file. */
/* #undef HAVE_SYS_DL_H */
/* Define to 1 if you have the <sys/filio.h> header file. */
/* #undef HAVE_SYS_FILIO_H */
/* Define to 1 if you have the <sys/inttypes.h> header file. */
/* #undef HAVE_SYS_INTTYPES_H */
/* Define to 1 if you have the <sys/int_types.h> header file. */
/* #undef HAVE_SYS_INT_TYPES_H */
/* Define to 1 if you have the <sys/mman.h> header file. */
#define HAVE_SYS_MMAN_H 1
/* Define to 1 if you have the <sys/param.h> header file. */
#define HAVE_SYS_PARAM_H 1
/* Define to 1 if you have the <sys/queue.h> header file. */
#define HAVE_SYS_QUEUE_H 1
/* "have <sys/select.h>" */
#define HAVE_SYS_SELECT_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/times.h> header file. */
#define HAVE_SYS_TIMES_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <sys/uio.h> header file. */
#define HAVE_SYS_UIO_H 1
/* Define to 1 if you have the <termios.h> header file. */
#define HAVE_TERMIOS_H 1
/* Define to 1 if you have the `timegm' function. */
#define HAVE_TIMEGM 1
/* Define this if uname(2) is POSIX */
#define HAVE_UNAME_SYSCALL 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to 1 if you have the `vsnprintf' function. */
#define HAVE_VSNPRINTF 1
/* This value is set to 1 to indicate that the system argz facility works */
/* #undef HAVE_WORKING_ARGZ */
/* yara sources are compiled in */
#define HAVE_YARA 1
/* Define to 1 if you have the <zlib.h> header file. */
#define HAVE_ZLIB_H 1
/* For internal use only - DO NOT DEFINE */
/* #undef HAVE__INTERNAL__SHA_COLLECT */
/* Define as const if the declaration of iconv() needs const. */
/* #undef ICONV_CONST */
/* Define if UNRAR is linked instead of loaded. */
/* #undef UNRAR_LINKED */
/* Define if UNRAR is linked instead of loaded. */
/* #undef HAVE_SYSTEM_TOMSFASTMATH */
/* "Full clamav library version number" */
#define LIBCLAMAV_FULLVER "11.0.0"
/* "Major clamav library version number" */
#define LIBCLAMAV_MAJORVER 11
/* "Full freshclam library version number" */
#define LIBFRESHCLAM_FULLVER "2.0.2"
/* "Major freshclam library version number" */
#define LIBFRESHCLAM_MAJORVER 2
/* The archive extension */
#define LT_LIBEXT ".a"
/* The archive prefix */
#define LT_LIBPREFIX "lib"
/* Define to the extension used for runtime loadable modules, say, ".so" or ".dylib". */
#define LT_MODULE_EXT ".so"
/* Define to the name of the environment variable that determines the run-time
module search path. */
#ifdef _WIN32
#define SEARCH_LIBDIR "/usr"
#else
#define SEARCH_LIBDIR "/usr/lib/x86_64-linux-gnu"
#endif
/* Define to the shared library suffix, say, ".dylib". */
#define LT_SHARED_EXT ".so"
/* disable assertions */
/* #undef NDEBUG */
/* Define if dlsym() requires a leading underscore in symbol names. */
/* #undef NEED_USCORE */
/* bzip funtions do not have bz2 prefix */
/* #undef NOBZ2PREFIX */
/* "no fd_set" */
/* #undef NO_FD_SET */
/* Name of package */
#define PACKAGE "ClamAV"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "https://github.com/Cisco-Talos/clamav/issues"
/* Define to the full name of this package. */
#define PACKAGE_NAME "ClamAV"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "ClamAV 1.0.0"
/* Define to the one symbol short name of this package. */
/* #undef PACKAGE_TARNAME */
/* Define to the home page for this package. */
#define PACKAGE_URL "https://www.clamav.net/"
/* Define to the version of this package. */
#define PACKAGE_VERSION "1.0.0"
/* Libprelude support enabled */
/* #undef PRELUDE */
/* Define whether application use libtool >= 2.0 */
/* #undef PRELUDE_APPLICATION_USE_LIBTOOL2 */
/* Define to if the `setpgrp' function takes no argument. */
/* #undef SETPGRP_VOID */
/* The number of bytes in type int */
#define SIZEOF_INT 4
/* The number of bytes in type long */
#define SIZEOF_LONG 8
/* The number of bytes in type long long */
#define SIZEOF_LONG_LONG 8
/* The number of bytes in type short */
#define SIZEOF_SHORT 2
/* The number of bytes in type void * */
#define SIZEOF_VOID_P 8
/* Define to if you have the ANSI C header files. */
/* #undef STDC_HEADERS */
/* Support for IPv6 */
/* #undef SUPPORT_IPv6 */
/* enable memory pools */
#define USE_MPOOL 1
/* use syslog */
#define USE_SYSLOG 1
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
/* #undef _ALL_SOURCE */
#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
/* #undef _POSIX_PTHREAD_SEMANTICS */
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
/* #undef _TANDEM_SOURCE */
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
/* #undef __EXTENSIONS__ */
#endif
/* LLVM version (if found) */
/* #undef LLVM_VERSION */
/* Version number of package */
#define VERSION "1.0.0"
/* Version suffix for package */
#define VERSION_SUFFIX ""
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
significant byte first (like Motorola and SPARC, unlike Intel). */
#if defined AC_APPLE_UNIVERSAL_BUILD
# if defined __BIG_ENDIAN__
# define WORDS_BIGENDIAN 1
# endif
#else
# ifndef WORDS_BIGENDIAN
/* #undef WORDS_BIGENDIAN */
# endif
#endif
/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
`char[]'. */
/* #undef YYTEXT_POINTER */
/* Enable large inode numbers on Mac OS X 10.5. */
#ifndef _DARWIN_USE_64_BIT_INODE
# define _DARWIN_USE_64_BIT_INODE 1
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
/* #undef _FILE_OFFSET_BITS */
/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
/* #undef _LARGEFILE_SOURCE */
/* Define for large files, on AIX-style hosts. */
/* #undef _LARGE_FILES */
/* Define to 1 if on MINIX. */
/* #undef _MINIX */
/* Define to 2 if the system does not provide POSIX.1 features except with
this defined. */
/* #undef _POSIX_1_SOURCE */
/* POSIX compatibility */
/* #undef _POSIX_PII_SOCKET */
/* Define to 1 if you need to in order for `stat' and other things to work. */
/* #undef _POSIX_SOURCE */
/* thread safe */
#define _REENTRANT 1
/* Define so that glibc/gnulib argp.h does not typedef error_t. */
/* #undef __error_t_defined */
/* Define to `__inline__' or `__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
#ifndef __cplusplus
#define inline inline
#endif
/* Define to `long int' if <sys/types.h> does not define. */
/* #undef off_t */
/* Define to `int' if <sys/types.h> does not define. */
/* #undef ssize_t */
/* Define to the equivalent of the C99 'restrict' keyword, or to
nothing if this is not supported. Do not define if restrict is
supported directly. */
#define restrict __restrict
/* Work around a bug in Sun C++: it does not support _Restrict or
__restrict__, even though the corresponding Sun C compiler ends up with
"#define restrict _Restrict" or "#define restrict __restrict__" in the
previous line. Perhaps some future version of Sun C++ will work with
restrict; if so, hopefully it defines __RESTRICT like Sun C does. */
#if defined __SUNPRO_CC && !defined __RESTRICT
# define _Restrict
# define __restrict__
#endif
/* Define to "int" if <sys/socket.h> does not define. */
/* #undef socklen_t */
#include "platform.h"

84
clamav/clamav-types.h Normal file
View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2007-2013 Sourcefire, Inc.
*
* Authors: Tomasz Kojm, Micah Snyder
*
* WARNING: This file was generated by CMake. Do not edit!
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#ifndef __CLAMAV_TYPES_H
#define __CLAMAV_TYPES_H
#include <inttypes.h>
/* Ensure we have print format types */
/* PRIu64 should be in <inttypes.h> */
#ifndef _SF64_PREFIX
#define _SF64_PREFIX "l"
#endif
#ifndef PRIu64
#define PRIu64 _SF64_PREFIX "u"
#endif
#ifndef PRIx64
#define PRIx64 _SF64_PREFIX "i"
#endif
#ifndef PRIi64
#define PRIi64 _SF64_PREFIX "x"
#endif
#ifndef STDu64
#define STDu64 "%" PRIu64
#define STDi64 "%" PRIi64
#define STDx64 "%" PRIx64
#endif
/* PRIu32 should also be in <inttypes.h> */
#ifndef PRIu32
#ifndef _SF32_PREFIX
#define _SF32_PREFIX ""
#endif
#define PRIu32 _SF32_PREFIX "u"
#define PRIi32 _SF32_PREFIX "i"
#define PRIx32 _SF32_PREFIX "x"
#endif
#ifndef STDu32
#define STDu32 "%" PRIu32
#define STDi32 "%" PRIi32
#define STDx32 "%" PRIx32
#endif
#ifndef INT32_MAX
#define INT32_MAX 2147483647
#endif
#endif

68
clamav/clamav-version.h Normal file
View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2019-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*
* Authors: Micah Snyder
*
* WARNING: This file was generated by CMake. Do not edit!
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#ifndef CLAMAV_VER_H
#define CLAMAV_VER_H
/**
* @macro
* Version number of the clamav package release
*/
#define CLAMAV_VERSION "1.0.0"
/**
* @macro
* Numerical representation of the version number of the clamav package
* release. This is a 24 bit number with 8 bits for major number, 8 bits
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
*/
#define CLAMAV_VERSION_NUM 0x010000
/**
* @macro
* Version number of the clamav library release
*/
#define LIBCLAMAV_VERSION "11.0.0"
/**
* @macro
* Numerical representation of the version number of the libclamav library
* release. This is a 24 bit number with 8 bits for major number, 8 bits
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
*/
#define LIBCLAMAV_VERSION_NUM 0x0b0000
/**
* @macro
* Version number of the clamav library release
*/
#define LIBFRESHCLAM_VERSION "2.0.2"
/**
* @macro
* Numerical representation of the version number of the libfreshclam library
* release. This is a 24 bit number with 8 bits for major number, 8 bits
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
*/
#define LIBFRESHCLAM_VERSION_NUM 0x020200
#endif /* CLAMAV_VER_H */

208
clamav/clamav_rust.h Normal file
View File

@ -0,0 +1,208 @@
/* Copyright (C) 2021-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved. */
#ifndef __CLAMAV_RUST_H
#define __CLAMAV_RUST_H
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "clamav.h"
#include "matcher-ac.h"
typedef struct cli_matcher cli_matcher; typedef struct cli_ctx_tag cli_ctx;
typedef enum IndicatorType {
/**
* For hash-based indicators.
*/
IndicatorType_Strong,
/**
* For potentially unwanted applications/programs that are not malicious but may be used maliciously.
*/
IndicatorType_PotentiallyUnwanted,
/**
* Weak indicators that together with other indicators can be used to form a stronger indicator.
* This type of indicator should NEVER alert the user on its own.
*/
IndicatorType_Weak,
} IndicatorType;
/**
* A generic container for any error that implements `Into<std::error::Error>`
*/
typedef struct FFIError FFIError;
bool script2cdiff(const char *script, const char *builder, const char *server);
/**
* This function is only meant to be called from sigtool.c
*/
int32_t cdiff_apply(int32_t fd, uint16_t mode);
/**
* Initialize a match vector
*/
evidence_t evidence_new(void);
/**
* Free the evidence
*/
void evidence_free(evidence_t evidence);
/**
* C interface for Evidence::render_verdict().
* Handles all the unsafe ffi stuff.
*
* Render a verdict based on the evidence, depending on the severity of the
* indicators found and the scan configuration.
*
* The individual alerting-indicators would have already been printed at this point.
*
* # Safety
*
* No parameters may be NULL
*/
bool evidence_render_verdict(evidence_t evidence);
/**
* C interface to get a string name for one of the alerts.
* Will first check for one from the strong indicators, then pua.
*
* # Safety
*
* Returns a string that is either static, or allocated when reading the database.
* So the lifetime of the string is good at least until you reload or unload the databases.
*
* No parameters may be NULL
*/
const char *evidence_get_last_alert(evidence_t evidence);
/**
* C interface to get a string name for one of the alerts.
* Will first check for one from the strong indicators, then pua.
*
* # Safety
*
* Returns a string that is either static, or allocated when reading the database.
* So the lifetime of the string is good at least until you reload or unload the databases.
*
* No parameters may be NULL
*/
const char *evidence_get_indicator(evidence_t evidence,
enum IndicatorType indicator_type,
uintptr_t index);
/**
* C interface to check number of alerting indicators in evidence.
*
* # Safety
*
* No parameters may be NULL
*/
uintptr_t evidence_num_alerts(evidence_t evidence);
/**
* C interface to check number of indicators in evidence.
* Handles all the unsafe ffi stuff.
*
* # Safety
*
* No parameters may be NULL
*/
uintptr_t evidence_num_indicators_type(evidence_t evidence, enum IndicatorType indicator_type);
/**
* C interface for Evidence::add_indicator().
* Handles all the unsafe ffi stuff.
*
* Add an indicator to the evidence.
*
* # Safety
*
* `hexsig` and `err` must not be NULL
*/
bool evidence_add_indicator(evidence_t evidence,
const char *name,
enum IndicatorType indicator_type,
struct FFIError **err);
/**
* Compute (and cache) a formatted error string from the provided [`FFIError`] pointer.
*
* # Safety
*
* `err` must not be NULL
*/
const char *ffierror_fmt(struct FFIError *err);
/**
* Free a [`FFIError`] structure
*
* # Safety
*
* `err` must not be NULL
*/
void ffierror_free(struct FFIError *err);
/**
* Initialize the hashmap
*/
fuzzyhashmap_t fuzzy_hashmap_new(void);
/**
* Free the hashmap
*/
void fuzzy_hash_free_hashmap(fuzzyhashmap_t fuzzy_hashmap);
/**
* C interface for FuzzyHashMap::check().
* Handles all the unsafe ffi stuff.
*
* # Safety
*
* No parameters may be NULL
*/
bool fuzzy_hash_check(fuzzyhashmap_t fuzzy_hashmap,
cli_ac_data *mdata,
image_fuzzy_hash_t image_fuzzy_hash);
/**
* C interface for FuzzyHashMap::load_subsignature().
* Handles all the unsafe ffi stuff.
*
* # Safety
*
* `hexsig` and `err` must not be NULL
*/
bool fuzzy_hash_load_subsignature(fuzzyhashmap_t fuzzy_hashmap,
const char *hexsig,
uint32_t lsig_id,
uint32_t subsig_id,
struct FFIError **err);
/**
* C interface for fuzzy_hash_calculate_image().
* Handles all the unsafe ffi stuff.
*
* # Safety
*
* `file_bytes` and `hash_out` must not be NULL
*/
bool fuzzy_hash_calculate_image(const uint8_t *file_bytes,
uintptr_t file_size,
uint8_t *hash_out,
uintptr_t hash_out_len,
struct FFIError **err);
bool clrs_log_init(void);
/**
* API exported for C code to log to standard error using Rust.
* This would be be an alternative to fputs, and reliably prints
* non-ASCII UTF8 characters on Windows, where fputs does not.
*/
void clrs_eprint(const char *c_buf);
#endif /* __CLAMAV_RUST_H */

View File

@ -0,0 +1,48 @@
# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
if(WIN32)
add_definitions(-DWIN32_LEAN_AND_MEAN)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE)
# Windows compatibility headers
include_directories(${CMAKE_SOURCE_DIR}/win32/compat)
endif()
# The clamscan executable.
add_executable( clamscan )
target_sources( clamscan
PRIVATE
clamscan.c
manager.c
manager.h
global.h )
if(WIN32)
target_sources( clamscan
PRIVATE
${CMAKE_SOURCE_DIR}/win32/res/clamscan.rc
${CMAKE_SOURCE_DIR}/win32/res/clam.manifest )
endif()
target_include_directories( clamscan
PRIVATE ${CMAKE_BINARY_DIR} # For clamav-config.h
)
set_target_properties( clamscan PROPERTIES COMPILE_FLAGS "${WARNCFLAGS}" )
if (APPLE AND CLAMAV_SIGN_FILE)
set_target_properties( clamscan PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ${CODE_SIGN_IDENTITY}
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${DEVELOPMENT_TEAM_ID}
)
endif()
target_link_libraries( clamscan
PRIVATE
ClamAV::libclamav
ClamAV::common )
if(WIN32)
install(TARGETS clamscan DESTINATION . COMPONENT programs)
install(FILES $<TARGET_PDB_FILE:clamscan> DESTINATION . OPTIONAL COMPONENT programs)
else()
install(TARGETS clamscan DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT programs)
endif()

21
clamav/clamscan/Doxyfile Normal file
View File

@ -0,0 +1,21 @@
PROJECT_NAME = ClamAV - Clamscan
OUTPUT_DIRECTORY = ../docs/clamscan
WARNINGS = YES
FILE_PATTERNS = *.c *.h
PERL_PATH = /usr/bin/perl
SEARCHENGINE = YES
GENERATE_LATEX=NO
OPTIMIZE_OUTPUT_FOR_C=YES
HAVE_DOT=YES
CALL_GRAPH=YES
CALLER_GRAPH=YES
JAVADOC_AUTOBRIEF=YES
GENERATE_MAN=NO
EXAMPLE_PATH=examples
DOT_CLEANUP=NO
MAX_DOT_GRAPH_DEPTH=3
EXTRACT_ALL=YES
INPUT = .

346
clamav/clamscan/clamscan.c Normal file
View File

@ -0,0 +1,346 @@
/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2007-2013 Sourcefire, Inc.
*
* Authors: Tomasz Kojm
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <locale.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifndef _WIN32
#include <sys/time.h>
#endif
#include <time.h>
#ifdef C_LINUX
#include <sys/resource.h>
#endif
// libclamav
#include "clamav.h"
#include "others.h"
#include "str.h"
// common
#include "misc.h"
#include "output.h"
#include "actions.h"
#include "optparser.h"
#include "global.h"
#include "manager.h"
void help(void);
struct s_info info;
short recursion = 0, bell = 0;
short printinfected = 0, printclean = 1;
int _clamscan(int argc, char **argv)
{
int ds, dms, ret;
double mb, rmb;
struct timeval t1, t2;
time_t date_start, date_end;
char buffer[26];
#ifndef _WIN32
sigset_t sigset;
#endif
struct optstruct *opts;
const struct optstruct *opt;
if (check_flevel())
exit(2);
#if !defined(_WIN32)
if (!setlocale(LC_CTYPE, "")) {
mprintf(LOGG_WARNING, "Failed to set locale\n");
}
#if !defined(C_BEOS)
sigemptyset(&sigset);
sigaddset(&sigset, SIGXFSZ);
sigprocmask(SIG_SETMASK, &sigset, NULL);
#endif /* !C_BEOS */
#endif /* !_WIN32 */
cl_initialize_crypto();
if ((opts = optparse(NULL, argc, argv, 1, OPT_CLAMSCAN, 0, NULL)) == NULL) {
mprintf(LOGG_ERROR, "Can't parse command line options\n");
return 2;
}
if (optget(opts, "verbose")->enabled) {
mprintf_verbose = 1;
logg_verbose = 1;
}
if (optget(opts, "quiet")->enabled)
mprintf_quiet = 1;
if (optget(opts, "stdout")->enabled)
mprintf_stdout = 1;
if (optget(opts, "debug")->enabled) {
#if defined(C_LINUX)
/* njh@bandsman.co.uk: create a dump if needed */
struct rlimit rlim;
rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
if (setrlimit(RLIMIT_CORE, &rlim) < 0)
perror("setrlimit");
#endif
cl_debug(); /* enable debug messages */
}
if (optget(opts, "gen-mdb")->enabled) {
cl_always_gen_section_hash();
}
if (optget(opts, "version")->enabled) {
print_version(optget(opts, "database")->strarg);
optfree(opts);
return 0;
}
if (optget(opts, "help")->enabled) {
optfree(opts);
help();
return 0;
}
if (optget(opts, "recursive")->enabled)
recursion = 1;
if (optget(opts, "infected")->enabled)
printinfected = 1;
if (optget(opts, "suppress-ok-results")->enabled)
printclean = 0;
if (optget(opts, "bell")->enabled)
bell = 1;
/* initialize logger */
if ((opt = optget(opts, "log"))->enabled) {
logg_file = opt->strarg;
if (logg(LOGG_INFO_NF, "\n-------------------------------------------------------------------------------\n\n")) {
mprintf(LOGG_ERROR, "Problem with internal logger.\n");
optfree(opts);
return 2;
}
} else
logg_file = NULL;
if (actsetup(opts)) {
optfree(opts);
logg_close();
exit(2);
}
memset(&info, 0, sizeof(struct s_info));
date_start = time(NULL);
gettimeofday(&t1, NULL);
ret = scanmanager(opts);
if (!optget(opts, "no-summary")->enabled) {
struct tm tmp;
date_end = time(NULL);
gettimeofday(&t2, NULL);
ds = t2.tv_sec - t1.tv_sec;
dms = t2.tv_usec - t1.tv_usec;
ds -= (dms < 0) ? (1) : (0);
dms += (dms < 0) ? (1000000) : (0);
logg(LOGG_INFO, "\n----------- SCAN SUMMARY -----------\n");
logg(LOGG_INFO, "Known viruses: %u\n", info.sigs);
logg(LOGG_INFO, "Engine version: %s\n", get_version());
logg(LOGG_INFO, "Scanned directories: %u\n", info.dirs);
logg(LOGG_INFO, "Scanned files: %u\n", info.files);
logg(LOGG_INFO, "Infected files: %u\n", info.ifiles);
if (info.errors)
logg(LOGG_INFO, "Total errors: %u\n", info.errors);
if (notremoved) {
logg(LOGG_INFO, "Not removed: %u\n", notremoved);
}
if (notmoved) {
logg(LOGG_INFO, "Not %s: %u\n", optget(opts, "copy")->enabled ? "moved" : "copied", notmoved);
}
mb = info.blocks * (CL_COUNT_PRECISION / 1024) / 1024.0;
logg(LOGG_INFO, "Data scanned: %2.2lf MB\n", mb);
rmb = info.rblocks * (CL_COUNT_PRECISION / 1024) / 1024.0;
logg(LOGG_INFO, "Data read: %2.2lf MB (ratio %.2f:1)\n", rmb, info.rblocks ? (double)info.blocks / (double)info.rblocks : 0);
logg(LOGG_INFO, "Time: %u.%3.3u sec (%u m %u s)\n", ds, dms / 1000, ds / 60, ds % 60);
#ifdef _WIN32
if (0 != localtime_s(&tmp, &date_start)) {
#else
if (!localtime_r(&date_start, &tmp)) {
#endif
logg(LOGG_ERROR, "Failed to get local time for Start Date.\n");
}
strftime(buffer, sizeof(buffer), "%Y:%m:%d %H:%M:%S", &tmp);
logg(LOGG_INFO, "Start Date: %s\n", buffer);
#ifdef _WIN32
if (0 != localtime_s(&tmp, &date_end)) {
#else
if (!localtime_r(&date_end, &tmp)) {
#endif
logg(LOGG_ERROR, "Failed to get local time for End Date.\n");
}
strftime(buffer, sizeof(buffer), "%Y:%m:%d %H:%M:%S", &tmp);
logg(LOGG_INFO, "End Date: %s\n", buffer);
}
optfree(opts);
return ret;
}
void help(void)
{
mprintf_stdout = 1;
mprintf(LOGG_INFO, "\n");
mprintf(LOGG_INFO, " Clam AntiVirus: Scanner %s\n", get_version());
mprintf(LOGG_INFO, " By The ClamAV Team: https://www.clamav.net/about.html#credits\n");
mprintf(LOGG_INFO, " (C) 2022 Cisco Systems, Inc.\n");
mprintf(LOGG_INFO, "\n");
mprintf(LOGG_INFO, " clamscan [options] [file/directory/-]\n");
mprintf(LOGG_INFO, "\n");
mprintf(LOGG_INFO, " --help -h Show this help\n");
mprintf(LOGG_INFO, " --version -V Print version number\n");
mprintf(LOGG_INFO, " --verbose -v Be verbose\n");
mprintf(LOGG_INFO, " --archive-verbose -a Show filenames inside scanned archives\n");
mprintf(LOGG_INFO, " --debug Enable libclamav's debug messages\n");
mprintf(LOGG_INFO, " --quiet Only output error messages\n");
mprintf(LOGG_INFO, " --stdout Write to stdout instead of stderr. Does not affect 'debug' messages.\n");
mprintf(LOGG_INFO, " --no-summary Disable summary at end of scanning\n");
mprintf(LOGG_INFO, " --infected -i Only print infected files\n");
mprintf(LOGG_INFO, " --suppress-ok-results -o Skip printing OK files\n");
mprintf(LOGG_INFO, " --bell Sound bell on virus detection\n");
mprintf(LOGG_INFO, "\n");
mprintf(LOGG_INFO, " --tempdir=DIRECTORY Create temporary files in DIRECTORY\n");
mprintf(LOGG_INFO, " --leave-temps[=yes/no(*)] Do not remove temporary files\n");
mprintf(LOGG_INFO, " --gen-json[=yes/no(*)] Generate JSON metadata for the scanned file(s). For testing & development use ONLY.\n");
mprintf(LOGG_INFO, " JSON will be printed if --debug is enabled.\n");
mprintf(LOGG_INFO, " A JSON file will dropped to the temp directory if --leave-temps is enabled.\n");
mprintf(LOGG_INFO, " --database=FILE/DIR -d FILE/DIR Load virus database from FILE or load all supported db files from DIR\n");
mprintf(LOGG_INFO, " --official-db-only[=yes/no(*)] Only load official signatures\n");
mprintf(LOGG_INFO, " --log=FILE -l FILE Save scan report to FILE\n");
mprintf(LOGG_INFO, " --recursive[=yes/no(*)] -r Scan subdirectories recursively\n");
mprintf(LOGG_INFO, " --allmatch[=yes/no(*)] -z Continue scanning within file after finding a match\n");
mprintf(LOGG_INFO, " --cross-fs[=yes(*)/no] Scan files and directories on other filesystems\n");
mprintf(LOGG_INFO, " --follow-dir-symlinks[=0/1(*)/2] Follow directory symlinks (0 = never, 1 = direct, 2 = always)\n");
mprintf(LOGG_INFO, " --follow-file-symlinks[=0/1(*)/2] Follow file symlinks (0 = never, 1 = direct, 2 = always)\n");
mprintf(LOGG_INFO, " --file-list=FILE -f FILE Scan files from FILE\n");
mprintf(LOGG_INFO, " --remove[=yes/no(*)] Remove infected files. Be careful!\n");
mprintf(LOGG_INFO, " --move=DIRECTORY Move infected files into DIRECTORY\n");
mprintf(LOGG_INFO, " --copy=DIRECTORY Copy infected files into DIRECTORY\n");
mprintf(LOGG_INFO, " --exclude=REGEX Don't scan file names matching REGEX\n");
mprintf(LOGG_INFO, " --exclude-dir=REGEX Don't scan directories matching REGEX\n");
mprintf(LOGG_INFO, " --include=REGEX Only scan file names matching REGEX\n");
mprintf(LOGG_INFO, " --include-dir=REGEX Only scan directories matching REGEX\n");
#ifdef _WIN32
mprintf(LOGG_INFO, " --memory Scan loaded executable modules\n");
mprintf(LOGG_INFO, " --kill Kill/Unload infected loaded modules\n");
mprintf(LOGG_INFO, " --unload Unload infected modules from processes\n");
#endif
mprintf(LOGG_INFO, "\n");
mprintf(LOGG_INFO, " --bytecode[=yes(*)/no] Load bytecode from the database\n");
mprintf(LOGG_INFO, " --bytecode-unsigned[=yes/no(*)] Load unsigned bytecode\n");
mprintf(LOGG_INFO, " **Caution**: You should NEVER run bytecode signatures from untrusted sources.\n");
mprintf(LOGG_INFO, " Doing so may result in arbitrary code execution.\n");
mprintf(LOGG_INFO, " --bytecode-timeout=N Set bytecode timeout (in milliseconds)\n");
mprintf(LOGG_INFO, " --statistics[=none(*)/bytecode/pcre] Collect and print execution statistics\n");
mprintf(LOGG_INFO, " --detect-pua[=yes/no(*)] Detect Possibly Unwanted Applications\n");
mprintf(LOGG_INFO, " --exclude-pua=CAT Skip PUA sigs of category CAT\n");
mprintf(LOGG_INFO, " --include-pua=CAT Load PUA sigs of category CAT\n");
mprintf(LOGG_INFO, " --detect-structured[=yes/no(*)] Detect structured data (SSN, Credit Card)\n");
mprintf(LOGG_INFO, " --structured-ssn-format=X SSN format (0=normal,1=stripped,2=both)\n");
mprintf(LOGG_INFO, " --structured-ssn-count=N Min SSN count to generate a detect\n");
mprintf(LOGG_INFO, " --structured-cc-count=N Min CC count to generate a detect\n");
mprintf(LOGG_INFO, " --structured-cc-mode=X CC mode (0=credit debit and private label, 1=credit cards only\n");
mprintf(LOGG_INFO, " --scan-mail[=yes(*)/no] Scan mail files\n");
mprintf(LOGG_INFO, " --phishing-sigs[=yes(*)/no] Enable email signature-based phishing detection\n");
mprintf(LOGG_INFO, " --phishing-scan-urls[=yes(*)/no] Enable URL signature-based phishing detection\n");
mprintf(LOGG_INFO, " --heuristic-alerts[=yes(*)/no] Heuristic alerts\n");
mprintf(LOGG_INFO, " --heuristic-scan-precedence[=yes/no(*)] Stop scanning as soon as a heuristic match is found\n");
mprintf(LOGG_INFO, " --normalize[=yes(*)/no] Normalize html, script, and text files. Use normalize=no for yara compatibility\n");
mprintf(LOGG_INFO, " --scan-pe[=yes(*)/no] Scan PE files\n");
mprintf(LOGG_INFO, " --scan-elf[=yes(*)/no] Scan ELF files\n");
mprintf(LOGG_INFO, " --scan-ole2[=yes(*)/no] Scan OLE2 containers\n");
mprintf(LOGG_INFO, " --scan-pdf[=yes(*)/no] Scan PDF files\n");
mprintf(LOGG_INFO, " --scan-swf[=yes(*)/no] Scan SWF files\n");
mprintf(LOGG_INFO, " --scan-html[=yes(*)/no] Scan HTML files\n");
mprintf(LOGG_INFO, " --scan-xmldocs[=yes(*)/no] Scan xml-based document files\n");
mprintf(LOGG_INFO, " --scan-hwp3[=yes(*)/no] Scan HWP3 files\n");
mprintf(LOGG_INFO, " --scan-archive[=yes(*)/no] Scan archive files (supported by libclamav)\n");
mprintf(LOGG_INFO, " --alert-broken[=yes/no(*)] Alert on broken executable files (PE & ELF)\n");
mprintf(LOGG_INFO, " --alert-broken-media[=yes/no(*)] Alert on broken graphics files (JPEG, TIFF, PNG, GIF)\n");
mprintf(LOGG_INFO, " --alert-encrypted[=yes/no(*)] Alert on encrypted archives and documents\n");
mprintf(LOGG_INFO, " --alert-encrypted-archive[=yes/no(*)] Alert on encrypted archives\n");
mprintf(LOGG_INFO, " --alert-encrypted-doc[=yes/no(*)] Alert on encrypted documents\n");
mprintf(LOGG_INFO, " --alert-macros[=yes/no(*)] Alert on OLE2 files containing VBA macros\n");
mprintf(LOGG_INFO, " --alert-exceeds-max[=yes/no(*)] Alert on files that exceed max file size, max scan size, or max recursion limit\n");
mprintf(LOGG_INFO, " --alert-phishing-ssl[=yes/no(*)] Alert on emails containing SSL mismatches in URLs\n");
mprintf(LOGG_INFO, " --alert-phishing-cloak[=yes/no(*)] Alert on emails containing cloaked URLs\n");
mprintf(LOGG_INFO, " --alert-partition-intersection[=yes/no(*)] Alert on raw DMG image files containing partition intersections\n");
mprintf(LOGG_INFO, " --nocerts Disable authenticode certificate chain verification in PE files\n");
mprintf(LOGG_INFO, " --dumpcerts Dump authenticode certificate chain in PE files\n");
mprintf(LOGG_INFO, "\n");
mprintf(LOGG_INFO, " --max-scantime=#n Scan time longer than this will be skipped and assumed clean (milliseconds)\n");
mprintf(LOGG_INFO, " --max-filesize=#n Files larger than this will be skipped and assumed clean\n");
mprintf(LOGG_INFO, " --max-scansize=#n The maximum amount of data to scan for each container file (**)\n");
mprintf(LOGG_INFO, " --max-files=#n The maximum number of files to scan for each container file (**)\n");
mprintf(LOGG_INFO, " --max-recursion=#n Maximum archive recursion level for container file (**)\n");
mprintf(LOGG_INFO, " --max-dir-recursion=#n Maximum directory recursion level\n");
mprintf(LOGG_INFO, " --max-embeddedpe=#n Maximum size file to check for embedded PE\n");
mprintf(LOGG_INFO, " --max-htmlnormalize=#n Maximum size of HTML file to normalize\n");
mprintf(LOGG_INFO, " --max-htmlnotags=#n Maximum size of normalized HTML file to scan\n");
mprintf(LOGG_INFO, " --max-scriptnormalize=#n Maximum size of script file to normalize\n");
mprintf(LOGG_INFO, " --max-ziptypercg=#n Maximum size zip to type reanalyze\n");
mprintf(LOGG_INFO, " --max-partitions=#n Maximum number of partitions in disk image to be scanned\n");
mprintf(LOGG_INFO, " --max-iconspe=#n Maximum number of icons in PE file to be scanned\n");
mprintf(LOGG_INFO, " --max-rechwp3=#n Maximum recursive calls to HWP3 parsing function\n");
#if HAVE_PCRE
mprintf(LOGG_INFO, " --pcre-match-limit=#n Maximum calls to the PCRE match function.\n");
mprintf(LOGG_INFO, " --pcre-recmatch-limit=#n Maximum recursive calls to the PCRE match function.\n");
mprintf(LOGG_INFO, " --pcre-max-filesize=#n Maximum size file to perform PCRE subsig matching.\n");
#endif /* HAVE_PCRE */
mprintf(LOGG_INFO, " --disable-cache Disable caching and cache checks for hash sums of scanned files.\n");
mprintf(LOGG_INFO, "\n");
mprintf(LOGG_INFO, "Pass in - as the filename for stdin.\n");
mprintf(LOGG_INFO, "\n");
mprintf(LOGG_INFO, "(*) Default scan settings\n");
mprintf(LOGG_INFO, "(**) Certain files (e.g. documents, archives, etc.) may in turn contain other\n");
mprintf(LOGG_INFO, " files inside. The above options ensure safe processing of this kind of data.\n\n");
}

1676
clamav/clamscan/manager.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,84 @@
# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
if(WIN32)
add_definitions(-DWIN32_LEAN_AND_MEAN)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE)
# Windows compatibility headers
include_directories(${CMAKE_SOURCE_DIR}/win32/compat)
endif()
# The "common" static library.
add_library( common STATIC )
target_sources( common
PRIVATE
cert_util.c
actions.c
clamdcom.c
getopt.c
hostid.c
idmef_logging.c
misc.c
optparser.c
output.c
tar.c
PUBLIC
cert_util.h
actions.h
clamdcom.h
fdpassing.h
getopt.h
hostid.h
idmef_logging.h
misc.h
optparser.h
output.h
tar.h )
target_include_directories( common
PRIVATE ${CMAKE_BINARY_DIR}
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} )
if(FOUND_SYSTEMD)
target_include_directories( common
PRIVATE ${SYSTEMD_INCLUDE_DIRS} )
endif()
if(APPLE)
target_sources( common PRIVATE mac/cert_util_mac.m )
elseif(WIN32)
target_sources( common
PRIVATE service.c scanmem.c exescanner.c
PUBLIC service.h scanmem.h exescanner.h )
target_sources( common PRIVATE win/cert_util_win.c )
else()
target_sources( common PRIVATE linux/cert_util_linux.c )
endif()
target_link_libraries( common
PUBLIC
ClamAV::libclamav
ZLIB::ZLIB
CURL::libcurl
OpenSSL::SSL
OpenSSL::Crypto )
if(WIN32)
target_link_libraries( common
PUBLIC
crypt32
psapi)
endif()
if(HAVE_SYSTEMD)
target_link_libraries( common
PRIVATE
SYSTEMD::systemd )
endif()
set_target_properties( common PROPERTIES COMPILE_FLAGS "${WARNCFLAGS}" )
if(WIN32)
set_target_properties(common PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif()
add_library( ClamAV::common ALIAS common )

717
clamav/common/actions.c Normal file
View File

@ -0,0 +1,717 @@
/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2009-2013 Sourcefire, Inc.
*
* Author: aCaB, Micah Snyder
*
* These functions are actions that may be taken when a sample alerts.
* The user may wish to:
* - move file to destination directory.
* - copy file to destination directory.
* - remove (delete) the file.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#ifdef _WIN32
#include <windows.h>
#include <winternl.h>
#endif
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdbool.h>
#include <fcntl.h>
#include <errno.h>
#include <libgen.h>
// libclamav
#include "clamav.h"
#include "str.h"
#include "others.h"
#include "optparser.h"
#include "output.h"
#include "misc.h"
#include "actions.h"
void (*action)(const char *) = NULL;
unsigned int notmoved = 0, notremoved = 0;
static char *actarget;
static int targlen;
static int getdest(const char *fullpath, char **newname)
{
char *tmps, *filename;
int fd, i;
tmps = strdup(fullpath);
if (!tmps) {
*newname = NULL;
return -1;
}
filename = basename(tmps);
if (!(*newname = (char *)malloc(targlen + strlen(filename) + 6))) {
free(tmps);
return -1;
}
sprintf(*newname, "%s" PATHSEP "%s", actarget, filename);
for (i = 1; i < 1000; i++) {
fd = open(*newname, O_WRONLY | O_CREAT | O_EXCL, 0600);
if (fd >= 0) {
free(tmps);
return fd;
}
if (errno != EEXIST) break;
sprintf(*newname, "%s" PATHSEP "%s.%03u", actarget, filename, i);
}
free(tmps);
free(*newname);
*newname = NULL;
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(LOGG_INFO, "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(LOGG_INFO, "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(LOGG_INFO, "win32_openat: Failed to open file '%s'. \nError: 0x%x \nioStatusBlock: 0x%x\n", filename, ntStatus, ioStatusBlock.Information);
goto done;
}
logg(LOGG_DEBUG, "win32_openat: Opened file \"%s\"\n", filename);
if (0 == GetFileInformationByHandleEx(
next_handle, // hFile,
FileAttributeTagInfo, // FileInformationClass
&tagInfo, // lpFileInformation
sizeof(FILE_ATTRIBUTE_TAG_INFO))) { // dwBufferSize
logg(LOGG_INFO, "win32_openat: Failed to get file information by handle '%s'. Error: %d.\n", filename, GetLastError());
CloseHandle(next_handle);
next_handle = NULL;
goto done;
}
logg(LOGG_DEBUG, "win32_openat: tagInfo.FileAttributes: 0x%0x\n", tagInfo.FileAttributes);
logg(LOGG_DEBUG, "win32_openat: tagInfo.ReparseTag: 0x%0x\n", tagInfo.ReparseTag);
if (0 != (tagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
logg(LOGG_INFO, "win32_openat: File is a soft link: '%s' Aborting path traversal.\n\n", filename);
CloseHandle(next_handle);
next_handle = NULL;
goto done;
}
logg(LOGG_DEBUG, "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(LOGG_INFO, "traverse_to: Invalid arguments!\n");
goto done;
}
#ifdef _WIN32
ntdll = LoadLibraryA("ntdll.dll");
if (NULL == ntdll) {
logg(LOGG_INFO, "traverse_to: failed to load ntdll!\n");
goto done;
}
pNtCreateFile = (PNTCF)GetProcAddress(ntdll, "NtCreateFile");
if (NULL == pNtCreateFile) {
logg(LOGG_INFO, "traverse_to: failed to get NtCreateFile proc address!\n");
goto done;
}
pRtlInitUnicodeString = (PRIUS)GetProcAddress(ntdll, "RtlInitUnicodeString");
if (NULL == pRtlInitUnicodeString) {
logg(LOGG_INFO, "traverse_to: failed to get pRtlInitUnicodeString proc address!\n");
goto done;
}
#endif
tokenized_directory = strdup(directory);
if (NULL == tokenized_directory) {
logg(LOGG_INFO, "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(LOGG_INFO, "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(LOGG_INFO, "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(LOGG_INFO, "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(LOGG_INFO, "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_GENERIC_READ | 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(LOGG_INFO, "traverse_to: Failed open %s\n", tokens[i]);
goto done;
}
CloseHandle(current_handle);
current_handle = next_handle;
next_handle = NULL;
#endif
logg(LOGG_DEBUG, "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(LOGG_INFO, "traverse_rename: Invalid arguments!\n");
goto done;
}
#ifndef _WIN32
if (0 != traverse_to(source, true, &source_directory_fd)) {
logg(LOGG_INFO, "traverse_rename: Failed to open file descriptor for source directory!\n");
goto done;
}
#else
if (0 != traverse_to(source, false, &source_file_handle)) {
logg(LOGG_INFO, "traverse_rename: Failed to open file descriptor for source file!\n");
goto done;
}
if (0 != traverse_to(destination, true, &destination_dir_handle)) {
logg(LOGG_INFO, "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(LOGG_INFO, "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(LOGG_INFO, "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(LOGG_INFO, "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(LOGG_INFO, "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(LOGG_INFO, "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(LOGG_INFO, "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(LOGG_INFO, "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(LOGG_INFO, "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(LOGG_INFO, "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(LOGG_INFO, "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(LOGG_INFO, "traverse_unlink: Failed to set file disposition to 'DELETE' for '%s'.\n", target);
goto done;
}
if (FALSE == CloseHandle(target_file_handle)) {
logg(LOGG_INFO, "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 = NULL;
char *real_filename = NULL;
int fd = -1;
int copied = 0;
if (NULL == filename) {
goto done;
}
fd = getdest(filename, &nuname);
#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(LOGG_ERROR, "Can't move file %s to %s\n", filename, nuname);
notmoved++;
if (nuname) traverse_unlink(nuname);
} else {
if (copied && (0 != traverse_unlink(filename)))
logg(LOGG_ERROR, "Can't unlink '%s' after copy: %s\n", filename, strerror(errno));
else
logg(LOGG_INFO, "%s: moved to '%s'\n", filename, nuname);
}
done:
if (NULL != real_filename) free(real_filename);
if (fd >= 0) close(fd);
if (NULL != nuname) free(nuname);
return;
}
static void action_copy(const char *filename)
{
char *nuname;
int fd = getdest(filename, &nuname);
if (fd < 0 || filecopy(filename, nuname)) {
logg(LOGG_ERROR, "Can't copy file '%s'\n", filename);
notmoved++;
if (nuname) traverse_unlink(nuname);
} else
logg(LOGG_INFO, "%s: copied to '%s'\n", filename, nuname);
if (fd >= 0) close(fd);
if (nuname) free(nuname);
}
static void action_remove(const char *filename)
{
char *real_filename = NULL;
if (NULL == filename) {
goto done;
}
if (0 != traverse_unlink(filename)) {
logg(LOGG_ERROR, "Can't remove file '%s'\n", filename);
notremoved++;
} else {
logg(LOGG_INFO, "%s: Removed.\n", filename);
}
done:
if (NULL != real_filename) free(real_filename);
return;
}
static int isdir(void)
{
STATBUF sb;
if (CLAMSTAT(actarget, &sb) || !S_ISDIR(sb.st_mode)) {
logg(LOGG_ERROR, "'%s' doesn't exist or is not a directory\n", actarget);
return 0;
}
return 1;
}
/*
* Call this function at the beginning to configure the user preference.
* Later, call the "action" callback function to perform the selection action.
*/
int actsetup(const struct optstruct *opts)
{
int move = optget(opts, "move")->enabled;
if (move || optget(opts, "copy")->enabled) {
#ifndef _WIN32
cl_error_t ret;
#endif
actarget = optget(opts, move ? "move" : "copy")->strarg;
#ifndef _WIN32
ret = cli_realpath((const char *)actarget, &actarget);
if (CL_SUCCESS != ret || NULL == actarget) {
logg(LOGG_INFO, "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);
} else if (optget(opts, "remove")->enabled)
action = action_remove;
return 0;
}

699
clamav/common/cert_util.c Normal file
View File

@ -0,0 +1,699 @@
/*
* OpenSSL certificate caching.
*
* Copyright (C) 2016-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*
* Authors: Russ Kubik
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <string.h>
#include <pthread.h>
#include "cert_util.h"
#include "cert_util_internal.h"
#include "output.h"
static cert_store_t _cert_store = {
.mutex = PTHREAD_MUTEX_INITIALIZER};
static cl_error_t _x509_to_pem(X509 *cert,
char **data,
int *len)
{
cl_error_t ret = CL_EFORMAT;
BIO *out = NULL;
long pem_len = 0;
char *pem_data = NULL;
if (cert == NULL || data == NULL || len == NULL) {
mprintf(LOGG_ERROR, "_x509_to_pem: Invalid argument\n");
goto done;
}
/* Output the certs to a new BIO using the PEM format */
out = BIO_new(BIO_s_mem());
if (!out) {
mprintf(LOGG_ERROR, "BIO_new failed\n");
goto done;
}
PEM_write_bio_X509(out, cert);
(void)BIO_flush(out);
/* Convert the BIO to char* */
pem_len = BIO_get_mem_data(out, &pem_data);
if (pem_len <= 0 || !pem_data) {
mprintf(LOGG_ERROR, "BIO_new: BIO_get_mem_data failed\n");
BIO_free_all(out);
goto done;
}
*data = calloc(1, pem_len + 1);
if (!*data) {
mprintf(LOGG_ERROR, "BIO_new: malloc failed\n");
BIO_free_all(out);
goto done;
}
memcpy(*data, pem_data, pem_len);
(*data)[pem_len] = '\0';
*len = (int)pem_len;
BIO_free_all(out);
ret = CL_SUCCESS;
done:
return ret;
}
/**
* @brief This method will convert a X509 certificate to PEM format and append
* it to a string buffer.
*
* @note If realloc fails to reserve memory for *cert_data it will free whatever
* is currently in *cert_data before returning. total_buf_len is also set
* to 0 (zero) in this case.
*
* @param[in] *ca_cert Pointer to CA certificate
* @param[out] **cert_data Pointer to allocated string buffer
* @param[out] *total_buf_len Total of string buffer length after appending
* CA certificate (ca_cert)
* @param[in,out] *remaining_buf_len Remaining data left allowed in CA certificate
* chain after appending CA certificate
* (ca_cert)
*
* @return 0 on success, -1 on error
*/
static cl_error_t _x509_to_pem_append(X509 *ca_cert,
char **cert_data,
int *total_buf_len,
size_t *remaining_buf_len)
{
char *pem_data = NULL;
char *tmp;
int pem_data_len = 0;
cl_error_t ret = CL_EOPEN;
int current_len = 0;
if (ca_cert == NULL || total_buf_len == NULL ||
remaining_buf_len == NULL || *cert_data == NULL) {
mprintf(LOGG_ERROR, "NULL parameter given\n");
goto done;
}
current_len = *total_buf_len;
if (CL_SUCCESS != _x509_to_pem(ca_cert, &pem_data, &pem_data_len)) {
mprintf(LOGG_ERROR, "Failed to convert x509 certificate to PEM\n");
goto done;
}
if (pem_data_len > (int)*remaining_buf_len) {
tmp = realloc(*cert_data, current_len + pem_data_len + 1);
if (tmp == NULL) {
mprintf(LOGG_ERROR, "Could not realloc enough memory for PEM "
"certificate\n");
free(*cert_data);
*cert_data = NULL;
*total_buf_len = 0;
goto done;
}
*cert_data = tmp;
tmp = NULL;
*remaining_buf_len = 0;
} else {
*remaining_buf_len -= pem_data_len;
}
memcpy(&((*cert_data)[current_len]), pem_data, pem_data_len);
*total_buf_len = current_len + pem_data_len;
(*cert_data)[*total_buf_len] = '\0';
ret = CL_SUCCESS;
done:
free(pem_data);
pem_data = NULL;
return ret;
}
cert_store_t *cert_store_get_int(void)
{
return &_cert_store;
}
void cert_store_unload_int(void)
{
if (_cert_store.loaded) {
cert_store_free_cert_list_int(&_cert_store.system_certs);
cert_store_free_cert_list_int(&_cert_store.trusted_certs);
_cert_store.loaded = false;
}
}
void cert_store_free_cert_list_int(cert_list_t *cert_list)
{
size_t i;
if (cert_list && cert_list->certificates) {
for (i = 0; i < cert_list->count; ++i) {
X509_free(cert_list->certificates[i]);
cert_list->certificates[i] = NULL;
}
free(cert_list->certificates);
cert_list->certificates = NULL;
cert_list->count = 0L;
}
}
void cert_store_unload(void)
{
int pt_err;
pt_err = pthread_mutex_lock(&_cert_store.mutex);
if (pt_err) {
errno = pt_err;
mprintf(LOGG_ERROR, "Mutex lock failed\n");
}
cert_store_unload_int();
pt_err = pthread_mutex_unlock(&_cert_store.mutex);
if (pt_err) {
errno = pt_err;
mprintf(LOGG_ERROR, "Mutex unlock failed\n");
}
}
#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0+ */
static cl_error_t x509_cert_name_cmp(X509 *cert_a, X509 *cert_b, int *cmp_out)
{
cl_error_t status = CL_EMEM;
X509_NAME *a = NULL;
X509_NAME *b = NULL;
BIO *bio_out_a = NULL;
BIO *bio_out_b = NULL;
BUF_MEM *biomem_a;
BUF_MEM *biomem_b;
bio_out_a = BIO_new(BIO_s_mem());
if (!bio_out_a)
goto done;
bio_out_b = BIO_new(BIO_s_mem());
if (!bio_out_b)
goto done;
a = X509_get_subject_name(cert_a);
if (-1 == X509_NAME_print_ex(bio_out_a, a, 0, XN_FLAG_SEP_SPLUS_SPC)) {
mprintf(LOGG_ERROR, "Failed to print x509 certificate name!\n");
goto done;
}
BIO_get_mem_ptr(bio_out_a, &biomem_a);
b = X509_get_subject_name(cert_b);
if (-1 == X509_NAME_print_ex(bio_out_b, b, 0, XN_FLAG_SEP_SPLUS_SPC)) {
mprintf(LOGG_ERROR, "Failed to print x509 certificate name!\n");
goto done;
}
BIO_get_mem_ptr(bio_out_b, &biomem_b);
*cmp_out = strncmp(biomem_a->data, biomem_b->data, MIN(biomem_a->length, biomem_b->length));
status = CL_SUCCESS;
done:
if (NULL != bio_out_a)
BIO_free(bio_out_a);
if (NULL != bio_out_b)
BIO_free(bio_out_b);
return status;
}
cl_error_t x509_get_cert_name(X509 *cert, char **name)
{
cl_error_t status = CL_EMEM;
X509_NAME *a = NULL;
BIO *bio_out = NULL;
BUF_MEM *biomem;
char *cert_name = NULL;
if (NULL == cert || NULL == name) {
status = CL_EARG;
goto done;
}
*name = NULL;
bio_out = BIO_new(BIO_s_mem());
if (!bio_out)
goto done;
a = X509_get_subject_name(cert);
if (-1 == X509_NAME_print_ex(bio_out, a, 0, XN_FLAG_SEP_SPLUS_SPC)) {
mprintf(LOGG_ERROR, "Failed to print x509 certificate name!\n");
goto done;
}
BIO_get_mem_ptr(bio_out, &biomem);
cert_name = malloc(biomem->length + 1);
if (!cert_name) {
mprintf(LOGG_ERROR, "Failed to allocate memory for certificate name biomem structure!\n");
goto done;
}
memcpy(cert_name, biomem->data, biomem->length);
cert_name[biomem->length] = '\0';
*name = cert_name;
status = CL_SUCCESS;
done:
if (NULL != bio_out)
BIO_free(bio_out);
return status;
}
#endif
cl_error_t cert_store_export_pem(char **cert_data,
int *cert_data_len,
X509 *additional_ca_cert)
{
const uint32_t STARTING_RAW_PEM_LENGTH = 350 * 1024;
uint32_t i;
cl_error_t ret = CL_EOPEN;
bool locked = false;
int pt_err;
size_t remaining_buf_len = STARTING_RAW_PEM_LENGTH;
bool add_additional_ca_cert = true;
if ((cert_data == NULL) || (cert_data_len == NULL)) {
mprintf(LOGG_ERROR, "One or more arguments are NULL\n");
goto done;
}
*cert_data = calloc(1, STARTING_RAW_PEM_LENGTH + 1);
if (*cert_data == NULL) {
mprintf(LOGG_ERROR, "Could not allocate memory for PEM certs\n");
goto done;
}
*cert_data_len = 0;
pt_err = pthread_mutex_lock(&_cert_store.mutex);
if (pt_err) {
errno = pt_err;
mprintf(LOGG_ERROR, "Mutex lock failed\n");
}
locked = true;
if (!_cert_store.loaded) {
goto done;
}
/* Load system root ca certs into list */
for (i = 0; i < _cert_store.system_certs.count; ++i) {
if (_x509_to_pem_append(_cert_store.system_certs.certificates[i],
cert_data,
cert_data_len,
&remaining_buf_len) != 0) {
goto done;
}
/*
* Two certs by the same name can cause conflicts. Trust the
* one in the OS certificate/key store if the additional CA
* name matches that of one in the store.
*/
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
/* OpenSSL >= 1.1.0 */
if (additional_ca_cert) {
int cmp = 0;
if (CL_SUCCESS == x509_cert_name_cmp(_cert_store.system_certs.certificates[i],
additional_ca_cert,
&cmp)) {
if (0 == cmp)
add_additional_ca_cert = false;
}
}
#else
/* OpenSSL <= 1.0.2 */
if (additional_ca_cert && additional_ca_cert->cert_info &&
(strcmp(_cert_store.system_certs.certificates[i]->name,
additional_ca_cert->name) == 0)) {
add_additional_ca_cert = false;
}
#endif
}
/* Load trusted ca certs into list */
for (i = 0; i < _cert_store.trusted_certs.count; ++i) {
if (_x509_to_pem_append(_cert_store.trusted_certs.certificates[i],
cert_data,
cert_data_len,
&remaining_buf_len) != 0) {
goto done;
}
/*
* Two certs by the same name can cause conflicts. Trust the
* one in the OS certificate/key store if the additional CA
* name matches that of one in the store.
*/
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
/* OpenSSL >= 1.1.0 */
if (additional_ca_cert) {
int cmp = 0;
if (CL_SUCCESS == x509_cert_name_cmp(_cert_store.trusted_certs.certificates[i],
additional_ca_cert,
&cmp)) {
if (0 == cmp)
add_additional_ca_cert = false;
}
}
#else
/* OpenSSL <= 1.0.2 */
if (additional_ca_cert && additional_ca_cert->cert_info &&
(strcmp(_cert_store.trusted_certs.certificates[i]->name,
additional_ca_cert->name) == 0)) {
add_additional_ca_cert = false;
}
#endif
}
/* End with the additional CA certificate if provided */
if (additional_ca_cert && add_additional_ca_cert && *cert_data) {
/* Return an error only if we were unable to allocate memory */
if (_x509_to_pem_append(additional_ca_cert,
cert_data,
cert_data_len,
&remaining_buf_len) != 0) {
goto done;
}
}
ret = CL_SUCCESS;
done:
if (locked) {
pt_err = pthread_mutex_unlock(&_cert_store.mutex);
if (pt_err) {
errno = pt_err;
mprintf(LOGG_ERROR, "Mutex unlock failed\n");
}
locked = false;
}
if (ret != CL_SUCCESS && cert_data && *cert_data) {
free(*cert_data);
*cert_data = NULL;
}
return ret;
}
cl_error_t cert_store_set_trusted_int(X509 **trusted_certs, size_t trusted_cert_count)
{
cl_error_t ret = CL_EOPEN;
size_t i, j;
cert_list_t tmp_trusted = {0};
do {
if ((trusted_certs == NULL) || (trusted_cert_count == 0)) {
mprintf(LOGG_ERROR, "Empty trusted certificate list\n");
break;
}
tmp_trusted.certificates = calloc(trusted_cert_count,
sizeof(*tmp_trusted.certificates));
if (!tmp_trusted.certificates) {
mprintf(LOGG_ERROR, "Failed to reserve memory for trusted certs\n");
break;
}
for (i = 0; i < trusted_cert_count; ++i) {
bool found = false;
/* Check if certificate already exists in system root cert list */
for (j = 0; j < _cert_store.system_certs.count; ++j) {
if (X509_cmp(trusted_certs[i],
_cert_store.system_certs.certificates[j]) == 0) {
found = true;
}
}
if (found) {
continue; /* certificate is already found in cert store */
}
tmp_trusted.certificates[tmp_trusted.count] =
X509_dup(trusted_certs[i]);
if (!tmp_trusted.certificates[tmp_trusted.count]) {
mprintf(LOGG_ERROR, "X509_dup failed at index: %zu\n", i);
continue; /* continue on error */
}
tmp_trusted.count++;
}
cert_store_free_cert_list_int(&_cert_store.trusted_certs);
_cert_store.trusted_certs.certificates = tmp_trusted.certificates;
_cert_store.trusted_certs.count = tmp_trusted.count;
tmp_trusted.certificates = NULL;
tmp_trusted.count = 0;
ret = CL_SUCCESS;
} while (0);
return ret;
}
cl_error_t cert_store_set_trusted(X509 **trusted_certs, size_t trusted_cert_count)
{
cl_error_t ret = CL_EOPEN;
int pt_err;
pt_err = pthread_mutex_lock(&_cert_store.mutex);
if (pt_err) {
errno = pt_err;
mprintf(LOGG_ERROR, "Mutex lock failed\n");
}
if (_cert_store.loaded) {
ret = cert_store_set_trusted_int(trusted_certs, trusted_cert_count);
}
pt_err = pthread_mutex_unlock(&_cert_store.mutex);
if (pt_err) {
errno = pt_err;
mprintf(LOGG_ERROR, "Mutex unlock failed\n");
}
return ret;
}
size_t cert_store_remove_trusted(void)
{
size_t count = 0;
int pt_err;
pt_err = pthread_mutex_lock(&_cert_store.mutex);
if (pt_err) {
errno = pt_err;
mprintf(LOGG_ERROR, "Mutex lock failed\n");
}
if (_cert_store.loaded) {
count = _cert_store.trusted_certs.count;
cert_store_free_cert_list_int(&_cert_store.trusted_certs);
}
pt_err = pthread_mutex_unlock(&_cert_store.mutex);
if (pt_err) {
errno = pt_err;
mprintf(LOGG_ERROR, "Mutex unlock failed\n");
}
return count;
}
void cert_fill_X509_store(X509_STORE *store, X509 **certs, size_t cert_count)
{
size_t i;
unsigned long err;
if (store && certs && cert_count > 0) {
for (i = 0; i < cert_count; ++i) {
if (!certs[i]) {
mprintf(LOGG_ERROR, "NULL cert at index %zu in X509 cert list; skipping\n", i);
continue;
}
if (X509_STORE_add_cert(store, certs[i]) != 1) {
char *name = NULL;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
x509_get_cert_name(certs[i], &name);
#else
name = certs[i]->name;
#endif
err = ERR_get_error();
if (X509_R_CERT_ALREADY_IN_HASH_TABLE == ERR_GET_REASON(err)) {
mprintf(LOGG_DEBUG, "Certificate skipped; already exists in store: %s\n",
(name ? name : ""));
} else {
mprintf(LOGG_ERROR, "Failed to add certificate to store: %s (%lu) [%s]\n",
ERR_error_string(err, NULL), err,
(name ? name : ""));
}
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
if (NULL != name) {
free(name);
name = NULL;
}
#endif
}
}
}
}
void cert_store_export_certs(X509_STORE *store, X509 *additional_ca_cert)
{
cert_store_t *cert_store = NULL;
int pt_err;
do {
if (!store) {
mprintf(LOGG_ERROR, "NULL X509 store\n");
break;
}
cert_store = cert_store_get_int();
if (!cert_store) {
mprintf(LOGG_ERROR, "Failed to retrieve cert store\n");
break;
}
pt_err = pthread_mutex_lock(&cert_store->mutex);
if (pt_err) {
errno = pt_err;
mprintf(LOGG_ERROR, "Mutex lock failed\n");
}
if (!cert_store->loaded) {
mprintf(LOGG_ERROR, "Cert store not loaded\n");
break;
}
/* On Linux, system certificates are loaded by OpenSSL */
#if defined(_WIN32) || defined(DARWIN)
cert_fill_X509_store(store,
cert_store->system_certs.certificates,
cert_store->system_certs.count);
#endif
cert_fill_X509_store(store,
cert_store->trusted_certs.certificates,
cert_store->trusted_certs.count);
/* Adding the additional CA cert to the trustchain */
if ((additional_ca_cert != NULL) &&
(X509_STORE_add_cert(store, additional_ca_cert) != 1)) {
char *name = NULL;
unsigned long err = ERR_get_error();
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
x509_get_cert_name(additional_ca_cert, &name);
#else
name = additional_ca_cert->name;
#endif
if (X509_R_CERT_ALREADY_IN_HASH_TABLE == ERR_GET_REASON(err)) {
mprintf(LOGG_INFO, "Certificate is already in trust [%s]\n",
(name ? name : ""));
} else {
mprintf(LOGG_ERROR, "Failed to add CA certificate for the SSL context. "
"Error: %d [%s]\n",
ERR_GET_REASON(err),
(name ? name : ""));
}
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
if (NULL != name) {
free(name);
name = NULL;
}
#endif
}
} while (0);
if (cert_store) {
pt_err = pthread_mutex_unlock(&cert_store->mutex);
if (pt_err) {
errno = pt_err;
mprintf(LOGG_ERROR, "Mutex unlock failed\n");
}
}
}
CURLcode sslctx_function(CURL *curl, void *ssl_ctx, void *userptr)
{
CURLcode status = CURLE_BAD_FUNCTION_ARGUMENT;
cert_store_t *cert_store = NULL;
UNUSEDPARAM(curl);
UNUSEDPARAM(userptr);
cert_store = cert_store_get_int();
if (!cert_store) {
mprintf(LOGG_ERROR, "Failed to retrieve cert store\n");
goto done;
}
if (!cert_store->loaded) {
if (CL_SUCCESS != cert_store_load(NULL, 0)) {
mprintf(LOGG_ERROR, "Failed to load cert store\n");
goto done;
}
}
X509_STORE *store = SSL_CTX_get_cert_store((SSL_CTX *)ssl_ctx);
cert_store_export_certs(store, NULL);
status = CURLE_OK;
done:
return status;
}

483
clamav/common/clamdcom.c Normal file
View File

@ -0,0 +1,483 @@
/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2009-2013 Sourcefire, Inc.
*
* Author: aCaB
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <fcntl.h>
#include <errno.h>
#ifndef _WIN32
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netdb.h>
#endif
#include "clamav.h"
#include "actions.h"
#include "output.h"
#include "clamdcom.h"
#ifndef _WIN32
struct sockaddr_un nixsock;
#endif
static const char *scancmd[] = {"CONTSCAN", "MULTISCAN", "INSTREAM", "FILDES", "ALLMATCHSCAN"};
/* Sends bytes over a socket
* Returns 0 on success */
int sendln(int sockd, const char *line, unsigned int len)
{
while (len) {
int sent = send(sockd, line, len, 0);
if (sent <= 0) {
if (sent && errno == EINTR) continue;
logg(LOGG_ERROR, "Can't send to clamd: %s\n", strerror(errno));
return 1;
}
line += sent;
len -= sent;
}
return 0;
}
/* Inits a RECVLN struct before it can be used in recvln() - see below */
void recvlninit(struct RCVLN *s, int sockd)
{
s->sockd = sockd;
s->bol = s->cur = s->buf;
s->r = 0;
}
/* Receives a full (terminated with \0) line from a socket
* Sets rbol to the begin of the received line, and optionally
* reol to the end of line.
* Should be called repeatedly until all input is consumed
* Returns:
* - the length of the line (a positive number) on success
* - 0 if the connection is closed
* - -1 on error
*/
int recvln(struct RCVLN *s, char **rbol, char **reol)
{
char *eol;
while (1) {
if (!s->r) {
s->r = recv(s->sockd, s->cur, sizeof(s->buf) - (s->cur - s->buf), 0);
if (s->r <= 0) {
if (s->r && errno == EINTR) {
s->r = 0;
continue;
}
if (s->r || s->cur != s->buf) {
*s->cur = '\0';
if (strcmp(s->buf, "UNKNOWN COMMAND\n"))
logg(LOGG_ERROR, "Communication error\n");
else
logg(LOGG_ERROR, "Command rejected by clamd (wrong clamd version?)\n");
return -1;
}
return 0;
}
}
if ((eol = memchr(s->cur, 0, s->r))) {
int ret = 0;
eol++;
s->r -= eol - s->cur;
*rbol = s->bol;
if (reol) *reol = eol;
ret = eol - s->bol;
if (s->r)
s->bol = s->cur = eol;
else
s->bol = s->cur = s->buf;
return ret;
}
s->r += s->cur - s->bol;
if (!eol && s->r == sizeof(s->buf)) {
logg(LOGG_ERROR, "Overlong reply from clamd\n");
return -1;
}
if (!eol) {
if (s->buf != s->bol) { /* old memmove sux */
memmove(s->buf, s->bol, s->r);
s->bol = s->buf;
}
s->cur = &s->bol[s->r];
s->r = 0;
}
}
}
/* Determines if a path should be excluded
* 0: scan, 1: skip */
int chkpath(const char *path, struct optstruct *clamdopts)
{
int status = 0;
const struct optstruct *opt;
char *real_path = NULL;
if (!path) {
status = 1;
goto done;
}
if ((opt = optget(clamdopts, "ExcludePath"))->enabled) {
while (opt) {
if (match_regex(path, opt->strarg) == 1) {
logg(LOGG_DEBUG, "%s: Excluded\n", path);
status = 1;
goto done;
}
opt = opt->nextarg;
}
}
done:
if (NULL != real_path) {
free(real_path);
}
return status;
}
#ifdef HAVE_FD_PASSING
/* Issues a FILDES command and pass a FD to clamd
* Returns >0 on success, 0 soft fail, -1 hard fail */
int send_fdpass(int sockd, const char *filename)
{
struct iovec iov[1];
struct msghdr msg;
struct cmsghdr *cmsg;
unsigned char fdbuf[CMSG_SPACE(sizeof(int))];
char dummy[] = "";
int fd;
const char zFILDES[] = "zFILDES";
if (filename) {
if ((fd = open(filename, O_RDONLY)) < 0) {
logg(LOGG_INFO, "%s: Failed to open file\n", filename);
return 0;
}
} else
fd = 0;
if (sendln(sockd, zFILDES, sizeof(zFILDES))) {
close(fd);
return -1;
}
iov[0].iov_base = dummy;
iov[0].iov_len = 1;
memset(&msg, 0, sizeof(msg));
msg.msg_control = fdbuf;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_controllen = CMSG_LEN(sizeof(int));
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
*(int *)CMSG_DATA(cmsg) = fd;
if (sendmsg(sockd, &msg, 0) == -1) {
logg(LOGG_ERROR, "FD send failed: %s\n", strerror(errno));
close(fd);
return -1;
}
close(fd);
return 1;
}
#endif
/* Issues an INSTREAM command to clamd and streams the given file
* Returns >0 on success, 0 soft fail, -1 hard fail */
int send_stream(int sockd, const char *filename, struct optstruct *clamdopts)
{
uint32_t buf[BUFSIZ / sizeof(uint32_t)];
int fd, len;
unsigned long int todo = optget(clamdopts, "StreamMaxLength")->numarg;
const char zINSTREAM[] = "zINSTREAM";
if (filename) {
if ((fd = safe_open(filename, O_RDONLY | O_BINARY)) < 0) {
logg(LOGG_INFO, "%s: Failed to open file. ERROR\n", filename);
return 0;
}
} else {
/* Read stream from STDIN */
fd = 0;
}
if (sendln(sockd, zINSTREAM, sizeof(zINSTREAM))) {
close(fd);
return -1;
}
while ((len = read(fd, &buf[1], sizeof(buf) - sizeof(uint32_t))) > 0) {
if ((unsigned int)len > todo) len = todo;
buf[0] = htonl(len);
if (sendln(sockd, (const char *)buf, len + sizeof(uint32_t))) {
close(fd);
return -1;
}
todo -= len;
if (!todo) {
len = 0;
break;
}
}
close(fd);
if (len) {
logg(LOGG_ERROR, "Failed to read from %s.\n", filename ? filename : "STDIN");
return 0;
}
*buf = 0;
sendln(sockd, (const char *)buf, 4);
return 1;
}
/* Connects to clamd
* Returns a FD or -1 on error */
int dconnect(struct optstruct *clamdopts)
{
int sockd, res;
const struct optstruct *opt;
struct addrinfo hints, *info, *p;
char port[10];
char *ipaddr;
#ifndef _WIN32
opt = optget(clamdopts, "LocalSocket");
if (opt->enabled) {
if ((sockd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) {
if (connect(sockd, (struct sockaddr *)&nixsock, sizeof(nixsock)) == 0)
return sockd;
else {
logg(LOGG_ERROR, "Could not connect to clamd on LocalSocket %s: %s\n", opt->strarg, strerror(errno));
close(sockd);
}
}
}
#endif
snprintf(port, sizeof(port), "%lld", optget(clamdopts, "TCPSocket")->numarg);
opt = optget(clamdopts, "TCPAddr");
while (opt) {
if (opt->enabled) {
ipaddr = NULL;
if (opt->strarg)
ipaddr = (!strcmp(opt->strarg, "any") ? NULL : opt->strarg);
memset(&hints, 0x00, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((res = getaddrinfo(ipaddr, port, &hints, &info))) {
logg(LOGG_ERROR, "Could not lookup %s: %s\n", ipaddr ? ipaddr : "", gai_strerror(res));
opt = opt->nextarg;
continue;
}
for (p = info; p != NULL; p = p->ai_next) {
if ((sockd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) {
logg(LOGG_ERROR, "Can't create the socket: %s\n", strerror(errno));
continue;
}
if (connect(sockd, p->ai_addr, p->ai_addrlen) < 0) {
logg(LOGG_ERROR, "Could not connect to clamd on %s: %s\n", opt->strarg, strerror(errno));
closesocket(sockd);
continue;
}
freeaddrinfo(info);
return sockd;
}
freeaddrinfo(info);
}
opt = opt->nextarg;
}
return -1;
}
/* Sends a proper scan request to clamd and parses its replies
* This is used only in non IDSESSION mode
* Returns the number of infected files or -1 on error
* NOTE: filename may be NULL for STREAM scantype. */
int dsresult(int sockd, int scantype, const char *filename, int *printok, int *errors, struct optstruct *clamdopts)
{
int infected = 0, len = 0, beenthere = 0;
char *bol, *eol;
struct RCVLN rcv;
STATBUF sb;
if (filename) {
if (1 == chkpath(filename, clamdopts)) {
goto done;
}
}
recvlninit(&rcv, sockd);
switch (scantype) {
case MULTI:
case CONT:
case ALLMATCH:
if (!filename) {
logg(LOGG_INFO, "Filename cannot be NULL for MULTISCAN or CONTSCAN.\n");
infected = -1;
goto done;
}
len = strlen(filename) + strlen(scancmd[scantype]) + 3;
if (!(bol = malloc(len))) {
logg(LOGG_ERROR, "Cannot allocate a command buffer: %s\n", strerror(errno));
infected = -1;
goto done;
}
sprintf(bol, "z%s %s", scancmd[scantype], filename);
if (sendln(sockd, bol, len)) {
free(bol);
infected = -1;
goto done;
}
free(bol);
break;
case STREAM:
/* NULL filename safe in send_stream() */
len = send_stream(sockd, filename, clamdopts);
break;
#ifdef HAVE_FD_PASSING
case FILDES:
/* NULL filename safe in send_fdpass() */
len = send_fdpass(sockd, filename);
break;
#endif
}
if (len <= 0) {
if (printok)
*printok = 0;
if (errors)
(*errors)++;
infected = len;
goto done;
}
while ((len = recvln(&rcv, &bol, &eol))) {
if (len == -1) {
infected = -1;
goto done;
}
beenthere = 1;
if (!filename) logg(LOGG_INFO, "%s\n", bol);
if (len > 7) {
char *colon = strrchr(bol, ':');
if (colon && colon[1] != ' ') {
char *br;
*colon = 0;
br = strrchr(bol, '(');
if (br)
*br = 0;
colon = strrchr(bol, ':');
}
if (!colon) {
char *unkco = "UNKNOWN COMMAND";
if (!strncmp(bol, unkco, sizeof(unkco) - 1))
logg(LOGG_INFO, "clamd replied \"UNKNOWN COMMAND\". Command was %s\n",
(scantype < 0 || scantype > MAX_SCANTYPE) ? "unidentified" : scancmd[scantype]);
else
logg(LOGG_INFO, "Failed to parse reply: \"%s\"\n", bol);
infected = -1;
goto done;
} else if (!memcmp(eol - 7, " FOUND", 6)) {
static char last_filename[PATH_MAX + 1] = {'\0'};
*(eol - 7) = 0;
if (printok)
*printok = 0;
if (scantype != ALLMATCH) {
infected++;
} else {
if (filename != NULL && strcmp(filename, last_filename)) {
infected++;
strncpy(last_filename, filename, PATH_MAX);
last_filename[PATH_MAX] = '\0';
}
}
if (filename) {
if (scantype >= STREAM) {
logg(LOGG_INFO, "%s%s FOUND\n", filename, colon);
if (action) action(filename);
} else {
logg(LOGG_INFO, "%s FOUND\n", bol);
*colon = '\0';
if (action)
action(bol);
}
}
} else if (!memcmp(eol - 7, " ERROR", 6)) {
if (errors)
(*errors)++;
if (printok)
*printok = 0;
if (filename) {
if (scantype >= STREAM)
logg(LOGG_INFO, "%s%s\n", filename, colon);
else
logg(LOGG_INFO, "%s\n", bol);
}
}
}
}
if (!beenthere) {
if (!filename) {
logg(LOGG_INFO, "STDIN: noreply from clamd\n.");
infected = -1;
goto done;
}
if (CLAMSTAT(filename, &sb) == -1) {
logg(LOGG_INFO, "%s: stat() failed with %s, clamd may not be responding\n",
filename, strerror(errno));
infected = -1;
goto done;
}
if (!S_ISDIR(sb.st_mode)) {
logg(LOGG_INFO, "%s: no reply from clamd\n", filename);
infected = -1;
goto done;
}
}
done:
return infected;
}

67
clamav/common/clamdcom.h Normal file
View File

@ -0,0 +1,67 @@
/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2009-2013 Sourcefire, Inc.
*
* Author: aCaB
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#ifndef __CLAMDCOM_H
#define __CLAMDCOM_H
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#include "misc.h"
enum {
CONT,
MULTI,
STREAM,
FILDES,
ALLMATCH,
MAX_SCANTYPE = ALLMATCH
};
struct RCVLN {
char buf[PATH_MAX + 1024]; /* FIXME must match that in clamd - bb1349 */
int sockd;
int r;
char *cur;
char *bol;
};
#ifndef _WIN32
extern struct sockaddr_un nixsock;
#endif
int sendln(int sockd, const char *line, unsigned int len);
void recvlninit(struct RCVLN *s, int sockd);
int recvln(struct RCVLN *s, char **rbol, char **reol);
int chkpath(const char *path, struct optstruct *clamdopts);
#ifdef HAVE_FD_PASSING
int send_fdpass(int sockd, const char *filename);
#endif
int send_stream(int sockd, const char *filename, struct optstruct *clamdopts);
int dconnect(struct optstruct *clamdopts);
int dsresult(int sockd, int scantype, const char *filename, int *printok, int *errors, struct optstruct *clamdopts);
#endif

330
clamav/common/exeScanner.c Normal file
View File

@ -0,0 +1,330 @@
/*
* Copyright (C) 2021-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2005-2010 Gianluigi Tiesi <sherpya@netfarm.it>
*
* Authors: Gianluigi Tiesi
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include "exescanner.h"
/* -1 = wildchar - -2 = stop here */
sigs_t signatures[] = {
{{0x60, 0xbe, -1, -1, -1, -1, 0x8d, 0xbe, -1, -1, -1, 0xff, 0x57, -2},
"UPX",
.0f},
{{0x94, 0xbc, 0x5d, 0x07, 0x42, 0x00, 0xb9, 0x1d, 0x00, 0x00, 0x00, 0x80,
0x34, 0x0c, 0x44, 0xe2},
"UPXSHiT",
.0f},
{{0xbe, 0xa4, 0x01, 0x40, 0x00, 0xad, 0x93, 0xad, 0x97, 0xad, 0x56, 0x96,
0xb2, 0x80, 0xa4, 0xb6},
"FSG 1.33",
.0f},
{{0x4d, 0x5a, -1, -1, -1, -1, -1, -1, -1, -1, 0x00, 0x00, 0x50, 0x45, 0x00,
0x00},
"FSG 2.00",
.0f},
{{0x4d, 0x5a, 0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c, 0x33, 0x32, 0x2e, 0x44,
0x4c, 0x4c, 0x00, 0x00},
"WinUpack 0.39",
.0f},
{{0xbe, 0x88, 0x01, 0x40, 0x00, 0xad, 0x8b, 0xf8, 0x95, 0xad, 0x91, 0xf3,
0xa5, 0xad, 0xb5, 0x1c},
"Upack 2.4/2.9",
.0f},
{{0xbe, 0x48, 0x01, 0x40, 0x00, 0xad, 0x8b, 0xf8, 0x95, 0xa5, 0x33, 0xc0,
0x33, 0xc9, 0xab, 0x48},
"Upack 1.1/1.2",
.0f},
{{0x83, 0xec, 0x20, 0x53, 0x55, 0x56, 0x33, 0xdb, 0x57, 0x89, 0x5c, 0x24,
0x18, 0xc7, 0x44, 0x24},
"NullSoft PiMP",
.0f},
{{0xe9, -1, -1, -1, 0xff, 0x0c, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00},
"Mew 11 1.2",
.0f},
{{0x60, 0xe9, 0x3d, 0x04, 0x00, 0x00, -2}, "ASPack 2.11", .0f},
{{0x60, 0xe8, 0x03, 0x00, 0x00, 0x00, 0xe9, 0xeb, 0x04, 0x5d, 0x45, 0x55,
0xc3, 0xe8, 0x01, 0x00},
"ASPack 2.12",
.0f},
{{0x55, 0x83, 0xc4, 0x04, 0x76, 0x08, 0x7a, 0x06, 0x74, 0x04, 0x66, 0x83,
0xea, 0x00, 0xf5, 0x50},
"Morphine 1.4/2.7",
.0f},
{{0x56, 0x72, 0x05, 0x05, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x0b, 0xd2, 0xf9,
0x84, 0xdb, 0x68, 0x34},
"Morphine 1.4/2.7 [2]",
.0f},
{{0x53, 0x51, 0x52, 0x56, 0x57, 0x55, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x5d,
0x8b, 0xd5, 0x81, 0xed},
"PEDiminisher 0.1",
.0f},
{{0xe8, 0xf6, 0x03, 0x00, 0x00, 0xe9, 0x9e, 0xfd, 0xff, 0xff, 0xcc, 0xcc,
0xcc, 0xcc, 0xcc, 0xcc},
"MSVC8 Release",
-1.0f},
{{0xe9, -1, -1, -1, 0x00, 0xe9, -1, -1, -1, 0x00, 0xe9, -1, -1, -1, 0x00,
0xe9},
"MSVC8 Debug",
-1.0f},
{{0xe8, -1, -1, 0x00, 0x00, 0xe9, 0x16, 0xfe, 0xff, 0xff, -2},
"MSVC6 Release",
-2.0f},
{{0xe9, 0x96, 0xee, 0x0e, 0x00, 0xb8, 0x6c, 0x02, 0x58, 0x00, 0xe8, 0xae,
0xe4, 0x0e, 0x00, 0x83},
"MSVC6 Release (2)",
-1.0f},
{{0x55, 0x8b, 0xec, 0x6a, 0xff, 0x68, 0xb0, 0x41, 0x40, 0x00, 0x68, 0x10,
0x36, 0x40, 0x00, 0x64},
"MSVC6 Release (3)",
-1.0f},
{{0x55, 0x8b, 0xec, 0x53, 0x8b, 0x5d, 0x08, 0x56, 0x8b, 0x75, 0x0c, 0x57,
0x8b, 0x7d, 0x10, 0x85},
"MSVC6 Release (4)",
-1.0f},
{{0x83, 0x7c, 0x24, 0x08, 0x01, 0x75, 0x05, 0xe8, -1, -1, 0x00, 0x00, 0xff,
0x74, 0x24, 0x04},
"MSVC6 Release DLL",
-1.0f},
{{0xff, 0x25, -1, -1, -1, -1, 0xcc, 0xcc, 0x03, 0x30, 0x01, 0x00, 0x07,
0x00, 0x00, 0x00},
"DotNet",
-1.0f},
{{0x55, 0x89, 0xe5, -2}, "MinGW", -1.0f},
{{0}, 0, 0}};
int sigcmp(const uint8_t *data, const int16_t *sig, size_t n)
{
uint8_t *d = (uint8_t *)data;
int16_t *s = (int16_t *)sig;
while (n-- != 0) {
if (*s == -2)
return 0;
if ((*s != -1) && (*d != *s))
return (*d < *s) ? -1 : +1;
d++;
s++;
}
return 0;
}
sigs_t *checksig(uint8_t *data)
{
int i = 0;
while (signatures[i].name) {
if (!sigcmp(data, signatures[i].sig, 16))
return &signatures[i];
i++;
}
return NULL;
}
double calc_entropy(const unsigned char *data, size_t size)
{
double entropy = .0f;
size_t p[256];
size_t c, i;
memset(p, 0, sizeof(p));
for (c = 0; c < size; c++)
p[data[c]]++;
for (i = 0; i < 256; i++)
if (p[i])
entropy -= ((double)p[i] / size) * log((double)p[i] / size);
return entropy;
}
#define FILLBYTES(dst) \
if (IsBadReadPtr(seek, sizeof(dst))) { \
logg(LOGG_ERROR, "exeScanner: Bad pointer!!!\n"); \
goto cleanup; \
} \
memcpy(&dst, seek, sizeof(dst));
/* Packed exe heuristic detection, errors are handled as like of non packed data
*/
int is_packed(const char *filename)
{
int packed = 0;
int i = 0, c = 0;
int badsection = 0;
double entropy = 0.0;
sigs_t *sig = NULL;
uint16_t e_mz;
uint32_t e_lfanew, e_magic;
uint32_t epoff = 0;
unsigned char *seek = NULL, *s_start = NULL, *ep = NULL, *lpMapAddress = NULL;
PIMAGE_FILE_HEADER pehdr;
PIMAGE_OPTIONAL_HEADER32 opthdr;
PIMAGE_SECTION_HEADER sechdr;
char secname[IMAGE_SIZEOF_SHORT_NAME];
HANDLE hFile = INVALID_HANDLE_VALUE, hMapFile = NULL;
hFile = CreateFileA(filename, GENERIC_READ, 0, NULL, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
elogg(LOGG_INFO, "exeScanner: CreateFileA failed %lu\n", GetLastError());
return packed; /* Returning packed, the module is loaded so it must exists
on disk */
}
hMapFile = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, "exeScanner");
if (!hMapFile) {
elogg(LOGG_INFO, "exeScanner: CreateFileMappingA() failed %lu\n", GetLastError());
goto cleanup;
}
lpMapAddress = (LPBYTE)MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0);
if (!lpMapAddress) {
elogg(LOGG_INFO, "exeScanner: MapViewOfFile() failed %lu\n", GetLastError());
goto cleanup;
}
seek = lpMapAddress;
/* DOS Signature 'MZ' */
FILLBYTES(e_mz);
if (e_mz != IMAGE_DOS_SIGNATURE) {
elogg(LOGG_INFO, "exeScanner: DOS Signature not found\n");
goto cleanup;
}
seek += 0x3c;
FILLBYTES(e_lfanew);
if (!e_lfanew) {
elogg(LOGG_INFO, "exeScanner: Invalid PE offset\n");
goto cleanup;
}
seek = lpMapAddress + e_lfanew;
/* PE Signature 'PE' */
FILLBYTES(e_magic);
if (e_magic != IMAGE_NT_SIGNATURE) {
elogg(LOGG_INFO, "exeScanner: PE Signature not found\n");
goto cleanup;
}
seek += sizeof(e_magic);
if (IsBadReadPtr(seek, sizeof(IMAGE_FILE_HEADER)))
goto cleanup;
pehdr = (PIMAGE_FILE_HEADER)seek;
seek += sizeof(IMAGE_FILE_HEADER);
if (IsBadReadPtr(seek, sizeof(IMAGE_OPTIONAL_HEADER32)))
goto cleanup;
opthdr = (PIMAGE_OPTIONAL_HEADER32)seek;
seek += sizeof(IMAGE_OPTIONAL_HEADER32);
if (pehdr->Machine != IMAGE_FILE_MACHINE_I386) {
elogg(LOGG_INFO, "exeScanner: Not an x86 executable\n");
goto cleanup;
}
/* Invalid sections number */
if ((pehdr->NumberOfSections < 1) || (pehdr->NumberOfSections > 32)) {
elogg(LOGG_INFO, "exeScanner: Invalid sections number\n");
packed = 1;
goto cleanup;
}
for (i = 0; i < pehdr->NumberOfSections; i++) {
double section_entropy = .0f;
if (IsBadReadPtr(seek, sizeof(IMAGE_SECTION_HEADER)))
goto cleanup;
sechdr = (PIMAGE_SECTION_HEADER)seek;
seek += sizeof(IMAGE_SECTION_HEADER);
if (opthdr->AddressOfEntryPoint >= sechdr->VirtualAddress)
epoff = opthdr->AddressOfEntryPoint - sechdr->VirtualAddress +
sechdr->PointerToRawData;
s_start = lpMapAddress + sechdr->PointerToRawData;
if (!IsBadReadPtr(s_start, sechdr->SizeOfRawData))
section_entropy = calc_entropy(s_start, sechdr->SizeOfRawData);
entropy = MAX(entropy, section_entropy);
/* Sanitize the section name */
memcpy(secname, sechdr->Name, IMAGE_SIZEOF_SHORT_NAME);
for (c = 0; (c < IMAGE_SIZEOF_SHORT_NAME) && secname[c]; c++)
if (!isprint(secname[c]))
secname[c] = '?';
secname[IMAGE_SIZEOF_SHORT_NAME - 1] = 0;
elogg(LOGG_INFO, "exeScanner: Section name: [%s] - Entropy %f\n", secname,
section_entropy);
if (!sechdr->SizeOfRawData)
badsection = 1;
}
elogg(LOGG_INFO, "exeScanner: Max entropy = %f\n", entropy);
/* EP Check */
elogg(LOGG_INFO, "exeScanner: Entry Point rva: 0x%lx - raw: 0x%lx\n",
opthdr->AddressOfEntryPoint, epoff);
ep = lpMapAddress + epoff;
if (!IsBadReadPtr(ep, EP_SIGNATURE_SIZE)) {
#ifdef DUMP_SIGNATURE
int i;
for (i = 0; i < EP_SIGNATURE_SIZE; i++)
elogg(LOGG_INFO, "%02x ", ep[i]);
elogg(LOGG_INFO, "\n[C Code]: ");
for (i = 0; i < EP_SIGNATURE_SIZE - 1; i++)
elogg(LOGG_INFO, "0x%02x, ", ep[i]);
elogg(LOGG_INFO, "0x%02x\n", ep[i]);
#endif
if ((sig = checksig(ep))) {
elogg(LOGG_INFO, "exeScanner: Signature check: %s\n", sig->name);
entropy += sig->score;
packed = (sig->score >= .0f);
if (sig->score < .0f)
elogg(
"exeScanner: Whitelisted signature found, lowering entropy to %f\n",
entropy);
} else
elogg(LOGG_INFO, "exeScanner: Signature check: Nothing found\n");
} else
elogg(LOGG_INFO, "exeScanner: Invalid address of Entry Point\n");
if (badsection) {
if ((entropy == .0f) || (entropy > ENTROPY_THRESHOLD)) {
elogg(LOGG_INFO, "exeScanner: found zero SizeOfRawData and entropy %f\n", entropy);
packed = 1;
goto cleanup;
}
}
cleanup:
if (lpMapAddress)
UnmapViewOfFile(lpMapAddress);
if (hMapFile)
CloseHandle(hMapFile);
CloseHandle(hFile);
return packed;
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2021-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2006-2008 Gianluigi Tiesi <sherpya@netfarm.it>
*
* Authors: Gianluigi Tiesi
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#ifndef _EXESCANNER_H_
#define _EXESCANNER_H_
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#ifdef EXESCANNER_STANDALONE
#define DUMP_SIGNATURE
#include <windows.h>
#define logg printf
#define elogg printf
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef __int16 int16_t;
#else
#include "output.h"
#include <others.h>
static inline void elogg(const char *fmt, ...){};
#endif /* EXESCANNER_STANDALONE */
#define ENTROPY_THRESHOLD 4.0
#define EP_SIGNATURE_SIZE 16
#ifndef IMAGE_DOS_SIGNATURE
#define IMAGE_DOS_SIGNATURE 0x5A4D /* MZ */
#endif
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
typedef struct _sigs_t {
int16_t sig[16];
const char *name;
double score;
} sigs_t;
extern int is_packed(const char *filename);
static const char screv[] =
{
0x65, 0x78, 0x65, 0x53, 0x63, 0x61, 0x6e, 0x6e,
0x65, 0x72, 0x7c, 0x47, 0x50, 0x4c, 0x7c, 0x47,
0x69, 0x61, 0x6e, 0x6c, 0x75, 0x69, 0x67, 0x69,
0x20, 0x54, 0x69, 0x65, 0x73, 0x69, 0x7c, 0x3c,
0x73, 0x68, 0x65, 0x72, 0x70, 0x79, 0x61, 0x40,
0x6e, 0x65, 0x74, 0x66, 0x61, 0x72, 0x6d, 0x2e,
0x69, 0x74, 0x3e};
#endif /* _EXESCANNER_H_ */

302
clamav/common/getopt.c Normal file
View File

@ -0,0 +1,302 @@
/*
* getopt.c - my re-implementation of getopt.
* Copyright 1997, 2000, 2001, 2002, 2006, Benjamin Sittler
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "getopt.h"
int optind = 1, opterr = 1, optopt = 0;
char *optarg = 0;
/* reset argument parser to start-up values */
/*
int getopt_reset(void)
{
optind = 1;
opterr = 1;
optopt = 0;
optarg = 0;
return 0;
}
*/
/* this is the plain old UNIX getopt, with GNU-style extensions. */
/* if you're porting some piece of UNIX software, this is all you need. */
/* this supports GNU-style permution and optional arguments */
int my_getopt(int argc, char *argvc[], const char *opts)
{
char **argv = (char **)argvc;
static int charind = 0;
const char *s;
char mode, colon_mode;
int off = 0, opt = -1;
if (getenv("POSIXLY_CORRECT"))
colon_mode = mode = '+';
else {
if ((colon_mode = *opts) == ':') off++;
if (((mode = opts[off]) == '+') || (mode == '-')) {
off++;
if ((colon_mode != ':') && ((colon_mode = opts[off]) == ':'))
off++;
}
}
optarg = 0;
if (charind) {
optopt = argv[optind][charind];
for (s = opts + off; *s; s++)
if (optopt == *s) {
charind++;
if ((*(++s) == ':') || ((optopt == 'W') && (*s == ';'))) {
if (argv[optind][charind]) {
optarg = &(argv[optind++][charind]);
charind = 0;
} else if (*(++s) != ':') {
charind = 0;
if (++optind >= argc) {
if (opterr) fprintf(stderr,
"%s: option requires an argument -- %c\n",
argv[0], optopt);
opt = (colon_mode == ':') ? ':' : '?';
goto getopt_ok;
}
optarg = argv[optind++];
}
}
opt = optopt;
goto getopt_ok;
}
if (opterr) fprintf(stderr,
"%s: illegal option -- %c\n",
argv[0], optopt);
opt = '?';
if (argv[optind][++charind] == '\0') {
optind++;
charind = 0;
}
getopt_ok:
if (charind && !argv[optind][charind]) {
optind++;
charind = 0;
}
} else if ((optind >= argc) ||
((argv[optind][0] == '-') &&
(argv[optind][1] == '-') &&
(argv[optind][2] == '\0'))) {
optind++;
opt = -1;
} else if ((argv[optind][0] != '-') ||
(argv[optind][1] == '\0')) {
char *tmp;
int i, j, k;
if (mode == '+')
opt = -1;
else if (mode == '-') {
optarg = argv[optind++];
charind = 0;
opt = 1;
} else {
for (i = j = optind; i < argc; i++)
if ((argv[i][0] == '-') &&
(argv[i][1] != '\0')) {
optind = i;
opt = my_getopt(argc, argv, opts);
while (i > j) {
tmp = argv[--i];
for (k = i; k + 1 < optind; k++) argv[k] = argv[k + 1];
argv[--optind] = tmp;
}
break;
}
if (i == argc) opt = -1;
}
} else {
charind++;
opt = my_getopt(argc, argv, opts);
}
if (optind > argc) optind = argc;
return opt;
}
/* this is the extended getopt_long{,_only}, with some GNU-like
* extensions. Implements _getopt_internal in case any programs
* expecting GNU libc getopt call it.
*/
static int _getopt_internal(int argc, char *argv[], const char *shortopts,
const struct option *longopts, int *longind,
int long_only)
{
char mode, colon_mode;
int shortoff = 0, opt = -1;
if (getenv("POSIXLY_CORRECT")) {
colon_mode = mode = '+';
} else {
if ((colon_mode = *shortopts) == ':') shortoff++;
if (((mode = shortopts[shortoff]) == '+') || (mode == '-')) {
shortoff++;
if ((colon_mode != ':') && ((colon_mode = shortopts[shortoff]) == ':'))
shortoff++;
}
}
optarg = 0;
if ((optind >= argc) ||
((argv[optind][0] == '-') &&
(argv[optind][1] == '-') &&
(argv[optind][2] == '\0'))) {
optind++;
opt = -1;
} else if ((argv[optind][0] != '-') ||
(argv[optind][1] == '\0')) {
char *tmp;
int i, j, k;
opt = -1;
if (mode == '+')
return -1;
else if (mode == '-') {
optarg = argv[optind++];
return 1;
}
for (i = j = optind; i < argc; i++)
if ((argv[i][0] == '-') &&
(argv[i][1] != '\0')) {
optind = i;
opt = _getopt_internal(argc, argv, shortopts,
longopts, longind,
long_only);
while (i > j) {
tmp = argv[--i];
for (k = i; k + 1 < optind; k++)
argv[k] = argv[k + 1];
argv[--optind] = tmp;
}
break;
}
} else if ((!long_only) && (argv[optind][1] != '-'))
opt = my_getopt(argc, argv, shortopts);
else {
int charind, offset;
int found = 0, ind, hits = 0;
if (((optopt = argv[optind][1]) != '-') && !argv[optind][2]) {
int c;
ind = shortoff;
while ((c = shortopts[ind++])) {
if (((shortopts[ind] == ':') ||
((c == 'W') && (shortopts[ind] == ';'))) &&
(shortopts[++ind] == ':'))
ind++;
if (optopt == c) return my_getopt(argc, argv, shortopts);
}
}
offset = 2 - (argv[optind][1] != '-');
for (charind = offset;
(argv[optind][charind] != '\0') &&
(argv[optind][charind] != '=');
charind++)
;
for (ind = 0; longopts[ind].name && !hits; ind++)
if ((strlen(longopts[ind].name) == (size_t)(charind - offset)) &&
(strncmp(longopts[ind].name,
argv[optind] + offset, charind - offset) == 0))
found = ind, hits++;
if (!hits)
for (ind = 0; longopts[ind].name; ind++)
if (strncmp(longopts[ind].name,
argv[optind] + offset, charind - offset) == 0)
found = ind, hits++;
if (hits == 1) {
opt = 0;
if (argv[optind][charind] == '=') {
if (longopts[found].has_arg == 0) {
opt = '?';
if (opterr) fprintf(stderr,
"%s: option `--%s' doesn't allow an argument\n",
argv[0], longopts[found].name);
} else {
optarg = argv[optind] + ++charind;
// charind = 0; // Never used again past here
}
} else if (longopts[found].has_arg == 1) {
if (++optind >= argc) {
opt = (colon_mode == ':') ? ':' : '?';
if (opterr) fprintf(stderr,
"%s: option `--%s' requires an argument\n",
argv[0], longopts[found].name);
} else
optarg = argv[optind];
}
if (!opt) {
if (longind) *longind = found;
if (!longopts[found].flag)
opt = longopts[found].val;
else
*(longopts[found].flag) = longopts[found].val;
}
optind++;
} else if (!hits) {
if (offset == 1)
opt = my_getopt(argc, argv, shortopts);
else {
opt = '?';
if (opterr) fprintf(stderr,
"%s: unrecognized option `%s'\n",
argv[0], argv[optind++]);
}
} else {
opt = '?';
if (opterr) fprintf(stderr,
"%s: option `%s' is ambiguous\n",
argv[0], argv[optind++]);
}
}
if (optind > argc) optind = argc;
return opt;
}
int my_getopt_long(int argc, char *argv[], const char *shortopts,
const struct option *longopts, int *longind)
{
return _getopt_internal(argc, argv, shortopts, longopts, longind, 0);
}
int my_getopt_long_only(int argc, char *argv[], const char *shortopts,
const struct option *longopts, int *longind)
{
return _getopt_internal(argc, argv, shortopts, longopts, longind, 1);
}

65
clamav/common/getopt.h Normal file
View File

@ -0,0 +1,65 @@
/*
* getopt.h - interface to my re-implementation of getopt.
* Copyright 1997, 2000, 2001, 2002, 2006, Benjamin Sittler
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _GETOPT_H_INCLUDED
#define _GETOPT_H_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
/* UNIX-style short-argument parser */
extern int my_getopt(int argc, char *argv[], const char *opts);
extern int optind, opterr, optopt;
extern char *optarg;
struct option {
const char *name;
int has_arg;
int *flag;
int val;
};
/* human-readable values for has_arg */
#undef no_argument
#define no_argument 0
#undef required_argument
#define required_argument 1
#undef optional_argument
#define optional_argument 2
/* GNU-style long-argument parsers */
extern int my_getopt_long(int argc, char *argv[], const char *shortopts,
const struct option *longopts, int *longind);
extern int my_getopt_long_only(int argc, char *argv[], const char *shortopts,
const struct option *longopts, int *longind);
#ifdef __cplusplus
}
#endif
#endif /* _GETOPT_H_INCLUDED */

64
clamav/common/hostid.c Normal file
View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved.
*
* Author: Shawn Webb
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <string.h>
// libclamav
#include "others.h"
#include "output.h"
char hostid[37];
int is_valid_hostid(void)
{
int count, i;
if (strlen(hostid) != 36)
return 0;
count = 0;
for (i = 0; i < 36; i++)
if (hostid[i] == '-')
count++;
if (count != 4)
return 0;
if (hostid[8] != '-' || hostid[13] != '-' || hostid[18] != '-' || hostid[23] != '-')
return 0;
return 1;
}
char *get_hostid(void *cbdata)
{
UNUSEDPARAM(cbdata);
if (!strcmp(hostid, "none"))
return NULL;
if (!is_valid_hostid())
return strdup(STATS_ANON_UUID);
logg(LOGG_INFO, "HostID is valid: %s\n", hostid);
return strdup(hostid);
}

View File

@ -0,0 +1,258 @@
/*
* Copyright (C) 2007-2013 Sourcefire, Inc.
*
* Authors: Selim Menouar, Verene Houdebine
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "clamav.h"
#include "misc.h"
#include "output.h"
#ifndef PRELUDE
void prelude_logging(const char *filename, const char *virname, const char *virhash, int virsize)
{
UNUSEDPARAM(filename);
UNUSEDPARAM(virname);
UNUSEDPARAM(virhash);
UNUSEDPARAM(virsize);
logg(LOGG_INFO, "You have to compile with libprelude using ./configure --enable-prelude\n");
}
#else
#include <libprelude/prelude.h>
#define ANALYZER_MODEL "ClamAV"
#define ANALYZER_CLASS "AntiVirus"
#define ANALYZER_MANUFACTURER "http://www.sourcefire.com"
static prelude_client_t *prelude_client;
int idmef_analyzer_setup(idmef_analyzer_t *analyzer, const char *analyzer_name)
{
int ret;
prelude_string_t *str;
/* alert->analyzer->name */
ret = idmef_analyzer_new_name(analyzer, &str);
if (ret < 0)
return ret;
prelude_string_set_constant(str, analyzer_name);
/* alert->analyzer->model */
ret = idmef_analyzer_new_model(analyzer, &str);
if (ret < 0)
return ret;
prelude_string_set_constant(str, ANALYZER_MODEL);
/* alert->analyzer->class */
ret = idmef_analyzer_new_class(analyzer, &str);
if (ret < 0)
return ret;
prelude_string_set_constant(str, ANALYZER_CLASS);
/* alert->analyzer->manufacturer */
ret = idmef_analyzer_new_manufacturer(analyzer, &str);
if (ret < 0)
return ret;
prelude_string_set_constant(str, ANALYZER_MANUFACTURER);
/* alert->analyzer->version */
ret = idmef_analyzer_new_version(analyzer, &str);
if (ret < 0)
return ret;
prelude_string_set_constant(str, get_version());
return 0;
}
int prelude_initialize_client(const char *analyzer_name)
{
int ret;
prelude_client = NULL;
ret = prelude_init(0, NULL);
if (ret < 0) {
logg(LOGG_INFO, "Unable to initialize the prelude library : %s", prelude_strerror(ret));
return -1;
}
ret = prelude_client_new(&prelude_client, analyzer_name);
if (ret < 0) {
logg(LOGG_INFO, "Unable to create a prelude client object : %s", prelude_strerror(ret));
return -1;
}
ret = idmef_analyzer_setup(prelude_client_get_analyzer(prelude_client), analyzer_name);
if (ret < 0) {
logg(LOGG_INFO, "%s", prelude_strerror(ret));
return -1;
}
ret = prelude_client_start(prelude_client);
if (ret < 0 || !prelude_client) {
logg(LOGG_INFO, "Unable to start prelude client : %s", prelude_strerror(ret));
prelude_client_destroy(prelude_client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);
return -1;
}
ret = prelude_client_set_flags(prelude_client, PRELUDE_CLIENT_FLAGS_ASYNC_SEND | PRELUDE_CLIENT_FLAGS_ASYNC_TIMER);
if (ret < 0) {
logg(LOGG_INFO, "Unable to send asynchronous send and timer : %s", prelude_strerror(ret));
prelude_client_destroy(prelude_client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);
return -1;
}
return 0;
}
int add_string_additional_data(idmef_alert_t *alert, const char *meaning, const char *ptr)
{
int ret;
prelude_string_t *str;
idmef_additional_data_t *ad;
idmef_data_t *data;
ret = idmef_alert_new_additional_data(alert, &ad, IDMEF_LIST_APPEND);
if (ret < 0)
return ret;
idmef_additional_data_set_type(ad, IDMEF_ADDITIONAL_DATA_TYPE_STRING);
idmef_additional_data_new_data(ad, &data);
ret = idmef_data_set_char_string_ref(data, ptr);
if (ret < 0)
return ret;
ret = idmef_additional_data_new_meaning(ad, &str);
if (ret < 0)
return ret;
ret = prelude_string_set_ref(str, meaning);
if (ret < 0)
return ret;
return 0;
}
int add_int_additional_data(idmef_alert_t *alert, const char *meaning, int data)
{
int ret;
prelude_string_t *str;
idmef_additional_data_t *ad;
ret = idmef_alert_new_additional_data(alert, &ad, IDMEF_LIST_APPEND);
if (ret < 0)
return ret;
idmef_additional_data_set_integer(ad, data);
ret = idmef_additional_data_new_meaning(ad, &str);
if (ret < 0)
return ret;
ret = prelude_string_set_ref(str, meaning);
if (ret < 0)
return ret;
return 0;
}
void prelude_logging(const char *filename, const char *virname, const char *virhash, int virsize)
{
int ret;
idmef_message_t *idmef = NULL;
idmef_alert_t *alert;
idmef_classification_t *class;
prelude_string_t *str;
idmef_target_t *target;
idmef_file_t *file;
ret = idmef_message_new(&idmef);
if (ret < 0)
goto err;
ret = idmef_message_new_alert(idmef, &alert);
if (ret < 0)
goto err;
ret = idmef_alert_new_classification(alert, &class);
if (ret < 0)
goto err;
ret = idmef_classification_new_text(class, &str);
if (ret < 0)
goto err;
prelude_string_set_constant(str, "Virus Found");
ret = idmef_alert_new_target(alert, &target, 0);
if (ret < 0)
goto err;
ret = idmef_target_new_file(target, &file, 0);
if (ret < 0)
goto err;
ret = idmef_file_new_path(file, &str);
if (ret < 0)
goto err;
prelude_string_set_ref(str, filename);
if (virname != NULL) {
ret = add_string_additional_data(alert, "virname", virname);
if (ret < 0)
goto err;
}
if (virhash != NULL) {
ret = add_string_additional_data(alert, "virhash", virhash);
if (ret < 0)
goto err;
}
ret = add_int_additional_data(alert, "virsize", virsize);
if (ret < 0)
goto err;
logg(LOGG_INFO, "le client : %s", prelude_client_get_config_filename(prelude_client));
prelude_client_send_idmef(prelude_client, idmef);
idmef_message_destroy(idmef);
return;
err:
if (idmef != NULL)
idmef_message_destroy(idmef);
logg(LOGG_INFO, "%s error: %s", prelude_strsource(ret), prelude_strerror(ret));
return;
}
#endif

View File

@ -0,0 +1,102 @@
/*
* OpenSSL certificate verification for Linux.
*
* Copyright (C) 2016-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*
* Authors: Russ Kubik
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <stdint.h>
#include <stdlib.h>
#include <inttypes.h>
#include <curl/curl.h>
#include "output.h"
#include "cert_util.h"
#include "cert_util_internal.h"
void set_tls_ca_bundle(CURL *curl)
{
char *ca_bundle;
ca_bundle = getenv("CURL_CA_BUNDLE");
if (ca_bundle == NULL)
return;
if (curl_easy_setopt(curl, CURLOPT_CAINFO, ca_bundle) != CURLE_OK) {
fprintf(stderr, "Failed to set CURLOPT_CAINFO!\n");
}
}
cl_error_t cert_store_load(X509 **trusted_certs, size_t trusted_cert_count)
{
cl_error_t ret = CL_EOPEN;
cert_store_t *store = NULL;
int pt_err;
do {
store = cert_store_get_int();
if (!store) {
mprintf(LOGG_ERROR, "Failed to retrieve cert store\n");
break;
}
pt_err = pthread_mutex_lock(&store->mutex);
if (pt_err) {
errno = pt_err;
mprintf(LOGG_ERROR, "Mutex lock failed\n");
}
if (store->loaded) {
ret = 0;
break;
}
/* System certs do not need to be added as they can be accessed directly
* by the SSL library. */
store->system_certs.count = 0;
store->system_certs.certificates = NULL;
if (trusted_certs && trusted_cert_count > 0) {
if (cert_store_set_trusted_int(trusted_certs, trusted_cert_count) == 0) {
mprintf(LOGG_DEBUG, "Trusted certificates loaded: %zu\n",
store->trusted_certs.count);
} else {
mprintf(LOGG_WARNING, "Continuing without trusted certificates\n");
/* proceed as if we succeeded using only certificates from the
* system */
}
}
store->loaded = true;
ret = 0;
} while (0);
if (store) {
pt_err = pthread_mutex_unlock(&store->mutex);
if (pt_err) {
errno = pt_err;
mprintf(LOGG_ERROR, "Mutex unlock failed\n");
}
}
return ret;
}

View File

@ -0,0 +1,402 @@
/*
* OpenSSL certificate verification for macOS.
*
* Copyright (C) 2016-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*
* Authors: Russ Kubik
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <Foundation/Foundation.h>
#import <Security/SecRequirement.h>
#import <Security/SecBase.h>
#import <Security/SecCode.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <Security/Security.h>
#include <sys/syslimits.h>
#import <sys/proc_info.h>
#import <libproc.h>
#include <sys/stat.h>
#include <libgen.h>
#include <mach-o/dyld.h>
#include <curl/curl.h>
#include "output.h"
#include "cert_util.h"
#include "cert_util_internal.h"
/* Macro to obtain the number of elements in a fixed sized array that was either
* statically declared or declared on the stack in the same scope. The macro
* will generate a divide-by-zero compiler warning if the input is a pointer.
*
* See also:
* http://stackoverflow.com/questions/8018843/macro-definition-array-size
*/
#define ARRAY_SIZE(a) ((sizeof(a) / sizeof(*(a))) / ((size_t)(!(sizeof(a) % sizeof(*(a))))))
/* Keychain types available on macOS. User specific keychains are omitted for
* simplicity. */
typedef enum keychain_type {
KEYCHAIN_TYPE_SYSTEM_ROOT,
KEYCHAIN_TYPE_SYSTEM
} keychain_type_t;
/* Basic information about a keychain */
typedef struct keychain_info {
const char *name;
const char *file_path;
} keychain_info_t;
/* Table to support name and file path lookup for each keychain type */
static const keychain_info_t _KEYCHAIN_INFO[] =
{
{.name = "system root",
.file_path = "/System/Library/Keychains/SystemRootCertificates.keychain"},
{.name = "system",
.file_path = "/Library/Keychains/System.keychain"}};
/*!
* @brief Get basic information about the specified keychain.
* @param[in] keychain_type Keychain type
* @return The keychain information. All pointers contained in this
* point to read only data and so do not need to be freed.
*/
static keychain_info_t _get_keychain_info(keychain_type_t keychain_type)
{
return _KEYCHAIN_INFO[keychain_type];
}
/*!
* @brief Get a reference to an allocated array of certifcates contained
* in the specified keychain.
* @param[in] keychain_type Keychain type
* @return If successful, reference to allocated array of certifcates. The
* caller is responsible for calling CFRelease on the returned
* reference after use.
* @return NULL otherwise
*/
static CFTypeRef _get_cert_ref(keychain_type_t keychain_type)
{
keychain_info_t kc_info = _get_keychain_info(keychain_type);
CFTypeRef keys[] = {
kSecMatchSearchList,
kSecClass,
kSecReturnRef,
kSecMatchLimit,
kSecMatchTrustedOnly,
kSecMatchValidOnDate,
};
CFTypeRef values[] = {
/* NOTE: must match the order specified above */
kCFNull, /* place holder for match search list */
kSecClassCertificate, /* kSecClass */
kCFBooleanTrue, /* kSecReturnRef */
kSecMatchLimitAll, /* kSecMatchLimit */
kCFBooleanTrue, /* kSecMatchTrustedOnly */
kCFNull, /* kSecMatchValidOnDate */
};
CFDictionaryRef query = NULL;
CFTypeRef items = NULL;
SecKeychainRef keychain = NULL;
CFArrayRef search_list = NULL;
SecKeychainStatus keychainStatus = 0;
OSStatus status;
status = SecKeychainOpen(kc_info.file_path, &keychain);
if (status != errSecSuccess) {
mprintf(LOGG_ERROR, "Failed to open %s keychain: %s (%d)\n",
kc_info.name,
kc_info.file_path,
status);
goto done;
}
status = SecKeychainGetStatus(keychain, &keychainStatus);
if (status != errSecSuccess) {
mprintf(LOGG_ERROR, "Failed to get the status of the %s keychain: %d\n",
kc_info.name,
status);
goto done;
}
if (!(keychainStatus & kSecReadPermStatus)) {
mprintf(LOGG_ERROR, "The %s keychain is not readable: %" PRIu32 "\n",
kc_info.name,
keychainStatus);
goto done;
}
if (keychain_type == KEYCHAIN_TYPE_SYSTEM_ROOT) {
/*
* The SystemRootCertificates.keychain is a system keychain file that should be locked
* and should definitely not have writable permissions. This may indicate that the file
* has been tampered with.
*/
if (keychainStatus & (kSecUnlockStateStatus | kSecWritePermStatus)) {
mprintf(LOGG_ERROR, "System Root Certificates Keychain has invalid permissions: %" PRIu32 "\n",
keychainStatus);
/* continue on error */
}
}
search_list = CFArrayCreate(kCFAllocatorDefault,
(const void **)&keychain, 1, &kCFTypeArrayCallBacks);
if (search_list == NULL) {
mprintf(LOGG_ERROR, "Failed to create %s keychain search list\n",
kc_info.name);
goto done;
}
/* set the search list for the secItemCopyMatching call */
values[0] = search_list;
query = CFDictionaryCreate(NULL, keys, values, ARRAY_SIZE(keys),
&kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (query == NULL) {
mprintf(LOGG_ERROR, "Failed to create %s keychain query dictionary\n",
kc_info.name);
goto done;
}
status = SecItemCopyMatching(query, &items);
if (status != errSecSuccess) {
if (status == errSecItemNotFound) {
mprintf(LOGG_DEBUG, "No items found in %s keychain\n",
kc_info.name);
} else {
mprintf(LOGG_ERROR, "Unable to copy certificates from %s keychain (%d)\n",
kc_info.name,
status);
}
}
CFRelease(query);
query = NULL;
done:
if (keychain) {
CFRelease(keychain);
keychain = NULL;
}
if (search_list) {
CFRelease(search_list);
search_list = NULL;
}
return items;
}
cl_error_t cert_store_load(X509 **trusted_certs, size_t trusted_cert_count)
{
static const keychain_type_t keychains[] = {
KEYCHAIN_TYPE_SYSTEM_ROOT,
KEYCHAIN_TYPE_SYSTEM};
typedef struct keychain_cert_data {
CFArrayRef certs;
CFIndex certs_count;
} keychain_cert_data_t;
keychain_cert_data_t keychain_cert_data_array[ARRAY_SIZE(keychains)] = {
{.certs = NULL,
.certs_count = 0},
/* All other array values initialized to 0 by default */
};
size_t kc_index = 0;
cl_error_t ret = CL_EOPEN;
int pt_err;
cert_store_t *store = NULL;
CFIndex total_certificates = 0;
CFIndex i = 0;
bool locked = false;
store = cert_store_get_int();
if (!store) {
mprintf(LOGG_ERROR, "Failed to retrieve cert store\n");
goto done;
}
/* Load certificates from keychains before entering the critical section.
* On a default 10.12 installation loading the the system roots keychain
* could take up to 300 ms to complete. */
for (kc_index = 0; kc_index < ARRAY_SIZE(keychains); kc_index++) {
keychain_type_t kc = keychains[kc_index];
keychain_info_t kc_info = _get_keychain_info(kc);
keychain_cert_data_t *kc_data = &keychain_cert_data_array[kc_index];
CFTypeRef items = NULL;
items = _get_cert_ref(kc);
if (!items) {
continue;
}
if (CFGetTypeID(items) != CFArrayGetTypeID()) {
mprintf(LOGG_ERROR, "Expected array of certificates from %s keychain, "
"got type %lu\n",
kc_info.name,
CFGetTypeID(items));
continue;
}
if (CFArrayGetCount(items) < 1) {
CFRelease(items);
items = NULL;
continue;
}
kc_data->certs = (CFArrayRef)items;
kc_data->certs_count = CFArrayGetCount(items);
mprintf(LOGG_DEBUG, "Found %ld certificates from %s keychain\n",
kc_data->certs_count,
kc_info.name);
total_certificates += kc_data->certs_count;
}
if (total_certificates < 1) {
mprintf(LOGG_ERROR, "No certificate found in keychains. Expect at least one "
"certificate to be found in system root and system "
"keychains\n");
goto done;
}
store = cert_store_get_int();
if (!store) {
mprintf(LOGG_ERROR, "Failed to retrieve cert store\n");
goto done;
}
pt_err = pthread_mutex_lock(&store->mutex);
if (pt_err) {
errno = pt_err;
mprintf(LOGG_ERROR, "Mutex lock failed\n");
}
locked = true;
if (store->loaded) {
mprintf(LOGG_DEBUG, "Cert store already loaded\n");
ret = CL_SUCCESS;
goto done;
}
store->system_certs.count = 0;
store->system_certs.certificates = calloc(total_certificates,
sizeof(*store->system_certs.certificates));
if (store->system_certs.certificates == NULL) {
mprintf(LOGG_ERROR, "Failed to reserve memory for system cert list\n");
goto done;
}
for (kc_index = 0; kc_index < ARRAY_SIZE(keychains); kc_index++) {
keychain_type_t kc = keychains[kc_index];
keychain_info_t kc_info = _get_keychain_info(kc);
keychain_cert_data_t *kc_data = &keychain_cert_data_array[kc_index];
for (i = 0; i < kc_data->certs_count; i++) {
const void *value = CFArrayGetValueAtIndex(kc_data->certs, i);
if (CFGetTypeID(value) == SecCertificateGetTypeID()) {
SecCertificateRef cert = (SecCertificateRef)value;
CFDataRef cert_data = SecCertificateCopyData(cert); /* DER representation of X.509 */
if (cert_data) {
const unsigned char *der = CFDataGetBytePtr(cert_data);
CFIndex length = CFDataGetLength(cert_data);
char *name = NULL;
X509 *x509 = d2i_X509(NULL, &der, length);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
x509_get_cert_name(x509, &name);
#else
name = x509->name;
#endif
if (x509) {
mprintf(LOGG_DEBUG, "Found %s trusted certificate %s\n",
kc_info.name,
(name ? name : "<no name>"));
store->system_certs.certificates[store->system_certs.count++] = x509;
} else {
mprintf(LOGG_ERROR, "Failed conversion of DER format to X.509\n");
}
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
if (NULL != name) {
free(name);
name = NULL;
}
#endif
CFRelease(cert_data);
cert_data = NULL;
}
}
}
}
if (trusted_certs && trusted_cert_count > 0) {
if (cert_store_set_trusted_int(trusted_certs, trusted_cert_count) == 0) {
mprintf(LOGG_DEBUG, "Trusted certificates loaded: %zu\n",
store->trusted_certs.count);
} else {
mprintf(LOGG_WARNING, "Continuing without trusted certificates\n");
/* proceed as if we succeeded using only certificates from the
* system */
}
}
store->loaded = true;
ret = CL_SUCCESS;
done:
if (locked) {
pt_err = pthread_mutex_unlock(&store->mutex);
if (pt_err) {
errno = pt_err;
mprintf(LOGG_ERROR, "Mutex unlock failed\n");
}
locked = false;
}
for (kc_index = 0; kc_index < ARRAY_SIZE(keychains); kc_index++) {
keychain_cert_data_t *kc_data = &keychain_cert_data_array[kc_index];
if (kc_data->certs) {
CFRelease(kc_data->certs);
kc_data->certs = NULL;
kc_data->certs_count = 0;
}
}
return ret;
}

489
clamav/common/misc.c Normal file
View File

@ -0,0 +1,489 @@
/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2007-2013 Sourcefire, Inc.
*
* Authors: Tomasz Kojm
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef _WIN32
#include <sys/socket.h>
#include <pwd.h>
#include <grp.h>
#include <sys/types.h>
#endif
#include <dirent.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
// libclamav
#include "clamav.h"
#include "cvd.h"
#include "others.h" /* for cli_rmdirs() */
#include "regex/regex.h"
#include "version.h"
#include "optparser.h"
#include "output.h"
#include "misc.h"
#include <signal.h>
#ifndef WIN32
#include <sys/wait.h>
#endif
#ifndef REPO_VERSION
#define REPO_VERSION "exported"
#endif
const char *get_version(void)
{
if (!strncmp("devel-", VERSION, 6) && strcmp("exported", REPO_VERSION)) {
return REPO_VERSION "" VERSION_SUFFIX;
}
/* it is a release, or we have nothing better */
return VERSION "" VERSION_SUFFIX;
}
char *freshdbdir(void)
{
struct cl_cvd *d1, *d2;
struct optstruct *opts;
const struct optstruct *opt;
const char *dbdir;
char *retdir;
/* try to find the most up-to-date db directory */
dbdir = cl_retdbdir();
if ((opts = optparse(CONFDIR_FRESHCLAM, 0, NULL, 0, OPT_FRESHCLAM, 0, NULL))) {
if ((opt = optget(opts, "DatabaseDirectory"))->enabled) {
if (strcmp(dbdir, opt->strarg)) {
char *daily = (char *)malloc(strlen(opt->strarg) + strlen(dbdir) + 30);
if (daily == NULL) {
fprintf(stderr, "Unable to allocate memory for db directory...\n");
return NULL;
}
sprintf(daily, "%s" PATHSEP "daily.cvd", opt->strarg);
if (access(daily, R_OK))
sprintf(daily, "%s" PATHSEP "daily.cld", opt->strarg);
if (!access(daily, R_OK) && (d1 = cl_cvdhead(daily))) {
sprintf(daily, "%s" PATHSEP "daily.cvd", dbdir);
if (access(daily, R_OK))
sprintf(daily, "%s" PATHSEP "daily.cld", dbdir);
if (!access(daily, R_OK) && (d2 = cl_cvdhead(daily))) {
free(daily);
if (d1->version > d2->version)
dbdir = opt->strarg;
cl_cvdfree(d2);
} else {
free(daily);
dbdir = opt->strarg;
}
cl_cvdfree(d1);
} else {
free(daily);
}
}
}
}
retdir = strdup(dbdir);
if (opts)
optfree(opts);
return retdir;
}
void print_version(const char *dbdir)
{
char *fdbdir = NULL, *path;
const char *pt;
struct cl_cvd *daily;
time_t db_time;
unsigned int db_version = 0;
if (dbdir)
pt = dbdir;
else
pt = fdbdir = freshdbdir();
if (!pt) {
printf("ClamAV %s\n", get_version());
return;
}
if (!(path = malloc(strlen(pt) + 11))) {
if (!dbdir)
free(fdbdir);
return;
}
sprintf(path, "%s" PATHSEP "daily.cvd", pt);
if (!access(path, R_OK)) {
daily = cl_cvdhead(path);
if (daily) {
db_version = daily->version;
db_time = daily->stime;
cl_cvdfree(daily);
}
}
sprintf(path, "%s" PATHSEP "daily.cld", pt);
if (!access(path, R_OK)) {
daily = cl_cvdhead(path);
if (daily) {
if (daily->version > db_version) {
db_version = daily->version;
db_time = daily->stime;
}
cl_cvdfree(daily);
}
}
if (!dbdir)
free(fdbdir);
if (db_version) {
printf("ClamAV %s/%u/%s", get_version(), db_version, ctime(&db_time));
} else {
printf("ClamAV %s\n", get_version());
}
free(path);
}
int check_flevel(void)
{
if (cl_retflevel() < CL_FLEVEL) {
fprintf(stderr, "ERROR: This tool requires libclamav with functionality level %u or higher (current f-level: %u)\n", CL_FLEVEL, cl_retflevel());
return 1;
}
return 0;
}
const char *filelist(const struct optstruct *opts, int *err)
{
static char buff[1025];
static unsigned int cnt = 0;
const struct optstruct *opt;
static FILE *fs = NULL;
size_t len;
if (!cnt && (opt = optget(opts, "file-list"))->enabled) {
if (!fs) {
fs = fopen(opt->strarg, "r");
if (!fs) {
fprintf(stderr, "ERROR: --file-list: Can't open file %s\n", opt->strarg);
if (err)
*err = 54;
return NULL;
}
}
if (fgets(buff, 1024, fs)) {
buff[1024] = 0;
len = strlen(buff);
if (!len) {
fclose(fs);
return NULL;
}
len--;
while (len && ((buff[len] == '\n') || (buff[len] == '\r')))
buff[len--] = '\0';
return buff;
} else {
fclose(fs);
return NULL;
}
}
return opts->filename ? opts->filename[cnt++] : NULL;
}
int filecopy(const char *src, const char *dest)
{
#ifdef _WIN32
return (!CopyFileA(src, dest, 0));
#elif defined(C_DARWIN)
pid_t pid;
/* On Mac OS X use ditto and copy resource fork, too. */
switch (pid = fork()) {
case -1:
return -1;
case 0:
execl("/usr/bin/ditto", "ditto", src, dest, NULL);
perror("execl(ditto)");
break;
default:
wait(NULL);
return 0;
}
return -1;
#else /* C_DARWIN */
return cli_filecopy(src, dest);
#endif
}
#ifndef _WIN32
int close_std_descriptors()
{
int fds[3], i;
fds[0] = open("/dev/null", O_RDONLY);
fds[1] = open("/dev/null", O_WRONLY);
fds[2] = open("/dev/null", O_WRONLY);
if (fds[0] == -1 || fds[1] == -1 || fds[2] == -1) {
fputs("Can't open /dev/null\n", stderr);
for (i = 0; i <= 2; i++)
if (fds[i] != -1)
close(fds[i]);
return -1;
}
for (i = 0; i <= 2; i++) {
if (dup2(fds[i], i) == -1) {
fprintf(stderr, "dup2(%d, %d) failed\n", fds[i], i); /* may not be printed */
for (i = 0; i <= 2; i++)
if (fds[i] != -1)
close(fds[i]);
return -1;
}
}
for (i = 0; i <= 2; i++)
if (fds[i] > 2)
close(fds[i]);
return 0;
}
int daemonize_all_return(void)
{
pid_t pid;
pid = fork();
if (0 == pid) {
setsid();
}
return pid;
}
int daemonize(void)
{
int ret = 0;
ret = close_std_descriptors();
if (ret) {
return ret;
}
ret = daemonize_all_return();
pid_t pid = (pid_t)ret;
/*parent process.*/
if (pid > 0) {
exit(0);
}
return pid;
}
static void daemonize_child_initialized_handler(int sig)
{
(void)(sig);
exit(0);
}
int daemonize_parent_wait(const char *const user, const char *const log_file)
{
int daemonizePid = daemonize_all_return();
if (daemonizePid == -1) {
return -1;
} else if (daemonizePid) { // parent
/* The parent will wait until either the child process
* exits, or signals the parent that it's initialization is
* complete. If it exits, it is due to an error condition,
* so the parent should exit with the same error code as the child.
* If the child signals the parent that initialization is complete, it
* the parent will exit from the signal handler (initDoneSignalHandler)
* with exit code 0.
*/
struct sigaction sig;
memset(&sig, 0, sizeof(sig));
sigemptyset(&(sig.sa_mask));
sig.sa_handler = daemonize_child_initialized_handler;
if (0 != sigaction(SIGINT, &sig, NULL)) {
perror("sigaction");
return -1;
}
if (NULL != user) {
if (drop_privileges(user, log_file)) {
return -1;
}
}
int exitStatus;
wait(&exitStatus);
if (WIFEXITED(exitStatus)) { // error
exitStatus = WEXITSTATUS(exitStatus);
exit(exitStatus);
}
}
return 0;
}
void daemonize_signal_parent(pid_t parentPid)
{
close_std_descriptors();
kill(parentPid, SIGINT);
}
int drop_privileges(const char *const user_name, const char *const log_file)
{
int ret = 1;
/*This function is called in a bunch of places, and rather than change the error checking
* in every function, we are just going to return success if there is no work to do.
*/
if ((0 == geteuid()) && (NULL != user_name)) {
struct passwd *user = NULL;
if ((user = getpwnam(user_name)) == NULL) {
logg(LOGG_WARNING, "Can't get information about user %s.\n", user_name);
fprintf(stderr, "ERROR: Can't get information about user %s.\n", user_name);
goto done;
}
#ifdef HAVE_INITGROUPS
if (initgroups(user_name, user->pw_gid)) {
fprintf(stderr, "ERROR: initgroups() failed.\n");
logg(LOGG_WARNING, "initgroups() failed.\n");
goto done;
}
#elif HAVE_SETGROUPS
if (setgroups(1, &user->pw_gid)) {
fprintf(stderr, "ERROR: setgroups() failed.\n");
logg(LOGG_WARNING, "setgroups() failed.\n");
goto done;
}
#endif
/*Change ownership of the log file to the user we are going to switch to.*/
if (NULL != log_file) {
int ret = lchown(log_file, user->pw_uid, user->pw_gid);
if (ret) {
fprintf(stderr, "ERROR: lchown to user '%s' failed on\n", user->pw_name);
fprintf(stderr, "log file '%s'.\n", log_file);
fprintf(stderr, "Error was '%s'\n", strerror(errno));
logg(LOGG_WARNING, "lchown to user '%s' failed on log file '%s'. Error was '%s'\n",
user->pw_name, log_file, strerror(errno));
goto done;
}
}
if (setgid(user->pw_gid)) {
fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int)user->pw_gid);
logg(LOGG_WARNING, "setgid(%d) failed.\n", (int)user->pw_gid);
goto done;
}
if (setuid(user->pw_uid)) {
fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int)user->pw_uid);
logg(LOGG_WARNING, "setuid(%d) failed.\n", (int)user->pw_uid);
goto done;
}
}
ret = 0;
done:
return ret;
}
#endif /*_WIN32*/
int match_regex(const char *filename, const char *pattern)
{
regex_t reg;
int match, flags = REG_EXTENDED | REG_NOSUB;
char fname[513];
#ifdef _WIN32
flags |= REG_ICASE; /* case insensitive on Windows */
#endif
if (cli_regcomp(&reg, pattern, flags) != 0)
return 2;
if (pattern[strlen(pattern) - 1] == *PATHSEP) {
snprintf(fname, 511, "%s" PATHSEP, filename);
fname[512] = 0;
} else {
strncpy(fname, filename, 513);
fname[512] = '\0';
}
match = (cli_regexec(&reg, fname, 0, NULL, 0) == REG_NOMATCH) ? 0 : 1;
cli_regfree(&reg);
return match;
}
int cli_is_abspath(const char *path)
{
#ifdef _WIN32
int len = strlen(path);
return (len > 2 && path[0] == '\\' && path[1] == '\\') || (len >= 2 && ((*path >= 'a' && *path <= 'z') || (*path >= 'A' && *path <= 'Z')) && path[1] == ':');
#else
return *path == '/';
#endif
}
unsigned int countlines(const char *filename)
{
FILE *fh;
char buff[1024];
unsigned int lines = 0;
if ((fh = fopen(filename, "r")) == NULL)
return 0;
while (fgets(buff, sizeof(buff), fh)) {
if (buff[0] == '#') continue;
lines++;
}
fclose(fh);
return lines;
}

108
clamav/common/misc.h Normal file
View File

@ -0,0 +1,108 @@
/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2007-2013 Sourcefire, Inc.
*
* Authors: Tomasz Kojm
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#ifndef __MISC_H
#define __MISC_H
#ifndef _WIN32
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#endif
#include <stdbool.h>
#include "platform.h"
#include "optparser.h"
/* Maximum filenames under various systems - njh */
#ifndef NAME_MAX /* e.g. Linux */
#ifdef MAXNAMELEN /* e.g. Solaris */
#define NAME_MAX MAXNAMELEN
#else
#ifdef FILENAME_MAX /* e.g. SCO */
#define NAME_MAX FILENAME_MAX
#endif
#endif
#endif
#ifdef HAVE_SYSTEMD
#include <systemd/sd-daemon.h>
#else
#define sd_listen_fds(u) 0
#define SD_LISTEN_FDS_START 3
#define sd_is_socket(f, a, s, l) 1
#endif
#include <limits.h>
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
#ifndef ADDR_LEN
#define ADDR_LEN 13
#endif
char *freshdbdir(void);
void print_version(const char *dbdir);
int check_flevel(void);
const char *filelist(const struct optstruct *opts, int *err);
int filecopy(const char *src, const char *dest);
#ifndef _WIN32
/*Returns 0 on success (only the child process returns.*/
int daemonize(void);
/*closes stdin, stdout, stderr. This is called by daemonize, but not
* daemonize_all_return. Users of daemonize_all_return should call this
* when initialization is complete.*/
int close_std_descriptors(void);
/*Returns the return value of fork. All processes return */
int daemonize_all_return(void);
/*Parent waits for a SIGINT or the child process to exit. If
* it receives a SIGINT, it exits with exit code 0. If the child
* exits (error), it exits with the child process's exit code.
*
* @param user If user is supplied and this function is being called
* as root, daemonize_parent_wait will change the parent process
* to user before calling wait so that the child process can signal
* the parent when it is time to exit. The child process will still
* return as root.
*
* @param log_file If user AND log_file are both supplied and this
* function is being called as root, the ownership of log_file will
* be changed to user.
*/
int daemonize_parent_wait(const char *const user, const char *const log_file);
/*Sends a SIGINT to the parent process. It also closes stdin, stdout,
* and stderr.*/
void daemonize_signal_parent(pid_t parentPid);
int drop_privileges(const char *const user, const char *const log_file);
#endif /* _WIN32 */
const char *get_version(void);
int match_regex(const char *filename, const char *pattern);
int cli_is_abspath(const char *path);
unsigned int countlines(const char *filename);
#endif

1483
clamav/common/optparser.c Normal file

File diff suppressed because it is too large Load Diff

617
clamav/common/output.c Normal file
View File

@ -0,0 +1,617 @@
/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2007-2013 Sourcefire, Inc.
*
* Authors: Tomasz Kojm
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <fcntl.h>
#include <time.h>
#include <sys/stat.h>
#include <errno.h>
#ifndef _WIN32
#include <sys/time.h>
#include <sys/socket.h>
#endif
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#if defined(USE_SYSLOG) && !defined(C_AIX)
#include <syslog.h>
#endif
// libclamav
#include "clamav.h"
#include "others.h"
#include "str.h"
#include "output.h"
#ifdef CL_THREAD_SAFE
#include <pthread.h>
pthread_mutex_t logg_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mdprintf_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
#if defined(C_LINUX) && defined(HAVE_LIBINTL_H)
#include <libintl.h>
#include <locale.h>
#define gettext_noop(s) s
#define _(s) gettext(s)
#define N_(s) gettext_noop(s)
#else
#define _(s) s
#define N_(s) s
#endif
FILE *logg_fp = NULL;
short int logg_verbose = 0, logg_nowarn = 0, logg_lock = 1, logg_time = 0, logg_foreground = 1, logg_noflush = 0, logg_rotate = 0;
off_t logg_size = 0;
const char *logg_file = NULL;
#if defined(USE_SYSLOG) && !defined(C_AIX)
short logg_syslog;
#endif
short int mprintf_disabled = 0, mprintf_verbose = 0, mprintf_quiet = 0,
mprintf_stdout = 0, mprintf_nowarn = 0, mprintf_send_timeout = 100, mprintf_progress = 0;
#define ARGLEN(args, str, len) \
{ \
size_t arglen = 1, i; \
char *pt; \
va_start(args, str); \
len = strlen(str); \
for (i = 0; i < len - 1; i++) { \
if (str[i] == '%') { \
switch (str[++i]) { \
case 's': \
pt = va_arg(args, char *); \
if (pt) \
arglen += strlen(pt); \
break; \
case 'f': \
va_arg(args, double); \
arglen += 25; \
break; \
case 'l': \
va_arg(args, long); \
arglen += 20; \
break; \
default: \
va_arg(args, int); \
arglen += 10; \
break; \
} \
} \
} \
va_end(args); \
len += arglen; \
}
int mdprintf(int desc, const char *str, ...)
{
va_list args;
char buffer[512], *abuffer = NULL, *buff;
int bytes, todo, ret = 0;
size_t len;
ARGLEN(args, str, len);
if (len <= sizeof(buffer)) {
len = sizeof(buffer);
buff = buffer;
} else {
abuffer = malloc(len);
if (!abuffer) {
len = sizeof(buffer);
buff = buffer;
} else {
buff = abuffer;
}
}
va_start(args, str);
bytes = vsnprintf(buff, len, str, args);
va_end(args);
buff[len - 1] = 0;
if (bytes < 0) {
if (len > sizeof(buffer))
free(abuffer);
return bytes;
}
if ((size_t)bytes >= len)
bytes = len - 1;
todo = bytes;
#ifdef CL_THREAD_SAFE
/* make sure we don't mix sends from multiple threads,
* important for IDSESSION */
pthread_mutex_lock(&mdprintf_mutex);
#endif
while (todo > 0) {
ret = send(desc, buff, bytes, 0);
if (ret < 0) {
struct timeval tv;
if (errno != EWOULDBLOCK)
break;
/* didn't send anything yet */
#ifdef CL_THREAD_SAFE
pthread_mutex_unlock(&mdprintf_mutex);
#endif
tv.tv_sec = 0;
tv.tv_usec = mprintf_send_timeout * 1000;
do {
fd_set wfds;
FD_ZERO(&wfds);
FD_SET(desc, &wfds);
ret = select(desc + 1, NULL, &wfds, NULL, &tv);
} while (ret < 0 && errno == EINTR);
#ifdef CL_THREAD_SAFE
pthread_mutex_lock(&mdprintf_mutex);
#endif
if (!ret) {
/* timed out */
ret = -1;
break;
}
} else {
todo -= ret;
buff += ret;
}
}
#ifdef CL_THREAD_SAFE
pthread_mutex_unlock(&mdprintf_mutex);
#endif
if (len > sizeof(buffer))
free(abuffer);
return ret < 0 ? -1 : bytes;
}
static int rename_logg(STATBUF *sb)
{
char *rotate_file;
size_t rotate_file_len;
time_t t;
struct tm tmp;
if (!logg_rotate) {
if (logg_fp) {
fprintf(logg_fp, "Log size = %lld, max = %lld\n", (long long int)sb->st_size, (long long int)logg_size);
fprintf(logg_fp, "WARNING: Log size limit met but log file rotation turned off. Forcing log file rotation anyways.\n");
}
logg_rotate = 1;
}
rotate_file_len = strlen(logg_file) + strlen("-YYYY-MM-DD_HH:MM:SS.log");
rotate_file = calloc(1, rotate_file_len + 1);
if (!rotate_file) {
if (logg_fp)
fprintf(logg_fp, "Need to rotate log file due to size but ran out of memory.\n");
return -1;
}
t = time(NULL);
#ifdef _WIN32
if (0 != localtime_s(&tmp, &t)) {
#else
if (!localtime_r(&t, &tmp)) {
#endif
if (logg_fp)
fprintf(logg_fp, "Need to rotate log file due to size but could not get local time.\n");
free(rotate_file);
return -1;
}
strcpy(rotate_file, logg_file);
strftime(rotate_file + strlen(rotate_file) - strlen(".log"), rotate_file_len - strlen(rotate_file), "-%Y%m%d_%H%M%S.log", &tmp);
if (logg_fp) {
fclose(logg_fp);
logg_fp = NULL;
}
#ifdef _WIN32
if (0 == MoveFileA(logg_file, rotate_file)) {
fprintf(stderr, "Failed to rename file with error code: %d\n", GetLastError());
#else
if (rename(logg_file, rotate_file)) {
#endif
free(rotate_file);
return -1;
}
free(rotate_file);
return 0;
}
static int logg_open(void)
{
STATBUF sb;
if (logg_file)
if (logg_size > 0)
if (CLAMSTAT(logg_file, &sb) != -1)
if (sb.st_size > logg_size)
if (rename_logg(&sb))
return -1;
return 0;
}
void logg_close(void)
{
#if defined(USE_SYSLOG) && !defined(C_AIX)
if (logg_syslog)
closelog();
#endif
#ifdef CL_THREAD_SAFE
pthread_mutex_lock(&logg_mutex);
#endif
if (logg_fp) {
fclose(logg_fp);
logg_fp = NULL;
}
#ifdef CL_THREAD_SAFE
pthread_mutex_unlock(&logg_mutex);
#endif
}
int logg(loglevel_t loglevel, const char *str, ...)
{
va_list args;
char buffer[1025], *abuffer = NULL, *buff;
time_t currtime;
size_t len;
mode_t old_umask;
#ifdef F_WRLCK
struct flock fl;
#endif
if ((loglevel == LOGG_DEBUG_NV && logg_verbose < 2) ||
(loglevel == LOGG_DEBUG && !logg_verbose))
return 0;
ARGLEN(args, str, len);
if (len <= sizeof(buffer)) {
len = sizeof(buffer);
buff = buffer;
} else {
abuffer = malloc(len);
if (!abuffer) {
len = sizeof(buffer);
buff = buffer;
} else {
buff = abuffer;
}
}
va_start(args, str);
vsnprintf(buff, len, str, args);
va_end(args);
buff[len - 1] = 0;
#ifdef CL_THREAD_SAFE
pthread_mutex_lock(&logg_mutex);
#endif
logg_open();
if (!logg_fp && logg_file) {
old_umask = umask(0037);
if ((logg_fp = fopen(logg_file, "at")) == NULL) {
umask(old_umask);
#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))
free(abuffer);
return -1;
} else
umask(old_umask);
#ifdef F_WRLCK
if (logg_lock) {
memset(&fl, 0, sizeof(fl));
fl.l_type = F_WRLCK;
if (fcntl(fileno(logg_fp), F_SETLK, &fl) == -1) {
#ifdef EOPNOTSUPP
if (errno == EOPNOTSUPP)
printf("WARNING: File locking not supported (NFS?)\n");
else
#endif
{
#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))
free(abuffer);
return -1;
}
}
}
#endif
}
if (logg_fp) {
char flush = !logg_noflush;
/* Need to avoid logging time for verbose messages when logverbose
is not set or we get a bunch of timestamps in the log without
newlines... */
if (logg_time && ((loglevel != LOGG_DEBUG) || logg_verbose)) {
char timestr[32];
time(&currtime);
cli_ctime(&currtime, timestr, sizeof(timestr));
/* cut trailing \n */
timestr[strlen(timestr) - 1] = '\0';
fprintf(logg_fp, "%s -> ", timestr);
}
if (loglevel == LOGG_ERROR) {
fprintf(logg_fp, "ERROR: %s", buff);
flush = 1;
} else if (loglevel == LOGG_WARNING) {
if (!logg_nowarn)
fprintf(logg_fp, "WARNING: %s", buff);
flush = 1;
} else if (loglevel == LOGG_DEBUG || loglevel == LOGG_DEBUG_NV) {
fprintf(logg_fp, "%s", buff);
} else if (loglevel == LOGG_INFO_NF || loglevel == LOGG_INFO) {
fprintf(logg_fp, "%s", buff);
} else
fprintf(logg_fp, "%s", buff);
if (flush)
fflush(logg_fp);
}
if (logg_foreground) {
if (loglevel != LOGG_INFO_NF) {
if (logg_time) {
char timestr[32];
time(&currtime);
cli_ctime(&currtime, timestr, sizeof(timestr));
/* cut trailing \n */
timestr[strlen(timestr) - 1] = '\0';
mprintf(loglevel, "%s -> %s", timestr, buff);
} else {
mprintf(loglevel, "%s", buff);
}
}
}
#if defined(USE_SYSLOG) && !defined(C_AIX)
if (logg_syslog) {
cli_chomp(buff);
if (loglevel == LOGG_ERROR) {
syslog(LOG_ERR, "%s", buff);
} else if (loglevel == LOGG_WARNING) {
if (!logg_nowarn)
syslog(LOG_WARNING, "%s", buff);
} else if (loglevel == LOGG_DEBUG || loglevel == LOGG_DEBUG_NV) {
syslog(LOG_DEBUG, "%s", buff);
} else
syslog(LOG_INFO, "%s", buff);
}
#endif
#ifdef CL_THREAD_SAFE
pthread_mutex_unlock(&logg_mutex);
#endif
if (len > sizeof(buffer))
free(abuffer);
return 0;
}
void mprintf(loglevel_t loglevel, const char *str, ...)
{
va_list args;
FILE *fd;
char buffer[512], *abuffer = NULL, *buff;
size_t len;
if (mprintf_disabled)
return;
fd = stdout;
ARGLEN(args, str, len);
if (len <= sizeof(buffer)) {
len = sizeof(buffer);
buff = buffer;
} else {
abuffer = malloc(len);
if (!abuffer) {
len = sizeof(buffer);
buff = buffer;
} else {
buff = abuffer;
}
}
va_start(args, str);
vsnprintf(buff, len, str, args);
va_end(args);
buff[len - 1] = 0;
#ifdef _WIN32
do {
int tmplen = len + 1;
wchar_t *tmpw = malloc(tmplen * sizeof(wchar_t));
char *nubuff;
if (!tmpw)
break;
if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, buff, -1, tmpw, tmplen)) {
free(tmpw);
break;
}
/* FIXME CHECK IT'S REALLY UTF8 */
nubuff = (char *)malloc(tmplen);
if (!nubuff) {
free(tmpw);
break;
}
if (!WideCharToMultiByte(CP_OEMCP, 0, tmpw, -1, nubuff, tmplen, NULL, NULL)) {
free(nubuff);
free(tmpw);
break;
}
free(tmpw);
if (len > sizeof(buffer))
free(abuffer);
abuffer = buff = nubuff;
len = sizeof(buffer) + 1;
} while (0);
#endif
if (loglevel == LOGG_ERROR) {
if (!mprintf_stdout)
fd = stderr;
fprintf(fd, "ERROR: %s", buff);
} else if (!mprintf_quiet) {
if (loglevel == LOGG_WARNING) {
if (!mprintf_nowarn) {
if (!mprintf_stdout)
fd = stderr;
fprintf(fd, "WARNING: %s", buff);
}
} else if (loglevel == LOGG_DEBUG) {
if (mprintf_verbose)
fprintf(fd, "%s", buff);
} else if (loglevel == LOGG_INFO) {
fprintf(fd, "%s", buff);
} else
fprintf(fd, "%s", buff);
}
if (fd == stdout)
fflush(stdout);
if (len > sizeof(buffer))
free(abuffer);
}
struct facstruct {
const char *name;
int code;
};
#if defined(USE_SYSLOG) && !defined(C_AIX)
static const struct facstruct facilitymap[] = {
#ifdef LOG_AUTH
{"LOG_AUTH", LOG_AUTH},
#endif
#ifdef LOG_AUTHPRIV
{"LOG_AUTHPRIV", LOG_AUTHPRIV},
#endif
#ifdef LOG_CRON
{"LOG_CRON", LOG_CRON},
#endif
#ifdef LOG_DAEMON
{"LOG_DAEMON", LOG_DAEMON},
#endif
#ifdef LOG_FTP
{"LOG_FTP", LOG_FTP},
#endif
#ifdef LOG_KERN
{"LOG_KERN", LOG_KERN},
#endif
#ifdef LOG_LPR
{"LOG_LPR", LOG_LPR},
#endif
#ifdef LOG_MAIL
{"LOG_MAIL", LOG_MAIL},
#endif
#ifdef LOG_NEWS
{"LOG_NEWS", LOG_NEWS},
#endif
#ifdef LOG_AUTH
{"LOG_AUTH", LOG_AUTH},
#endif
#ifdef LOG_SYSLOG
{"LOG_SYSLOG", LOG_SYSLOG},
#endif
#ifdef LOG_USER
{"LOG_USER", LOG_USER},
#endif
#ifdef LOG_UUCP
{"LOG_UUCP", LOG_UUCP},
#endif
#ifdef LOG_LOCAL0
{"LOG_LOCAL0", LOG_LOCAL0},
#endif
#ifdef LOG_LOCAL1
{"LOG_LOCAL1", LOG_LOCAL1},
#endif
#ifdef LOG_LOCAL2
{"LOG_LOCAL2", LOG_LOCAL2},
#endif
#ifdef LOG_LOCAL3
{"LOG_LOCAL3", LOG_LOCAL3},
#endif
#ifdef LOG_LOCAL4
{"LOG_LOCAL4", LOG_LOCAL4},
#endif
#ifdef LOG_LOCAL5
{"LOG_LOCAL5", LOG_LOCAL5},
#endif
#ifdef LOG_LOCAL6
{"LOG_LOCAL6", LOG_LOCAL6},
#endif
#ifdef LOG_LOCAL7
{"LOG_LOCAL7", LOG_LOCAL7},
#endif
{NULL, -1}};
int logg_facility(const char *name)
{
int i;
for (i = 0; facilitymap[i].name; i++)
if (!strcmp(facilitymap[i].name, name))
return facilitymap[i].code;
return -1;
}
#endif

97
clamav/common/output.h Normal file
View File

@ -0,0 +1,97 @@
/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2007-2013 Sourcefire, Inc.
*
* Authors: Tomasz Kojm
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#ifndef __OUTPUT_H
#define __OUTPUT_H
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef __GNUC__
int mdprintf(int desc, const char *str, ...) __attribute__((format(printf, 2, 3)));
#else
int mdprintf(int desc, const char *str, ...);
#endif
/*
* legend:
* NAME EXPLAIN
* LOGG_INFO normal
* LOGG_INFO_NF normal, no foreground (logfile and syslog only)
* LOGG_DEBUG debug, verbose
* LOGG_DEBUG_NV debug, non-verbose
* LOGG_WARNING warning
* LOGG_ERROR ERROR
*/
typedef enum loglevel {
LOGG_INFO,
LOGG_INFO_NF,
LOGG_DEBUG,
LOGG_DEBUG_NV,
LOGG_WARNING,
LOGG_ERROR
} loglevel_t;
/*
* @param loglevel legend:
* NAME EXPLAIN
* LOGG_INFO normal
* LOGG_INFO_NF normal, no foreground (logfile and syslog only)
* LOGG_DEBUG debug, verbose
* LOGG_DEBUG_NV debug, non-verbose
* LOGG_WARNING warning
* LOGG_ERROR ERROR
*
* @return 0 fur success and -1 for error, e.g. log file access problems
*/
#ifdef __GNUC__
int logg(loglevel_t loglevel, const char *str, ...) __attribute__((format(printf, 2, 3)));
#else
int logg(loglevel_t loglevel, const char *str, ...);
#endif
void logg_close(void);
extern short int logg_verbose, logg_nowarn, logg_lock, logg_time, logg_noflush, logg_rotate;
extern off_t logg_size;
extern const char *logg_file;
#if defined(USE_SYSLOG) && !defined(C_AIX)
extern short logg_syslog;
int logg_facility(const char *name);
#endif
#ifdef __GNUC__
void mprintf(loglevel_t loglevel, const char *str, ...) __attribute__((format(printf, 2, 3)));
#else
void mprintf(loglevel_t loglevel, const char *str, ...);
#endif
extern short int mprintf_disabled, mprintf_verbose, mprintf_quiet, mprintf_nowarn, mprintf_stdout, mprintf_send_timeout, mprintf_progress;
#endif

710
clamav/common/scanmem.c Normal file
View File

@ -0,0 +1,710 @@
/*
* Copyright (C) 2021-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2005-2010 Gianluigi Tiesi <sherpya@netfarm.it>
*
* Authors: Gianluigi Tiesi
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <windows.h>
#include <tlhelp32.h>
#include <psapi.h>
#include <windns.h>
#include <clamav.h>
#include <others.h>
#include "actions.h"
#include "output.h"
#include "clamdcom.h"
#include "exescanner.h"
#include "scanmem.h"
typedef int (*proc_callback)(PROCESSENTRY32 ProcStruct, MODULEENTRY32 me32, void *data, struct mem_info *info);
int sock;
struct optstruct *clamdopts;
static inline int lookup_cache(filelist_t **list, const char *filename)
{
filelist_t *current = *list;
while (current) {
/* Cache hit */
if (!_stricmp(filename, current->filename)) {
return current->res;
}
current = current->next;
}
return -1;
}
static inline void insert_cache(filelist_t **list, const char *filename,
int res)
{
filelist_t *current = *list, *prev = NULL;
if (!current) /* New */
*list = current = malloc(sizeof(filelist_t));
else {
while (current->next)
current = current->next;
prev = current;
prev->next = current = malloc(sizeof(filelist_t));
}
current->next = NULL;
current->res = res;
current->filename[0] = 0;
strncat(current->filename, filename,
MAX_PATH - 1 - strlen(current->filename));
current->filename[MAX_PATH - 1] = 0;
}
static inline void free_cache(filelist_t **list)
{
filelist_t *current, *prev;
current = prev = *list;
if (!current)
return;
do {
prev = current;
current = prev->next;
free(prev);
} while (current);
}
static inline char *wc2mb(const wchar_t *wc, DWORD flags)
{
BOOL invalid = FALSE;
DWORD len = 0, res = 0;
char *mb = NULL;
len = WideCharToMultiByte(CP_ACP, flags, wc, -1, NULL, 0, NULL, &invalid);
if (!len && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
fprintf(stderr, "WideCharToMultiByte() failed with %d\n", GetLastError());
return NULL;
}
mb = cli_malloc(len + 1);
if (!mb) return NULL;
res = WideCharToMultiByte(CP_ACP, flags, wc, -1, mb, len, NULL, &invalid);
if (res && ((!invalid || (flags != WC_NO_BEST_FIT_CHARS)))) return mb;
free(mb);
return NULL;
}
/* Needed to Scan System Processes */
int EnablePrivilege(LPCSTR PrivilegeName, DWORD yesno)
{
HANDLE hToken;
TOKEN_PRIVILEGES tp;
LUID luid;
if (!LoadLibraryA("advapi32.dll")) {
logg(LOGG_WARNING, "EnablePrivilege functions are missing\n");
return 0;
}
if (!OpenProcessToken(
GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_READ, &hToken))
return 0;
if (!LookupPrivilegeValue(NULL, PrivilegeName, &luid))
return 0;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = yesno;
AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
CloseHandle(hToken);
return (GetLastError() == ERROR_SUCCESS) ? 1 : 0;
}
static char *getaltpath(const wchar_t *filename)
{
WIN32_FIND_DATAW wfdw;
HANDLE hf = INVALID_HANDLE_VALUE;
wchar_t *part = _wcsdup(filename);
wchar_t comprev[MAX_PATH + 1] = L"", compose[MAX_PATH + 1];
wchar_t *rev = comprev, *slash = part, *c = NULL;
size_t l, la;
size_t i;
do {
if (slash != part)
*slash = 0;
/* c: d: etc */
if ((wcslen(part) == 2) && (part[1] == L':')) {
*rev++ = L':';
*rev++ = part[0];
break;
}
hf = FindFirstFileW(part, &wfdw);
if (hf == INVALID_HANDLE_VALUE) /* Network path */
{
for (i = wcslen(part); i > 0; i--)
*rev++ = part[i - 1];
break;
}
FindClose(hf);
l = wcslen(wfdw.cFileName);
la = wcslen(wfdw.cAlternateFileName);
if (la)
for (i = la; i > 0; i--)
*rev++ = *(wfdw.cAlternateFileName + i - 1);
else
for (i = l; i > 0; i--)
*rev++ = *(wfdw.cFileName + i - 1);
*rev++ = '\\';
} while ((slash = wcsrchr(part, L'\\')));
rev = comprev;
c = compose;
for (i = wcslen(rev); i > 0; i--)
*c++ = *(rev + i - 1);
*c = 0;
free(part);
return wc2mb(compose, WC_NO_BEST_FIT_CHARS);
}
int walkmodules_th(proc_callback callback, void *data, struct mem_info *info)
{
HANDLE hSnap = INVALID_HANDLE_VALUE, hModuleSnap = INVALID_HANDLE_VALUE;
PROCESSENTRY32 ps;
MODULEENTRY32 me32;
logg(LOGG_INFO, " *** Memory Scan: using ToolHelp ***\n\n");
hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap == INVALID_HANDLE_VALUE)
return -1;
ps.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(hSnap, &ps)) {
CloseHandle(hSnap);
return -1;
}
do {
/* system process */
if (!ps.th32ProcessID)
continue;
hModuleSnap = CreateToolhelp32Snapshot(
TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, ps.th32ProcessID);
if (hModuleSnap == INVALID_HANDLE_VALUE)
continue;
me32.dwSize = sizeof(MODULEENTRY32);
if (!Module32First(hModuleSnap, &me32)) {
CloseHandle(hModuleSnap);
continue;
}
/* Check and transform non ANSI filenames to ANSI using altnames */
if (GetModuleFileNameEx) {
HANDLE hFile = CreateFile(
me32.szExePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
wchar_t name[MAX_PATH + 1];
char *converted = NULL;
HANDLE p;
if (err == ERROR_BAD_NETPATH) {
logg(LOGG_WARNING, "Warning scanning files on non-ansi network paths is not "
"supported\n");
logg(LOGG_WARNING, "File: %s\n", me32.szExePath);
continue;
}
if ((err != ERROR_INVALID_NAME) && (err != ERROR_PATH_NOT_FOUND)) {
logg(LOGG_WARNING, "Expected ERROR_INVALID_NAME/ERROR_PATH_NOT_FOUND but got %d\n",
err);
continue;
}
p = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE,
ps.th32ProcessID);
if (!GetModuleFileNameEx(p, NULL, name, MAX_PATH)) {
logg(LOGG_WARNING, "GetModuleFileNameExW() failed %d\n", GetLastError());
CloseHandle(p);
continue;
}
CloseHandle(p);
if (!(converted = getaltpath(name))) {
logg(LOGG_WARNING, "Cannot map filename to ANSI codepage\n");
continue;
}
strcpy(me32.szExePath, converted);
free(converted);
} else
CloseHandle(hFile);
}
do
if (callback(ps, me32, data, info))
break;
while (Module32Next(hModuleSnap, &me32));
CloseHandle(hModuleSnap);
} while (Process32Next(hSnap, &ps));
CloseHandle(hSnap);
return 0;
}
int walkmodules_psapi(proc_callback callback, void *data, struct mem_info *info)
{
DWORD procs[1024], needed, nprocs, mneeded;
HANDLE hProc;
HMODULE mods[1024];
PROCESSENTRY32 ps;
MODULEENTRY32 me32;
MODULEINFO mi;
int i, j;
logg(LOGG_INFO, " *** Memory Scan: using PsApi ***\n\n");
if (!EnumProcesses(procs, sizeof(procs), &needed))
return -1;
nprocs = needed / sizeof(DWORD);
memset(&ps, 0, sizeof(PROCESSENTRY32));
memset(&me32, 0, sizeof(MODULEENTRY32));
ps.dwSize = sizeof(PROCESSENTRY32);
me32.dwSize = sizeof(MODULEENTRY32);
for (i = 0; i < nprocs; i++) {
if (!procs[i])
continue; /* System process */
hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE,
procs[i]);
if (!hProc)
continue;
if (!EnumProcessModules(hProc, mods, sizeof(mods),
&mneeded)) {
CloseHandle(hProc);
continue;
}
if (!GetModuleBaseName(hProc, mods[0], ps.szExeFile,
MAX_PATH - 1)) {
CloseHandle(hProc);
continue;
}
ps.th32ProcessID = procs[i];
for (j = 0; j < (mneeded / sizeof(HMODULE)); j++) {
if (!GetModuleBaseNameA(hProc, mods[j], me32.szModule,
MAX_PATH - 1))
continue;
if (!GetModuleFileNameExA(hProc, mods[j], me32.szExePath,
MAX_PATH - 1))
continue;
if (!GetModuleInformation(hProc, mods[j], &mi,
sizeof(mi)))
continue;
me32.hModule = mods[j];
me32.th32ProcessID = procs[i];
me32.modBaseAddr = mi.lpBaseOfDll;
me32.modBaseSize = mi.SizeOfImage;
if (callback(ps, me32, data, info))
break;
}
CloseHandle(hProc);
}
return 0;
}
int kill_process(DWORD pid)
{
HANDLE hProc;
if (GetCurrentProcessId() == pid) {
logg(LOGG_WARNING, "Don't want to kill myself\n");
return 1;
}
if ((hProc = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, pid))) {
TerminateProcess(hProc, 0);
if (WaitForSingleObject(hProc, TIMEOUT_MODULE) != WAIT_OBJECT_0)
logg(LOGG_WARNING, "Unable to unload process from memory\n");
CloseHandle(hProc);
} else
logg(LOGG_WARNING, "OpenProcess() failed %lu\n", GetLastError());
return 1; /* Skip to next process anyway */
}
/* Not so safe ;) */
int unload_module(DWORD pid, HANDLE hModule)
{
DWORD rc = 1;
HANDLE ht;
HANDLE hProc;
if (GetCurrentProcessId() == pid) {
logg(LOGG_WARNING, "Don't want to unload modules from myself\n");
return 1;
}
hProc = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |
PROCESS_VM_WRITE | PROCESS_VM_READ,
FALSE, pid);
if (!hProc) {
logg(LOGG_WARNING, "OpenProcess() failed %lu\n", GetLastError());
return 1; /* Skip to next process */
}
if ((ht = CreateRemoteThread(
hProc, 0, 0, (LPTHREAD_START_ROUTINE)FreeLibrary, hModule, 0,
&rc))) {
if (WaitForSingleObject(ht, TIMEOUT_MODULE) == WAIT_TIMEOUT) {
CloseHandle(ht);
CloseHandle(hProc);
logg(LOGG_INFO, "The module may trying to trick us, killing the process, please "
"rescan\n");
return kill_process(pid);
}
CloseHandle(ht);
rc = 0; /* Continue scanning this process */
} else {
DWORD res = GetLastError();
if (res == ERROR_CALL_NOT_IMPLEMENTED) {
logg(LOGG_WARNING, "Module unloading is not supported on this OS\n");
rc = -1; /* Don't complain about removing/moving the file */
} else {
logg(LOGG_ERROR, "CreateRemoteThread() failed %lu\n", res);
rc = 1; /* Skip to next process */
}
}
CloseHandle(hProc);
return rc;
}
#define FILLBYTES(dst) \
if (IsBadReadPtr(seek, sizeof(dst))) { \
logg(LOGG_ERROR, "ScanMem Align: Bad pointer!!!\n"); \
return 1; \
} \
memcpy(&dst, seek, sizeof(dst))
/* PE Realignment - FIXME: a lot of code is copy/paste from exeScanner.c */
int align_pe(unsigned char *buffer, size_t size)
{
int i = 0;
uint16_t e_mz;
uint32_t e_lfanew, e_magic;
unsigned char *seek = buffer;
PIMAGE_FILE_HEADER pehdr;
PIMAGE_OPTIONAL_HEADER32 opthdr;
PIMAGE_SECTION_HEADER sechdr;
FILLBYTES(e_mz);
if (e_mz != IMAGE_DOS_SIGNATURE) {
/* cli_dbgmsg("ScanMem Align: DOS Signature not found\n"); */
return 0;
}
seek += 0x3c;
FILLBYTES(e_lfanew);
if (!e_lfanew) {
/* cli_dbgmsg("ScanMem Align: Invalid PE offset\n"); */
return 0;
}
seek = buffer + e_lfanew;
/* PE Signature 'PE' */
FILLBYTES(e_magic);
if (e_magic != IMAGE_NT_SIGNATURE) {
/* cli_dbgmsg("ScanMem Align: PE Signature not found\n"); */
return 0;
}
seek += sizeof(e_magic);
if (IsBadReadPtr(seek, sizeof(IMAGE_FILE_HEADER)))
return 0;
pehdr = (PIMAGE_FILE_HEADER)seek;
seek += sizeof(IMAGE_FILE_HEADER);
if (IsBadReadPtr(seek, sizeof(IMAGE_OPTIONAL_HEADER32)))
return 0;
opthdr = (PIMAGE_OPTIONAL_HEADER32)seek;
seek += sizeof(IMAGE_OPTIONAL_HEADER32);
/* Invalid sections number */
if ((pehdr->NumberOfSections < 1) || (pehdr->NumberOfSections > 32)) {
/* cli_dbgmsg("ScanMem Align: Invalid sections number\n"); */
return 0;
}
for (i = 0; i < pehdr->NumberOfSections; i++) {
if (IsBadWritePtr(seek, sizeof(IMAGE_SECTION_HEADER)))
return 0;
sechdr = (PIMAGE_SECTION_HEADER)seek;
seek += sizeof(IMAGE_SECTION_HEADER);
sechdr->PointerToRawData = sechdr->VirtualAddress;
sechdr->SizeOfRawData = sechdr->Misc.VirtualSize;
}
return 1;
}
int dump_pe(const char *filename, PROCESSENTRY32 ProcStruct,
MODULEENTRY32 me32)
{
#ifdef _WIN64 /* MinGW has a broken header for ReadProcessMemory() */
size_t bytesread = 0;
#else
DWORD bytesread = 0;
#endif
DWORD byteswrite = 0;
int ret = -1;
HANDLE hFile = INVALID_HANDLE_VALUE, hProc = NULL;
unsigned char *buffer = NULL;
if (!(hProc = OpenProcess(PROCESS_VM_READ, FALSE, ProcStruct.th32ProcessID)))
return -1;
buffer = malloc((size_t)me32.modBaseSize);
if (!ReadProcessMemory(hProc, me32.modBaseAddr, buffer,
(size_t)me32.modBaseSize, &bytesread)) {
free(buffer);
CloseHandle(hProc);
return ret;
}
CloseHandle(hProc);
/* PE Realignment */
align_pe(buffer, me32.modBaseSize);
hFile = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
logg(LOGG_INFO, "Error creating %s\n", filename);
free(buffer);
return ret;
}
if (WriteFile(hFile, buffer, (DWORD)bytesread, &byteswrite, NULL))
ret = _open_osfhandle((intptr_t)hFile, O_RDONLY | O_BINARY);
free(buffer);
return ret;
}
int scanfile(const char *filename, scanmem_data *scan_data, struct mem_info *info)
{
int fd;
int scantype;
int ret = CL_CLEAN;
const char *virname = NULL;
logg(LOGG_DEBUG, "Scanning %s\n", filename);
if ((fd = safe_open(filename, O_RDONLY | O_BINARY)) == -1) {
logg(LOGG_WARNING, "Can't open file %s, %s\n", filename, strerror(errno));
return -1;
}
if (info->d) { // clamdscan
if (optget(info->opts, "stream")->enabled)
scantype = STREAM;
else if (optget(info->opts, "multiscan")->enabled)
scantype = MULTI;
else if (optget(info->opts, "allmatch")->enabled)
scantype = ALLMATCH;
else
scantype = CONT;
if ((sock = dconnect(clamdopts)) < 0) {
info->errors++;
return -1;
}
if (dsresult(sock, scantype, filename, NULL, &info->errors, clamdopts) > 0) {
info->ifiles++;
ret = CL_VIRUS;
}
} else { // clamscan
ret = cl_scandesc(fd, filename, &virname, &info->blocks, info->engine, info->options);
if (ret == CL_VIRUS) {
logg(LOGG_INFO, "%s: %s FOUND\n", filename, virname);
info->ifiles++;
} else if (scan_data->printclean) {
logg(LOGG_INFO, "%s: OK \n", filename);
}
}
close(fd);
return ret;
}
int scanmem_cb(PROCESSENTRY32 ProcStruct, MODULEENTRY32 me32, void *data, struct mem_info *info)
{
scanmem_data *scan_data = data;
int rc = 0;
int isprocess = 0;
char modulename[MAX_PATH] = "";
char expandmodule[MAX_PATH] = "";
if (!scan_data)
return 0;
scan_data->res = CL_CLEAN;
modulename[0] = 0;
/* Special case, btw why I get \SystemRoot\ in process szExePath?
There are also other cases? */
if ((strlen(me32.szExePath) > 12) &&
!strncmp(me32.szExePath, "\\SystemRoot\\", 12)) {
expandmodule[0] = 0;
strncat(expandmodule, me32.szExePath, MAX_PATH - 1 - strlen(expandmodule));
expandmodule[MAX_PATH - 1] = 0;
snprintf(expandmodule, MAX_PATH - 1, "%%SystemRoot%%\\%s",
&me32.szExePath[12]);
expandmodule[MAX_PATH - 1] = 0;
ExpandEnvironmentStrings(expandmodule, modulename, MAX_PATH - 1);
modulename[MAX_PATH - 1] = 0;
}
if (!modulename[0]) {
strncpy(modulename, me32.szExePath, MAX_PATH - 1);
modulename[MAX_PATH - 1] = 0;
}
scan_data->res = lookup_cache(&scan_data->files, modulename);
isprocess = !_stricmp(ProcStruct.szExeFile, modulename) ||
!_stricmp(ProcStruct.szExeFile, me32.szModule);
if (scan_data->res == -1) {
if (isprocess)
scan_data->processes++;
else
scan_data->modules++;
info->files++;
/* check for module exclusion */
scan_data->res = CL_CLEAN;
if (!(scan_data->exclude && chkpath(modulename, clamdopts)))
scan_data->res = scanfile(modulename, scan_data, info);
if ((scan_data->res != CL_VIRUS) && is_packed(modulename)) {
char *dumped = cli_gentemp(NULL);
int fd = -1;
if ((fd = dump_pe(dumped, ProcStruct, me32)) > 0) {
close(fd);
scan_data->res = scanfile(dumped, scan_data, info);
DeleteFile(dumped);
}
free(dumped);
}
insert_cache(&scan_data->files, modulename, scan_data->res);
}
if (scan_data->res == CL_VIRUS) {
if (isprocess && scan_data->kill) {
logg(LOGG_INFO, "Unloading program %s from memory\n", modulename);
rc = kill_process(ProcStruct.th32ProcessID);
} else if (scan_data->unload) {
logg(LOGG_INFO, "Unloading module %s from %s\n", me32.szModule, modulename);
if ((rc = unload_module(ProcStruct.th32ProcessID, me32.hModule)) == -1)
/* CreateProcessThread() is not implemented */
return 0;
}
if (action)
action(modulename);
return rc;
}
return rc;
}
int scanmem(struct mem_info *info)
{
scanmem_data data;
data.files = NULL;
data.printclean = 1;
data.kill = 0;
data.unload = 0;
data.exclude = 0;
data.res = CL_CLEAN;
data.processes = 0;
data.modules = 0;
HMODULE psapi_ok = LoadLibrary("psapi.dll");
HMODULE k32_ok = LoadLibrary("kernel32.dll");
if (!(psapi_ok || k32_ok)) {
logg(LOGG_INFO, " *** Memory Scanning is not supported on this OS ***\n\n");
return -1;
}
if (optget(info->opts, "infected")->enabled)
data.printclean = 0;
if (optget(info->opts, "kill")->enabled)
data.kill = 1;
if (optget(info->opts, "unload")->enabled)
data.unload = 1;
if (optget(info->opts, "exclude")->enabled)
data.exclude = 1;
if (info->d) {
if ((sock = dconnect(clamdopts)) < 0) {
info->errors++;
return -1;
}
}
logg(LOGG_INFO, " *** Scanning Programs in Computer Memory ***\n");
if (!EnablePrivilege(SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED))
logg(LOGG_INFO, "---Please login as an Administrator to scan System processes loaded "
"in computer memory---\n");
if (k32_ok)
walkmodules_th(scanmem_cb, (void *)&data, info);
else
walkmodules_psapi(scanmem_cb, (void *)&data, info);
free_cache(&data.files);
logg(LOGG_INFO, "\n *** Scanned %lu processes - %lu modules ***\n", data.processes,
data.modules);
logg(LOGG_INFO, " *** Computer Memory Scan Completed ***\n\n");
return data.res;
}

68
clamav/common/scanmem.h Normal file
View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2021-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2005-2010 Gianluigi Tiesi <sherpya@netfarm.it>
*
* Authors: Gianluigi Tiesi
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#ifndef __SCANMEM_H
#define __SCANMEM_H
#ifndef TH32CS_SNAPMODULE32
#define TH32CS_SNAPMODULE32 0x00000010
#endif
#define TIMEOUT_MODULE 30000
int scanmem(struct mem_info *info);
/* cache helpers */
typedef struct _filelist_t {
char filename[MAX_PATH];
int res;
struct _filelist_t *next;
} filelist_t;
/* Callback */
typedef struct _cb_data_t {
const char *filename;
size_t size, count;
int oldvalue;
int fd;
} cb_data_t;
typedef struct _scanmem_data_t {
filelist_t *files;
int printclean, kill, unload, exclude;
int res;
uint32_t processes, modules;
} scanmem_data;
struct mem_info {
unsigned int d; /*1 = clamdscan, 0 = clamscan */
unsigned int files; /* number of scanned files */
unsigned int ifiles; /* number of infected files */
unsigned long int blocks; /* number of *scanned* 16kb blocks */
unsigned int errors;
struct cl_engine *engine;
const struct optstruct *opts;
struct cl_scan_options *options;
};
#endif

228
clamav/common/service.c Normal file
View File

@ -0,0 +1,228 @@
/*
* Copyright (C) 2021-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2008-2010 Gianluigi Tiesi <sherpya@netfarm.it>
*
* Authors: Gianluigi Tiesi
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <platform.h>
#include <winsvc.h>
#include "service.h"
#include "output.h"
static SERVICE_STATUS svc;
static SERVICE_STATUS_HANDLE svc_handle;
static SERVICE_TABLE_ENTRYA DT[] = {{"Service", ServiceMain}, {NULL, NULL}};
static HANDLE evStart;
static HANDLE DispatcherThread;
static int checkpoint_every = 5000;
int svc_uninstall(const char *name, int verbose)
{
SC_HANDLE sm, svc;
int ret = 1;
if (!(sm = OpenSCManagerA(NULL, NULL, DELETE))) {
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
fprintf(stderr, "Windows Services are not supported on this Platform\n");
else
fprintf(stderr, "Unable to Open SCManager (%d)\n", GetLastError());
return 0;
}
if ((svc = OpenServiceA(sm, name, DELETE))) {
if (DeleteService(svc)) {
if (verbose) printf("Service %s successfully removed\n", name);
} else {
fprintf(stderr, "Unable to Open Service %s (%d)\n", name, GetLastError());
ret = 0;
}
} else {
if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
if (verbose) printf("Service %s does not exist\n", name);
} else {
fprintf(stderr, "Unable to Open Service %s (%d)\n", name, GetLastError());
ret = 0;
}
}
if (svc) CloseServiceHandle(svc);
CloseServiceHandle(sm);
return ret;
}
int svc_install(const char *name, const char *dname, const char *desc)
{
SC_HANDLE sm, svc;
char modulepath[MAX_PATH];
char binpath[MAX_PATH];
SERVICE_DESCRIPTIONA sdesc = {(char *)desc};
if (!GetModuleFileName(NULL, modulepath, MAX_PATH - 1)) {
fprintf(stderr, "Unable to get the executable name (%d)\n", GetLastError());
return 0;
}
if (!svc_uninstall(name, 0)) return 0;
if (!(sm = OpenSCManagerA(NULL, NULL, SC_MANAGER_CREATE_SERVICE | DELETE))) {
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
fprintf(stderr, "Windows Services are not supported on this Platform\n");
else
fprintf(stderr, "Unable to Open SCManager (%d)\n", GetLastError());
return 0;
}
if (strchr(modulepath, ' '))
snprintf(binpath, MAX_PATH - 1, "\"%s\" --daemon --service-mode", modulepath);
else
snprintf(binpath, MAX_PATH - 1, "%s --daemon --service-mode", modulepath);
svc = CreateServiceA(sm, name, dname, SERVICE_CHANGE_CONFIG,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
binpath,
NULL, /* Load group order */
NULL, /* Tag Id */
NULL, /* Dependencies */
NULL, /* User -> Local System */
"");
if (!svc) {
fprintf(stderr, "Unable to Create Service %s (%d)\n", name, GetLastError());
CloseServiceHandle(sm);
return 0;
}
/* ChangeServiceConfig2A() */
if (!ChangeServiceConfig2A(svc, SERVICE_CONFIG_DESCRIPTION, &sdesc))
fprintf(stderr, "Unable to set description for Service %s (%d)\n", name, GetLastError());
CloseServiceHandle(svc);
CloseServiceHandle(sm);
printf("Service %s successfully created.\n", name);
printf("Use 'net start %s' and 'net stop %s' to start/stop the service.\n", name, name);
return 1;
}
static void svc_getcpvalue(const char *name)
{
HKEY hKey;
DWORD dwType;
DWORD value, vlen = sizeof(DWORD);
char subkey[MAX_PATH];
snprintf(subkey, MAX_PATH - 1, "SYSTEM\\CurrentControlSet\\Services\\%s", name);
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS)
return;
if ((RegQueryValueExA(hKey, "Checkpoint", NULL, &dwType, (LPBYTE)&value, &vlen) == ERROR_SUCCESS) &&
(vlen == sizeof(DWORD) && (dwType == REG_DWORD)))
checkpoint_every = value;
RegCloseKey(hKey);
}
void svc_register(const char *name)
{
DWORD tid;
DT->lpServiceName = (char *)name;
svc_getcpvalue(name);
evStart = CreateEvent(NULL, TRUE, FALSE, NULL);
DispatcherThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)StartServiceCtrlDispatcherA, (LPVOID)DT, 0, &tid);
}
void svc_ready(void)
{
WaitForSingleObject(evStart, INFINITE);
svc.dwCurrentState = SERVICE_RUNNING;
svc.dwControlsAccepted |= SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
svc.dwCheckPoint = 0;
if (!SetServiceStatus(svc_handle, &svc)) {
logg(LOGG_INFO, "[service] SetServiceStatus() failed with %d\n", GetLastError());
exit(1);
}
}
int svc_checkpoint(const char *type, const char *name, unsigned int custom, void *context)
{
if (svc.dwCurrentState == SERVICE_START_PENDING) {
svc.dwCheckPoint++;
if ((svc.dwCheckPoint % checkpoint_every) == 0)
SetServiceStatus(svc_handle, &svc);
}
return 0;
}
void WINAPI ServiceCtrlHandler(DWORD code)
{
switch (code) {
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
svc.dwCurrentState = SERVICE_STOPPED;
svc.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
SetServiceStatus(svc_handle, &svc);
return;
case SERVICE_CONTROL_INTERROGATE:
break;
}
SetServiceStatus(svc_handle, &svc);
}
BOOL WINAPI cw_stop_ctrl_handler(DWORD CtrlType)
{
if (CtrlType == CTRL_C_EVENT) {
SetConsoleCtrlHandler(cw_stop_ctrl_handler, FALSE);
fprintf(stderr, "Control+C pressed, aborting...\n");
exit(0);
}
return TRUE;
}
void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv)
{
svc.dwServiceType = SERVICE_WIN32;
svc.dwCurrentState = SERVICE_START_PENDING;
svc.dwControlsAccepted = 0;
svc.dwWin32ExitCode = NO_ERROR;
svc.dwServiceSpecificExitCode = 0;
svc.dwCheckPoint = 0;
svc.dwWaitHint = 0;
if (!(svc_handle = RegisterServiceCtrlHandlerA(DT->lpServiceName, ServiceCtrlHandler))) {
logg(LOGG_INFO, "[service] RegisterServiceCtrlHandler() failed with %d\n", GetLastError());
exit(1);
}
if (!SetServiceStatus(svc_handle, &svc)) {
logg(LOGG_INFO, "[service] SetServiceStatus() failed with %d\n", GetLastError());
exit(1);
}
SetEvent(evStart);
WaitForSingleObject(DispatcherThread, INFINITE);
cw_stop_ctrl_handler(CTRL_C_EVENT);
}

38
clamav/common/service.h Normal file
View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2021-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2008-2010 Gianluigi Tiesi <sherpya@netfarm.it>
*
* Authors: Gianluigi Tiesi
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#ifndef __SERVICE_H
#define __SERVICE_H
#include <platform.h>
#include <winsvc.h>
int svc_uninstall(const char *name, int verbose);
int svc_install(const char *name, const char *dname, const char *desc);
static void svc_getcpvalue(const char *name);
void svc_register(const char *name);
void svc_ready(void);
int svc_checkpoint(const char *type, const char *name, unsigned int custom, void *context);
void WINAPI ServiceCtrlHandler(DWORD code);
BOOL WINAPI cw_stop_ctrl_handler(DWORD CtrlType);
void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv);
#endif

View File

@ -0,0 +1,172 @@
/*
* OpenSSL certificate verification for Windows.
*
* Copyright (C) 2019-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*
* Authors: Micah Snyder
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <Windows.h>
#include <wincrypt.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <curl/curl.h>
#include "output.h"
#include "cert_util.h"
#include "cert_util_internal.h"
cl_error_t cert_store_load(X509 **trusted_certs, size_t trusted_cert_count)
{
uint32_t numCertificatesFound = 0;
DWORD lastError;
HCERTSTORE hStore = NULL;
PCCERT_CONTEXT pWinCertContext = NULL;
X509 *x509 = NULL;
cl_error_t ret = CL_EOPEN;
int pt_err;
cert_store_t *store = NULL;
bool locked = false;
hStore = CertOpenSystemStoreA(NULL, "ROOT");
if (NULL == hStore) {
mprintf(LOGG_ERROR, "Failed to open system certificate store.\n");
goto done;
}
store = cert_store_get_int();
if (!store) {
mprintf(LOGG_ERROR, "Failed to retrieve cert store\n");
goto done;
}
pt_err = pthread_mutex_lock(&store->mutex);
if (pt_err) {
errno = pt_err;
mprintf(LOGG_ERROR, "Mutex lock failed\n");
}
locked = true;
if (store->loaded) {
mprintf(LOGG_INFO, "Cert store already loaded\n");
ret = CL_SUCCESS;
goto done;
}
store->system_certs.count = 0;
store->system_certs.certificates = NULL;
while (NULL != (pWinCertContext = CertEnumCertificatesInStore(hStore, pWinCertContext))) {
int addCertResult = 0;
const unsigned char *encoded_cert = pWinCertContext->pbCertEncoded;
x509 = NULL;
x509 = d2i_X509(NULL, &encoded_cert, pWinCertContext->cbCertEncoded);
if (NULL == x509) {
mprintf(LOGG_ERROR, "Failed to convert system certificate to x509.\n");
continue;
}
store->system_certs.certificates = realloc(
store->system_certs.certificates,
(numCertificatesFound + 1) * sizeof(*store->system_certs.certificates));
if (store->system_certs.certificates == NULL) {
mprintf(LOGG_ERROR, "Failed to reserve memory for system cert list\n");
goto done;
}
store->system_certs.certificates[store->system_certs.count++] = x509;
if (mprintf_verbose) {
char *issuer = NULL;
size_t issuerLen = 0;
issuerLen = CertGetNameStringA(pWinCertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, NULL, 0);
issuer = malloc(issuerLen);
if (NULL == issuer) {
mprintf(LOGG_ERROR, "Failed to allocate memory for certificate name.\n");
ret = CURLE_OUT_OF_MEMORY;
goto done;
}
if (0 == CertGetNameStringA(pWinCertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, issuer, issuerLen)) {
mprintf(LOGG_ERROR, "Failed to get friendly display name for certificate.\n");
} else {
mprintf(LOGG_INFO, "Certificate loaded from Windows certificate store: %s\n", issuer);
}
free(issuer);
}
numCertificatesFound++;
}
lastError = GetLastError();
switch (lastError) {
case E_INVALIDARG:
mprintf(LOGG_ERROR, "The handle in the hCertStore parameter is not the same as that in the certificate context pointed to by pPrevCertContext.\n");
break;
case CRYPT_E_NOT_FOUND:
case ERROR_NO_MORE_FILES:
if (0 == numCertificatesFound) {
mprintf(LOGG_ERROR, "No certificates were found.\n");
}
break;
default:
mprintf(LOGG_ERROR, "Unexpected error code from CertEnumCertificatesInStore()\n");
}
if (trusted_certs && trusted_cert_count > 0) {
if (cert_store_set_trusted_int(trusted_certs, trusted_cert_count) == 0) {
mprintf(LOGG_DEBUG, "Trusted certificates loaded: %zu\n",
store->trusted_certs.count);
} else {
mprintf(LOGG_WARNING, "Continuing without trusted certificates\n");
/* proceed as if we succeeded using only certificates from the
* system */
}
}
store->loaded = true;
ret = CL_SUCCESS;
done:
if (locked) {
pt_err = pthread_mutex_unlock(&store->mutex);
if (pt_err) {
errno = pt_err;
mprintf(LOGG_ERROR, "Mutex unlock failed\n");
}
locked = false;
}
if (NULL != pWinCertContext) {
CertFreeCertificateContext(pWinCertContext);
}
if (NULL != hStore) {
CertCloseStore(hStore, 0);
}
return ret;
}

View File

@ -0,0 +1,64 @@
# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
if(WIN32)
add_definitions(-DWIN32_LEAN_AND_MEAN)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE)
# Windows compatibility headers
include_directories(${CMAKE_SOURCE_DIR}/win32/compat)
endif()
# The freshclam executable.
add_executable( freshclam-bin )
target_sources( freshclam-bin
PRIVATE
freshclam.c
execute.c
execute.h
notify.c
notify.h )
if(WIN32)
target_sources( freshclam-bin
PRIVATE
${CMAKE_SOURCE_DIR}/win32/res/freshclam.rc
${CMAKE_SOURCE_DIR}/win32/res/clam.manifest )
endif()
set_target_properties( freshclam-bin PROPERTIES COMPILE_FLAGS "${WARNCFLAGS}" )
if (APPLE AND CLAMAV_SIGN_FILE)
set_target_properties( freshclam-bin PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ${CODE_SIGN_IDENTITY}
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${DEVELOPMENT_TEAM_ID}
)
endif()
target_link_libraries(freshclam-bin
PRIVATE
ClamAV::libfreshclam
ClamAV::libclamav
ClamAV::common )
if(WIN32)
install(TARGETS freshclam-bin DESTINATION . COMPONENT programs)
install(FILES $<TARGET_PDB_FILE:freshclam-bin> DESTINATION . OPTIONAL COMPONENT programs)
else()
install(TARGETS freshclam-bin DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT programs)
endif()
# Install an empty database directory
INSTALL(CODE "FILE(MAKE_DIRECTORY \${ENV}\${CMAKE_INSTALL_PREFIX}/\${DATABASE_DIRECTORY})" COMPONENT programs)
# Now we rename freshclam-bin executable to freshclam using target properties
set_target_properties( freshclam-bin
PROPERTIES OUTPUT_NAME freshclam )
if(SYSTEMD_FOUND)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/clamav-freshclam.service.in
${CMAKE_CURRENT_BINARY_DIR}/clamav-freshclam.service @ONLY)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/clamav-freshclam.service
DESTINATION ${SYSTEMD_UNIT_DIR}
COMPONENT programs)
endif()

21
clamav/freshclam/Doxyfile Normal file
View File

@ -0,0 +1,21 @@
PROJECT_NAME = ClamAV - FreshClam
OUTPUT_DIRECTORY = ../docs/freshclam
WARNINGS = YES
FILE_PATTERNS = *.c *.h
PERL_PATH = /usr/bin/perl
SEARCHENGINE = YES
GENERATE_LATEX=NO
OPTIMIZE_OUTPUT_FOR_C=YES
HAVE_DOT=YES
CALL_GRAPH=YES
CALLER_GRAPH=YES
JAVADOC_AUTOBRIEF=YES
GENERATE_MAN=NO
EXAMPLE_PATH=examples
DOT_CLEANUP=NO
MAX_DOT_GRAPH_DEPTH=3
EXTRACT_ALL=YES
INPUT = .

View File

@ -0,0 +1,13 @@
[Unit]
Description=ClamAV virus database updater
Documentation=man:freshclam(1) man:freshclam.conf(5) https://docs.clamav.net/
# If user wants it run from cron, don't start the daemon.
ConditionPathExists=!/etc/cron.d/clamav-freshclam
Wants=network-online.target
After=network-online.target
[Service]
ExecStart=@prefix@/bin/freshclam -d --foreground=true
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,79 @@
/*
* By Per Jessen <per@computer.org> with changes by the ClamAV team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>
#include <errno.h>
#include "output.h"
#include "optparser.h"
#include "execute.h"
#define MAX_CHILDREN 5
int g_active_children;
void execute(const char *type, const char *text, int bDaemonized)
{
int ret;
if (!bDaemonized) {
if (sscanf(text, "EXIT_%d", &ret) == 1) {
logg(LOGG_DEBUG, "%s: EXIT_%d\n", type, ret);
exit(ret);
}
if (system(text) == -1)
logg(LOGG_INFO, "%s: system(%s) failed\n", type, text);
return;
}
#ifdef _WIN32
if (system(text) == -1) {
logg(LOGG_WARNING, "%s: couldn't execute \"%s\".\n", type, text);
return;
}
#else
if (g_active_children < MAX_CHILDREN) {
pid_t pid;
switch (pid = fork()) {
case 0:
if (-1 == system(text)) {
logg(LOGG_WARNING, "%s: couldn't execute \"%s\".\n", type, text);
}
exit(0);
case -1:
logg(LOGG_WARNING, "%s::fork() failed, %s.\n", type, strerror(errno));
break;
default:
g_active_children++;
}
} else {
logg(LOGG_WARNING, "%s: already %d processes active.\n", type, g_active_children);
}
#endif
}

2114
clamav/freshclam/freshclam.c Normal file

File diff suppressed because it is too large Load Diff

176
clamav/freshclam/notify.c Normal file
View File

@ -0,0 +1,176 @@
/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2002-2013 Sourcefire, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/types.h>
#ifndef _WIN32
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif
#include <string.h>
#include <errno.h>
#include "optparser.h"
#include "output.h"
#include "clamdcom.h"
#include "notify.h"
int clamd_connect(const char *cfgfile, const char *option)
{
#ifndef _WIN32
struct sockaddr_un server;
#endif
struct addrinfo hints, *res, *p;
char port[6];
int ret;
struct optstruct *opts;
const struct optstruct *opt;
int sockd;
if ((opts = optparse(cfgfile, 0, NULL, 1, OPT_CLAMD, 0, NULL)) == NULL) {
logg(LOGG_ERROR, "%s: Can't find or parse configuration file %s\n", option,
cfgfile);
return -11;
}
#ifndef _WIN32
if ((opt = optget(opts, "LocalSocket"))->enabled) {
memset(&server, 0x00, sizeof(server));
server.sun_family = AF_UNIX;
strncpy(server.sun_path, opt->strarg, sizeof(server.sun_path));
server.sun_path[sizeof(server.sun_path) - 1] = '\0';
if ((sockd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
logg(LOGG_WARNING, "Clamd was NOT notified: Can't create socket endpoint for %s: %s\n",
opt->strarg, strerror(errno));
optfree(opts);
return -1;
}
if (connect(sockd, (struct sockaddr *)&server,
sizeof(struct sockaddr_un)) < 0) {
logg(LOGG_WARNING, "Clamd was NOT notified: Can't connect to clamd through %s: %s\n",
opt->strarg, strerror(errno));
closesocket(sockd);
optfree(opts);
return -11;
}
return sockd;
} else
#endif
if ((opt = optget(opts, "TCPSocket"))->enabled) {
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
snprintf(port, sizeof(port), "%u", (unsigned int)opt->numarg);
port[5] = 0;
opt = optget(opts, "TCPAddr");
while (opt) {
ret = getaddrinfo(opt->strarg, port, &hints, &res);
if (ret) {
logg(LOGG_ERROR, "%s: Can't resolve hostname %s (%s)\n", option,
opt->strarg ? opt->strarg : "",
(ret ==
EAI_SYSTEM)
? strerror(errno)
: gai_strerror(ret));
opt = opt->nextarg;
continue;
}
for (p = res; p != NULL; p = p->ai_next) {
if ((sockd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) {
logg(LOGG_ERROR, "%s: Can't create TCP socket to connect to %s: %s\n",
option, opt->strarg ? opt->strarg : "localhost", strerror(errno));
continue;
}
if (connect(sockd, p->ai_addr, p->ai_addrlen) == -1) {
logg(LOGG_ERROR, "%s: Can't connect to clamd on %s:%s: %s\n", option,
opt->strarg ? opt->strarg : "localhost", port, strerror(errno));
closesocket(sockd);
continue;
}
optfree(opts);
freeaddrinfo(res);
return sockd;
}
freeaddrinfo(res);
opt = opt->nextarg;
}
} else {
logg(LOGG_ERROR, "%s: No communication socket specified in %s\n", option,
cfgfile);
optfree(opts);
return 1;
}
optfree(opts);
return -1;
}
int notify(const char *cfgfile)
{
char buff[20];
int sockd, bread;
if ((sockd = clamd_connect(cfgfile, "NotifyClamd")) < 0)
return 1;
if (sendln(sockd, "RELOAD", 7) < 0) {
logg(LOGG_ERROR, "NotifyClamd: Could not write to clamd socket: %s\n", strerror(errno));
closesocket(sockd);
return 1;
}
memset(buff, 0, sizeof(buff));
if ((bread = recv(sockd, buff, sizeof(buff), 0)) > 0) {
if (!strstr(buff, "RELOADING")) {
logg(LOGG_ERROR, "NotifyClamd: Unknown answer from clamd: '%s'\n", buff);
closesocket(sockd);
return -1;
}
}
closesocket(sockd);
logg(LOGG_INFO, "Clamd successfully notified about the update.\n");
return 0;
}

View File

@ -0,0 +1,339 @@
diff -wu lzma_orig//7zCrc.c 7z/7zCrc.c
--- lzma_orig//7zCrc.c 2011-06-18 01:48:31.343602685 +0200
+++ 7z/7zCrc.c 2011-06-18 01:27:02.221109100 +0200
@@ -4,24 +4,12 @@
#include "7zCrc.h"
#include "CpuArch.h"
-#define kCrcPoly 0xEDB88320
+const UInt32 g_CrcTable[256] = { 0x0, 0x77073096, 0xee0e612c, 0x990951ba, 0x76dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0xedb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x9b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x1db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x6b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0xf00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x86d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x3b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x4db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0xd6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0xa00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x26d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x5005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0xcb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0xbdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d };
-#ifdef MY_CPU_LE
-#define CRC_NUM_TABLES 8
-#else
-#define CRC_NUM_TABLES 1
-#endif
-typedef UInt32 (MY_FAST_CALL *CRC_FUNC)(UInt32 v, const void *data, size_t size, const UInt32 *table);
+#define CRC_UPDATE_BYTE_2(crc, b) (g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8))
-static CRC_FUNC g_CrcUpdate;
-UInt32 g_CrcTable[256 * CRC_NUM_TABLES];
-
-#if CRC_NUM_TABLES == 1
-
-#define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8))
-
-static UInt32 MY_FAST_CALL CrcUpdateT1(UInt32 v, const void *data, size_t size, const UInt32 *table)
+UInt32 MY_FAST_CALL CrcUpdate(UInt32 v, const void *data, size_t size)
{
const Byte *p = (const Byte *)data;
for (; size > 0; size--, p++)
@@ -29,46 +17,7 @@
return v;
}
-#else
-
-UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table);
-UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table);
-
-#endif
-
-UInt32 MY_FAST_CALL CrcUpdate(UInt32 v, const void *data, size_t size)
-{
- return g_CrcUpdate(v, data, size, g_CrcTable);
-}
-
UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size)
{
- return g_CrcUpdate(CRC_INIT_VAL, data, size, g_CrcTable) ^ CRC_INIT_VAL;
-}
-
-void MY_FAST_CALL CrcGenerateTable()
-{
- UInt32 i;
- for (i = 0; i < 256; i++)
- {
- UInt32 r = i;
- unsigned j;
- for (j = 0; j < 8; j++)
- r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1));
- g_CrcTable[i] = r;
- }
- #if CRC_NUM_TABLES == 1
- g_CrcUpdate = CrcUpdateT1;
- #else
- for (; i < 256 * CRC_NUM_TABLES; i++)
- {
- UInt32 r = g_CrcTable[i - 256];
- g_CrcTable[i] = g_CrcTable[r & 0xFF] ^ (r >> 8);
- }
- g_CrcUpdate = CrcUpdateT4;
- #ifdef MY_CPU_X86_OR_AMD64
- if (!CPU_Is_InOrder())
- g_CrcUpdate = CrcUpdateT8;
- #endif
- #endif
+ return CrcUpdate(CRC_INIT_VAL, data, size) ^ CRC_INIT_VAL;
}
diff -wu lzma_orig//7zCrc.h 7z/7zCrc.h
--- lzma_orig//7zCrc.h 2011-06-18 01:48:31.343602685 +0200
+++ 7z/7zCrc.h 2011-06-18 01:27:02.229109090 +0200
@@ -8,14 +8,7 @@
EXTERN_C_BEGIN
-extern UInt32 g_CrcTable[];
-
-/* Call CrcGenerateTable one time before other CRC functions */
-void MY_FAST_CALL CrcGenerateTable(void);
-
#define CRC_INIT_VAL 0xFFFFFFFF
-#define CRC_GET_DIGEST(crc) ((crc) ^ CRC_INIT_VAL)
-#define CRC_UPDATE_BYTE(crc, b) (g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8))
UInt32 MY_FAST_CALL CrcUpdate(UInt32 crc, const void *data, size_t size);
UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size);
diff -wu lzma_orig//7zDec.c 7z/7zDec.c
--- lzma_orig//7zDec.c 2011-06-18 01:48:31.347602680 +0200
+++ 7z/7zDec.c 2011-06-18 01:27:02.229109090 +0200
@@ -3,7 +3,7 @@
#include <string.h>
-/* #define _7ZIP_PPMD_SUPPPORT */
+#define _7ZIP_PPMD_SUPPPORT
#include "7z.h"
diff -wu lzma_orig//7zFile.c 7z/7zFile.c
--- lzma_orig//7zFile.h 2011-06-18 01:48:31.347602680 +0200
+++ 7z/7zFile.h 2013-08-13 14:29:34.193054251 +0200
@@ -207,32 +207,6 @@
#endif
}
-WRes File_GetLength(CSzFile *p, UInt64 *length)
-{
- #ifdef USE_WINDOWS_FILE
-
- DWORD sizeHigh;
- DWORD sizeLow = GetFileSize(p->handle, &sizeHigh);
- if (sizeLow == 0xFFFFFFFF)
- {
- DWORD res = GetLastError();
- if (res != NO_ERROR)
- return res;
- }
- *length = (((UInt64)sizeHigh) << 32) + sizeLow;
- return 0;
-
- #else
-
- long pos = ftell(p->file);
- int res = fseek(p->file, 0, SEEK_END);
- *length = ftell(p->file);
- fseek(p->file, pos, SEEK_SET);
- return res;
-
- #endif
-}
-
/* ---------- FileSeqInStream ---------- */
diff -wu lzma_orig//7zFile.h 7z/7zFile.h
--- lzma_orig//7zFile.h 2011-06-18 01:48:31.347602680 +0200
+++ 7z/7zFile.h 2013-08-13 14:27:14.856436384 +0200
@@ -27,6 +27,7 @@
#else
FILE *file;
#endif
+ fmap_t *fmap;
} CSzFile;
void File_Construct(CSzFile *p);
@@ -48,7 +48,6 @@
WRes File_Write(CSzFile *p, const void *data, size_t *size);
WRes File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin);
-WRes File_GetLength(CSzFile *p, UInt64 *length);
/* ---------- FileInStream ---------- */
diff -wu lzma_orig//CpuArch.h 7z/CpuArch.h
--- lzma_orig//CpuArch.h 2011-06-18 01:48:31.363602662 +0200
+++ 7z/CpuArch.h 2011-06-18 01:27:02.229109090 +0200
@@ -5,6 +5,7 @@
#define __CPU_ARCH_H
#include "Types.h"
+#include "others.h"
EXTERN_C_BEGIN
@@ -16,68 +17,8 @@
If MY_CPU_LE_UNALIGN is not defined, we don't know about these properties of platform.
*/
-#if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__)
-#define MY_CPU_AMD64
-#endif
-
-#if defined(MY_CPU_AMD64) || defined(_M_IA64)
-#define MY_CPU_64BIT
-#endif
-
-#if defined(_M_IX86) || defined(__i386__)
-#define MY_CPU_X86
-#endif
-
-#if defined(MY_CPU_X86) || defined(MY_CPU_AMD64)
-#define MY_CPU_X86_OR_AMD64
-#endif
-
-#if defined(MY_CPU_X86) || defined(_M_ARM)
-#define MY_CPU_32BIT
-#endif
-
-#if defined(_WIN32) && defined(_M_ARM)
-#define MY_CPU_ARM_LE
-#endif
-
-#if defined(_WIN32) && defined(_M_IA64)
-#define MY_CPU_IA64_LE
-#endif
-
-#if defined(MY_CPU_X86_OR_AMD64)
-#define MY_CPU_LE_UNALIGN
-#endif
-
-#if defined(MY_CPU_X86_OR_AMD64) || defined(MY_CPU_ARM_LE) || defined(MY_CPU_IA64_LE) || defined(__ARMEL__) || defined(__MIPSEL__) || defined(__LITTLE_ENDIAN__)
-#define MY_CPU_LE
-#endif
-
-#if defined(__BIG_ENDIAN__)
-#define MY_CPU_BE
-#endif
-
-#if defined(MY_CPU_LE) && defined(MY_CPU_BE)
-Stop_Compiling_Bad_Endian
-#endif
-
-#ifdef MY_CPU_LE_UNALIGN
-
-#define GetUi16(p) (*(const UInt16 *)(p))
-#define GetUi32(p) (*(const UInt32 *)(p))
-#define GetUi64(p) (*(const UInt64 *)(p))
-#define SetUi16(p, d) *(UInt16 *)(p) = (d);
-#define SetUi32(p, d) *(UInt32 *)(p) = (d);
-#define SetUi64(p, d) *(UInt64 *)(p) = (d);
-
-#else
-
-#define GetUi16(p) (((const Byte *)(p))[0] | ((UInt16)((const Byte *)(p))[1] << 8))
-
-#define GetUi32(p) ( \
- ((const Byte *)(p))[0] | \
- ((UInt32)((const Byte *)(p))[1] << 8) | \
- ((UInt32)((const Byte *)(p))[2] << 16) | \
- ((UInt32)((const Byte *)(p))[3] << 24))
+#define GetUi16(p) (cli_readint16(p))
+#define GetUi32(p) (cli_readint32(p))
#define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32))
@@ -85,71 +26,12 @@
((Byte *)(p))[0] = (Byte)_x_; \
((Byte *)(p))[1] = (Byte)(_x_ >> 8); }
-#define SetUi32(p, d) { UInt32 _x_ = (d); \
- ((Byte *)(p))[0] = (Byte)_x_; \
- ((Byte *)(p))[1] = (Byte)(_x_ >> 8); \
- ((Byte *)(p))[2] = (Byte)(_x_ >> 16); \
- ((Byte *)(p))[3] = (Byte)(_x_ >> 24); }
+#define SetUi32(p, d) (cli_writeint32(p, d))
#define SetUi64(p, d) { UInt64 _x64_ = (d); \
SetUi32(p, (UInt32)_x64_); \
SetUi32(((Byte *)(p)) + 4, (UInt32)(_x64_ >> 32)); }
-#endif
-
-#if defined(MY_CPU_LE_UNALIGN) && defined(_WIN64) && (_MSC_VER >= 1300)
-
-#pragma intrinsic(_byteswap_ulong)
-#pragma intrinsic(_byteswap_uint64)
-#define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p))
-#define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p))
-
-#else
-
-#define GetBe32(p) ( \
- ((UInt32)((const Byte *)(p))[0] << 24) | \
- ((UInt32)((const Byte *)(p))[1] << 16) | \
- ((UInt32)((const Byte *)(p))[2] << 8) | \
- ((const Byte *)(p))[3] )
-
-#define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4))
-
-#endif
-
-#define GetBe16(p) (((UInt16)((const Byte *)(p))[0] << 8) | ((const Byte *)(p))[1])
-
-
-#ifdef MY_CPU_X86_OR_AMD64
-
-typedef struct
-{
- UInt32 maxFunc;
- UInt32 vendor[3];
- UInt32 ver;
- UInt32 b;
- UInt32 c;
- UInt32 d;
-} Cx86cpuid;
-
-enum
-{
- CPU_FIRM_INTEL,
- CPU_FIRM_AMD,
- CPU_FIRM_VIA
-};
-
-Bool x86cpuid_CheckAndRead(Cx86cpuid *p);
-int x86cpuid_GetFirm(const Cx86cpuid *p);
-
-#define x86cpuid_GetFamily(p) (((p)->ver >> 8) & 0xFF00F)
-#define x86cpuid_GetModel(p) (((p)->ver >> 4) & 0xF00F)
-#define x86cpuid_GetStepping(p) ((p)->ver & 0xF)
-
-Bool CPU_Is_InOrder();
-Bool CPU_Is_Aes_Supported();
-
-#endif
-
EXTERN_C_END
#endif
diff -wu lzma_orig//Types.h 7z/Types.h
--- lzma_orig//Types.h 2011-06-18 01:48:31.379602643 +0200
+++ 7z/Types.h 2011-06-18 01:27:02.229109090 +0200
@@ -21,7 +21,7 @@
#endif
EXTERN_C_BEGIN
-
+#include "fmap.h"
#define SZ_OK 0
#define SZ_ERROR_DATA 1
@@ -164,6 +164,7 @@
{
SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */
SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin);
+ off_t curpos;
} ISeekInStream;
typedef struct

222
clamav/libclamav/7z_iface.c Normal file
View File

@ -0,0 +1,222 @@
/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2011-2013 Sourcefire, Inc.
*
* Authors: aCaB
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#if defined(_WIN32)
#include <WinSock2.h>
#include <Windows.h>
#endif
#include "clamav.h"
#include "7z_iface.h"
#include "lzma_iface.h"
#include "scanners.h"
#include "others.h"
#include "fmap.h"
#include "7z/7z.h"
#include "7z/7zAlloc.h"
#include "7z/7zFile.h"
static ISzAlloc allocImp = {__lzma_wrap_alloc, __lzma_wrap_free}, allocTempImp = {__lzma_wrap_alloc, __lzma_wrap_free};
static SRes FileInStream_fmap_Read(void *pp, void *buf, size_t *size)
{
CFileInStream *p = (CFileInStream *)pp;
size_t read_sz;
if (*size == 0)
return 0;
read_sz = fmap_readn(p->file.fmap, buf, p->s.curpos, *size);
if (read_sz == (size_t)-1) {
*size = 0;
return SZ_ERROR_READ;
}
p->s.curpos += read_sz;
*size = read_sz;
return SZ_OK;
}
static SRes FileInStream_fmap_Seek(void *pp, Int64 *pos, ESzSeek origin)
{
CFileInStream *p = (CFileInStream *)pp;
switch (origin) {
case SZ_SEEK_SET:
p->s.curpos = *pos;
break;
case SZ_SEEK_CUR:
p->s.curpos += *pos;
*pos = p->s.curpos;
break;
case SZ_SEEK_END:
p->s.curpos = p->file.fmap->len + *pos;
*pos = p->s.curpos;
break;
default:
return 1;
}
return 0;
}
#define UTFBUFSZ 256
int cli_7unz(cli_ctx *ctx, size_t offset)
{
CFileInStream archiveStream;
CLookToRead lookStream;
CSzArEx db;
SRes res;
UInt16 utf16buf[UTFBUFSZ], *utf16name = utf16buf;
int namelen = UTFBUFSZ;
cl_error_t found = CL_CLEAN;
Int64 begin_of_archive = offset;
/* Replacement for
FileInStream_CreateVTable(&archiveStream); */
archiveStream.s.Read = FileInStream_fmap_Read;
archiveStream.s.Seek = FileInStream_fmap_Seek;
archiveStream.s.curpos = 0;
archiveStream.file.fmap = ctx->fmap;
LookToRead_CreateVTable(&lookStream, False);
if (archiveStream.s.Seek(&archiveStream.s, &begin_of_archive, SZ_SEEK_SET) != 0)
return CL_CLEAN;
lookStream.realStream = &archiveStream.s;
LookToRead_Init(&lookStream);
SzArEx_Init(&db);
res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);
if (res == SZ_ERROR_ENCRYPTED && SCAN_HEURISTIC_ENCRYPTED_ARCHIVE) {
cli_dbgmsg("cli_7unz: Encrypted header found in archive.\n");
found = cli_append_potentially_unwanted(ctx, "Heuristics.Encrypted.7Zip");
} else if (res == SZ_OK) {
UInt32 i, blockIndex = 0xFFFFFFFF;
Byte *outBuffer = 0;
size_t outBufferSize = 0;
unsigned int encrypted = 0;
for (i = 0; i < db.db.NumFiles; i++) {
size_t offset = 0;
size_t outSizeProcessed = 0;
const CSzFileItem *f = db.db.Files + i;
char *name;
char *tmp_name;
size_t j;
int newnamelen, fd;
// abort if we would exceed max files or max scan time.
if ((found = cli_checklimits("7unz", ctx, 0, 0, 0)))
break;
if (f->IsDir)
continue;
// skip this file if we would exceed max file size or max scan size. (we already checked for the max files and max scan time)
if (cli_checklimits("7unz", ctx, f->Size, 0, 0))
continue;
if (!db.FileNameOffsets)
newnamelen = 0; /* no filename */
else {
newnamelen = SzArEx_GetFileNameUtf16(&db, i, NULL);
if (newnamelen > namelen) {
if (namelen > UTFBUFSZ)
free(utf16name);
utf16name = cli_malloc(newnamelen * 2);
if (!utf16name) {
found = CL_EMEM;
break;
}
namelen = newnamelen;
}
SzArEx_GetFileNameUtf16(&db, i, utf16name);
}
name = (char *)utf16name;
for (j = 0; j < (size_t)newnamelen; j++) /* FIXME */
name[j] = utf16name[j];
name[j] = 0;
cli_dbgmsg("cli_7unz: extracting %s\n", name);
res = SzArEx_Extract(&db, &lookStream.s, i, &blockIndex, &outBuffer, &outBufferSize, &offset, &outSizeProcessed, &allocImp, &allocTempImp);
if (res == SZ_ERROR_ENCRYPTED) {
encrypted = 1;
if (SCAN_HEURISTIC_ENCRYPTED_ARCHIVE) {
cli_dbgmsg("cli_7unz: Encrypted files found in archive.\n");
found = cli_append_potentially_unwanted(ctx, "Heuristics.Encrypted.7Zip");
if (found != CL_SUCCESS) {
break;
}
}
}
if (CL_VIRUS == cli_matchmeta(ctx, name, 0, f->Size, encrypted, i, f->CrcDefined ? f->Crc : 0, NULL)) {
found = CL_VIRUS;
break;
}
if (res != SZ_OK)
cli_dbgmsg("cli_unz: extraction failed with %d\n", res);
else if ((outBuffer == NULL) || (outSizeProcessed == 0)) {
cli_dbgmsg("cli_unz: extracted empty file\n");
} else {
if ((found = cli_gentempfd(ctx->sub_tmpdir, &tmp_name, &fd)))
break;
cli_dbgmsg("cli_7unz: Saving to %s\n", tmp_name);
if (cli_writen(fd, outBuffer + offset, outSizeProcessed) != outSizeProcessed) {
found = CL_EWRITE;
}
found = cli_magic_scan_desc(fd, tmp_name, ctx, name, LAYER_ATTRIBUTES_NONE);
close(fd);
if (!ctx->engine->keeptmp && cli_unlink(tmp_name))
found = CL_EUNLINK;
free(tmp_name);
if (found != CL_SUCCESS)
break;
}
}
IAlloc_Free(&allocImp, outBuffer);
}
SzArEx_Free(&db, &allocImp);
if (namelen > UTFBUFSZ)
free(utf16name);
if (res == SZ_OK)
cli_dbgmsg("cli_7unz: completed successfully\n");
else if (res == SZ_ERROR_UNSUPPORTED)
cli_dbgmsg("cli_7unz: unsupported\n");
else if (res == SZ_ERROR_MEM)
cli_dbgmsg("cli_7unz: oom\n");
else if (res == SZ_ERROR_CRC)
cli_dbgmsg("cli_7unz: crc mismatch\n");
else if (res == SZ_ERROR_ENCRYPTED)
cli_dbgmsg("cli_7unz: encrypted\n");
else
cli_dbgmsg("cli_7unz: error %d\n", res);
return found;
}

Some files were not shown because too many files have changed in this diff Show More