Compare commits
46 Commits
Author | SHA1 | Date | |
---|---|---|---|
f2ac486d24 | |||
ab10389228 | |||
99893d8c3d | |||
6f25fb3672 | |||
967922012c | |||
d8f7c47faa | |||
4aa0d3eac7 | |||
0b5e40d5c4 | |||
d1ae9fc12c | |||
8d613b3e54 | |||
3aa4d63799 | |||
130dad7ffa | |||
fa2ea1f83c | |||
2722e9b2e1 | |||
f7f7d7136a | |||
00567557ba | |||
778c9d5fff | |||
b5bd70ec71 | |||
950bd28f60 | |||
bc29a0b1a9 | |||
a8fb2a8dd3 | |||
ed3fb48ce2 | |||
bcb5a7fb5b | |||
c949f3c04b | |||
315a368012 | |||
ce349a46a6 | |||
bc6e6b253c | |||
17f06235df | |||
bd6793499f | |||
2af92f8144 | |||
333f0ff2d2 | |||
da8473a3ec | |||
5f1c1af36b | |||
dafc57b769 | |||
72bd738a1d | |||
c2024e15f5 | |||
faccc6426c | |||
be6b92fc60 | |||
7478732ed8 | |||
e424cc1c04 | |||
45fe15f472 | |||
b879ee0b2e | |||
5aa7e4aa06 | |||
ce37ef75d6 | |||
a47d429273 | |||
13c1d09385 |
11
.vscode/launch.json
vendored
Normal file
11
.vscode/launch.json
vendored
Normal 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
21
.vscode/settings.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
112
Makefile
112
Makefile
@ -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
|
||||
|
||||
$(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
|
||||
|
||||
$(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
|
||||
|
||||
$(CC) $(CFLAGS) $^ -o $(OBG) $(LIB) $(freshclam_LIB)
|
||||
CMAKE = $(shell if test -f /etc/centos-release; then echo "1"; else echo "0"; fi)
|
||||
ifeq ("$(CMAKE)", "1")
|
||||
CMAKE := cmake3
|
||||
else
|
||||
CMAKE := cmake
|
||||
endif
|
||||
|
||||
ARCH := $(shell bash get_architecture.sh)
|
||||
|
||||
|
||||
IPTC_CFLAGS += -DHAVE_CONFIG_H -I./libiptc -D_LARGEFILE_SOURCE=1 -D_LARGE_FILES -D_FILE_OFFSET_BITS=64 -D_REENTRANT
|
||||
IPTC_LIB += -lip4tc
|
||||
|
||||
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)
|
||||
|
||||
|
99
README.md
99
README.md
@ -1,23 +1,27 @@
|
||||
# denyhosts
|
||||
- 拒绝主机&杀毒
|
||||
```
|
||||
|
||||
- 拒绝主机&杀毒
|
||||
|
||||
```text
|
||||
适用系统:
|
||||
Debian 11
|
||||
Debian 11、12
|
||||
Centos 7
|
||||
支持系统病毒扫描
|
||||
支持一次运行检测、后台运行检测
|
||||
支持钉钉告警和邮件告
|
||||
支持第三方QQ邮箱告警
|
||||
```
|
||||
支持第三方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,31 +29,31 @@ 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
|
||||
source /opt/rh/devtoolset-11/enable #临时
|
||||
echo "source /opt/rh/devtoolset-11/enable" >> /etc/profile #永久
|
||||
|
||||
|
||||
mv /etc/cron.d/clamav-update /root
|
||||
sed -i "s/DatabaseMirror .*/DatabaseMirror clamavdb.c3sl.ufpr.br/g" /etc/freshclam.conf
|
||||
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,单位秒)
|
||||
DAEMON = "off"; // on开启后台运行,off不开启
|
||||
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邮箱告警(默认使用gomail:https://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"; // 接收者邮箱
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
79
build.sh
79
build.sh
@ -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 clamavdb.c3sl.ufpr.br/g" /etc/clamav/freshclam.conf
|
||||
sed -i "s/DatabaseMirror .*/DatabaseMirror db.cn.clamav.net/g" /etc/clamav/freshclam.conf
|
||||
else
|
||||
:
|
||||
fi
|
||||
|
||||
|
||||
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 ../../
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
|
||||
tmux new-session -s main -d; tmux send -t main 'killall rhost; ./rhost -d' ENTER
|
||||
|
||||
tmux at -t main
|
||||
|
||||
}
|
||||
|
||||
check_os
|
||||
pkg_install
|
||||
if test "${1}" = "binary"; then
|
||||
binary
|
||||
|
||||
exit 0
|
||||
fi
|
||||
main
|
||||
|
327
clamav/build-libclamav.sh
Normal file
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
617
clamav/clamav-config.h
Normal 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
84
clamav/clamav-types.h
Normal 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
68
clamav/clamav-version.h
Normal 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
208
clamav/clamav_rust.h
Normal 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 */
|
48
clamav/clamscan/CMakeLists.txt
Normal file
48
clamav/clamscan/CMakeLists.txt
Normal 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
21
clamav/clamscan/Doxyfile
Normal 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
346
clamav/clamscan/clamscan.c
Normal 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
1676
clamav/clamscan/manager.c
Normal file
File diff suppressed because it is too large
Load Diff
84
clamav/common/CMakeLists.txt
Normal file
84
clamav/common/CMakeLists.txt
Normal 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
717
clamav/common/actions.c
Normal 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
699
clamav/common/cert_util.c
Normal 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
483
clamav/common/clamdcom.c
Normal 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
67
clamav/common/clamdcom.h
Normal 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
330
clamav/common/exeScanner.c
Normal 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;
|
||||
}
|
72
clamav/common/exeScanner.h
Normal file
72
clamav/common/exeScanner.h
Normal 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
302
clamav/common/getopt.c
Normal 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
65
clamav/common/getopt.h
Normal 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
64
clamav/common/hostid.c
Normal 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);
|
||||
}
|
258
clamav/common/idmef_logging.c
Normal file
258
clamav/common/idmef_logging.c
Normal 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
|
102
clamav/common/linux/cert_util_linux.c
Normal file
102
clamav/common/linux/cert_util_linux.c
Normal 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;
|
||||
}
|
402
clamav/common/mac/cert_util_mac.m
Normal file
402
clamav/common/mac/cert_util_mac.m
Normal 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
489
clamav/common/misc.c
Normal 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(®, 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(®, fname, 0, NULL, 0) == REG_NOMATCH) ? 0 : 1;
|
||||
cli_regfree(®);
|
||||
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
108
clamav/common/misc.h
Normal 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
1483
clamav/common/optparser.c
Normal file
File diff suppressed because it is too large
Load Diff
617
clamav/common/output.c
Normal file
617
clamav/common/output.c
Normal 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
97
clamav/common/output.h
Normal 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
710
clamav/common/scanmem.c
Normal 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
68
clamav/common/scanmem.h
Normal 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
228
clamav/common/service.c
Normal 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
38
clamav/common/service.h
Normal 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
|
172
clamav/common/win/cert_util_win.c
Normal file
172
clamav/common/win/cert_util_win.c
Normal 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;
|
||||
}
|
64
clamav/freshclam/CMakeLists.txt
Normal file
64
clamav/freshclam/CMakeLists.txt
Normal 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
21
clamav/freshclam/Doxyfile
Normal 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 = .
|
13
clamav/freshclam/clamav-freshclam.service.in
Normal file
13
clamav/freshclam/clamav-freshclam.service.in
Normal 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
|
79
clamav/freshclam/execute.c
Normal file
79
clamav/freshclam/execute.c
Normal 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
2114
clamav/freshclam/freshclam.c
Normal file
File diff suppressed because it is too large
Load Diff
176
clamav/freshclam/notify.c
Normal file
176
clamav/freshclam/notify.c
Normal 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;
|
||||
}
|
339
clamav/libclamav/7z/lzma.diff
Normal file
339
clamav/libclamav/7z/lzma.diff
Normal 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
222
clamav/libclamav/7z_iface.c
Normal 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
Loading…
Reference in New Issue
Block a user