概述
可通过clamd配置文件/usr/local/etc/clamd.conf中的配置项VirusEvent进行定义,默认情况下未指定VirusEvent,变量v会替换为发现的病毒名称。
# Execute a command when virus is found. In the command string %v will
# be replaced with the virus name.
# Default: no
#VirusEvent /usr/local/bin/send_sms 123456789 "VIRUS ALERT: %v"
关于VirusEvent的处理位于clamd/clamd_others.c文件中,如下函数virusaction,首先判断配置项VirusEvent是否使能,否则,退出处理。
#define VE_FILENAME "CLAM_VIRUSEVENT_FILENAME"
#define VE_VIRUSNAME "CLAM_VIRUSEVENT_VIRUSNAME"
void virusaction(const char *filename, const char *virname, const struct optstruct *opts)
{
const struct optstruct *opt;
char *buffer_file, *buffer_vir, *buffer_cmd, *path;
const char *pt;
char *env[4];
if (!(opt = optget(opts, "VirusEvent"))->enabled)
return;
接下来,将在环境变量中添加两个值,分别表示检查的文件名称,和病毒名称。键值由以上的宏VE_FILENAME和VE_VIRUSNAME指定。
path = getenv("PATH");
env[0] = path ? strdup(path) : NULL;
j = env[0] ? 1 : 0;
/* Allocate env vars.. to be portable env vars should not be freed */
buffer_file =
(char *)malloc(strlen(VE_FILENAME) + strlen(filename) + 2);
if (buffer_file) {
sprintf(buffer_file, "%s=%s", VE_FILENAME, filename);
env[j++] = buffer_file;
}
buffer_vir =
(char *)malloc(strlen(VE_VIRUSNAME) + strlen(virname) + 2);
if (buffer_vir) {
sprintf(buffer_vir, "%s=%s", VE_VIRUSNAME, virname);
env[j++] = buffer_vir;
}
env[j++] = NULL;
以下,使用病毒名称替换掉VirusEvent配置中指定的%v变量。
pt = opt->strarg;
while ((pt = strstr(pt, "%v"))) {
pt += 2;
v++;
}
len = strlen(opt->strarg);
buffer_cmd = (char *)calloc(len + v * strlen(virname) + 1, sizeof(char));
if (!buffer_cmd) {
if (path)
xfree(env[0]);
xfree(buffer_file);
xfree(buffer_vir);
return;
}
for (i = 0, j = 0; i < len; i++) {
if (i + 1 < len && opt->strarg[i] == '%' && opt->strarg[i + 1] == 'v') {
strcat(buffer_cmd, virname);
j += strlen(virname);
i++;
} else {
buffer_cmd[j++] = opt->strarg[i];
}
}
最后,启动一个新进程执行VirusEvent中指定的命令。
pthread_mutex_lock(&virusaction_lock);
/* We can only call async-signal-safe functions after fork(). */
pid = vfork();
if (pid == 0) { /* child */
_exit(execle("/bin/sh", "sh", "-c", buffer_cmd, NULL, env));
} else if (pid > 0) { /* parent */
pthread_mutex_unlock(&virusaction_lock);
while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) continue;
} else {
pthread_mutex_unlock(&virusaction_lock);
logg("!VirusEvent: fork failed.n");
}
if (path)
xfree(env[0]);
xfree(buffer_cmd);
xfree(buffer_file);
xfree(buffer_vir);
}
对于流检测方式,filename固定为字符串"stream"。
int scanstream( int odesc, unsigned long int *scanned,
const struct cl_engine *engine, struct cl_scan_options *options,
const struct optstruct *opts, char term)
{
...
if (ret == CL_VIRUS) {
if (context.virsize && optget(opts, "ExtendedDetectionInfo")->enabled) {
logg("stream(%s@%u): %s(%s:%llu) FOUNDn", peer_addr, port, virname, context.virhash, context.virsize);
} else {
logg("stream(%s@%u): %s FOUNDn", peer_addr, port, virname);
}
virusaction("stream", virname, opts);
对于文件描述符检测方式(scanfd函数),通过读取/proc/self/fd/目录下,描述符对应的连接文件,获取到文件名称。
cl_error_t cli_get_filepath_from_filedesc(int desc, char **filepath)
{
cl_error_t status = CL_EARG;
char *evaluated_filepath = NULL;
#ifdef __linux__
char fname[PATH_MAX];
char link[32];
memset(&fname, 0, PATH_MAX);
snprintf(link, sizeof(link), "/proc/self/fd/%u", desc);
link[sizeof(link) - 1] = '