更新libclamav库1.0.0版本

This commit is contained in:
2023-01-14 18:28:39 +08:00
parent b879ee0b2e
commit 45fe15f472
8531 changed files with 1222046 additions and 177272 deletions

View File

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

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

@@ -0,0 +1,717 @@
/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2009-2013 Sourcefire, Inc.
*
* Author: aCaB, Micah Snyder
*
* These functions are actions that may be taken when a sample alerts.
* The user may wish to:
* - move file to destination directory.
* - copy file to destination directory.
* - remove (delete) the file.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#ifdef _WIN32
#include <windows.h>
#include <winternl.h>
#endif
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdbool.h>
#include <fcntl.h>
#include <errno.h>
#include <libgen.h>
// libclamav
#include "clamav.h"
#include "str.h"
#include "others.h"
#include "optparser.h"
#include "output.h"
#include "misc.h"
#include "actions.h"
void (*action)(const char *) = NULL;
unsigned int notmoved = 0, notremoved = 0;
static char *actarget;
static int targlen;
static int getdest(const char *fullpath, char **newname)
{
char *tmps, *filename;
int fd, i;
tmps = strdup(fullpath);
if (!tmps) {
*newname = NULL;
return -1;
}
filename = basename(tmps);
if (!(*newname = (char *)malloc(targlen + strlen(filename) + 6))) {
free(tmps);
return -1;
}
sprintf(*newname, "%s" PATHSEP "%s", actarget, filename);
for (i = 1; i < 1000; i++) {
fd = open(*newname, O_WRONLY | O_CREAT | O_EXCL, 0600);
if (fd >= 0) {
free(tmps);
return fd;
}
if (errno != EEXIST) break;
sprintf(*newname, "%s" PATHSEP "%s.%03u", actarget, filename, i);
}
free(tmps);
free(*newname);
*newname = NULL;
return -1;
}
#ifdef _WIN32
typedef LONG (*PNTCF)(
PHANDLE FileHandle, // OUT
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PIO_STATUS_BLOCK IoStatusBlock, // OUT
PLARGE_INTEGER AllocationSize,
ULONG FileAttributes,
ULONG ShareAccess,
ULONG CreateDisposition,
ULONG CreateOptions,
PVOID EaBuffer,
ULONG EaLength);
typedef void (*PRIUS)(
PUNICODE_STRING DestinationString,
PCWSTR SourceString);
/**
* @brief A openat equivalent for Win32 with a check to NOFOLLOW soft-links.
*
* The caller is resposible for closing the HANDLE.
*
* For the desiredAccess, fileAttributes, createOptions, and shareAccess parameters
* see https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
*
* @param current_handle The current handle. If set to NULL, then filename should be a drive letter.
* @param filename The directory to open. If current_handle is valid, should be a directory found in the current directory.
* @param pNtCreateFile A function pointer to the NtCreateFile Win32 Native API.
* @param pRtlInitUnicodeString A function pointer to the RtlInitUnicodeString Win32 Native API.
* @param desiredAccess The DesiredAccess option for NtCreateFile
* @param fileAttributes The FileAttributes option for NtCreateFile
* @param createOptions The CreateOptions option for NtCreateFile
* @param shareAccess The ShareAccess option for NtCreateFile
* @return HANDLE A handle on success, NULL on failure.
*/
static HANDLE win32_openat(
HANDLE current_handle,
const char *filename,
PNTCF pNtCreateFile,
PRIUS pRtlInitUnicodeString,
ACCESS_MASK desiredAccess,
ULONG fileAttributes,
ULONG createOptions,
ULONG shareAccess)
{
HANDLE next_handle = NULL;
LONG ntStatus;
WCHAR *filenameW = NULL;
UNICODE_STRING filenameU;
int cchNextDirectoryName = 0;
IO_STATUS_BLOCK ioStatusBlock = {0};
OBJECT_ATTRIBUTES objAttributes = {0};
FILE_ATTRIBUTE_TAG_INFO tagInfo = {0};
/* Convert filename to a UNICODE_STRING, required by the native API NtCreateFile() */
cchNextDirectoryName = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
filenameW = malloc(cchNextDirectoryName * sizeof(WCHAR));
if (NULL == filenameW) {
logg(LOGG_INFO, "win32_openat: failed to allocate memory for next directory name UTF16LE string\n");
goto done;
}
if (0 == MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, cchNextDirectoryName)) {
logg(LOGG_INFO, "win32_openat: failed to allocate buffer for unicode version of intermediate directory name.\n");
goto done;
}
pRtlInitUnicodeString(&filenameU, filenameW);
InitializeObjectAttributes(
&objAttributes, // ObjectAttributes
&filenameU, // ObjectName
OBJ_CASE_INSENSITIVE, // Attributes
current_handle, // Root directory
NULL); // SecurityDescriptor
ntStatus = pNtCreateFile(
&next_handle, // FileHandle
desiredAccess, // DesiredAccess
&objAttributes, // ObjectAttributes
&ioStatusBlock, // [out] status
0, // AllocationSize
fileAttributes, // FileAttributes
shareAccess, // ShareAccess
FILE_OPEN, // CreateDisposition
createOptions, // CreateOptions
NULL, // EaBuffer
0); // EaLength
if (!NT_SUCCESS(ntStatus) || (NULL == next_handle)) {
logg(LOGG_INFO, "win32_openat: Failed to open file '%s'. \nError: 0x%x \nioStatusBlock: 0x%x\n", filename, ntStatus, ioStatusBlock.Information);
goto done;
}
logg(LOGG_DEBUG, "win32_openat: Opened file \"%s\"\n", filename);
if (0 == GetFileInformationByHandleEx(
next_handle, // hFile,
FileAttributeTagInfo, // FileInformationClass
&tagInfo, // lpFileInformation
sizeof(FILE_ATTRIBUTE_TAG_INFO))) { // dwBufferSize
logg(LOGG_INFO, "win32_openat: Failed to get file information by handle '%s'. Error: %d.\n", filename, GetLastError());
CloseHandle(next_handle);
next_handle = NULL;
goto done;
}
logg(LOGG_DEBUG, "win32_openat: tagInfo.FileAttributes: 0x%0x\n", tagInfo.FileAttributes);
logg(LOGG_DEBUG, "win32_openat: tagInfo.ReparseTag: 0x%0x\n", tagInfo.ReparseTag);
if (0 != (tagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
logg(LOGG_INFO, "win32_openat: File is a soft link: '%s' Aborting path traversal.\n\n", filename);
CloseHandle(next_handle);
next_handle = NULL;
goto done;
}
logg(LOGG_DEBUG, "win32_openat: File or directory is not a soft link.\n\n");
done:
if (NULL != filenameW) {
free(filenameW);
}
return next_handle;
}
#endif
/**
* @brief Traverse from root to the specified directory without following symlinks.
*
* The intention is so you can use `unlinkat` or `rename_at` to safely move or
* delete the target directory.
*
* The caller is responsible for closing the output file descriptor if the
* traversal succeeded.
*
* @param directory The directory to traverse to (must be NULL terminated).
* @param want_directory_handle Set to true to get the directory handle containing the file, false to get the file handle.
* @param[out] out_handle An open file descriptor or HANDLE (win32) for the directory.
* @return 0 Traverse succeeded.
* @return -1 Traverse failed.
*/
#ifndef _WIN32
static int traverse_to(const char *directory, bool want_directory_handle, int *out_handle)
#else
static int traverse_to(const char *directory, bool want_directory_handle, HANDLE *out_handle)
#endif
{
int status = -1;
size_t tokens_count;
const char *tokens[PATH_MAX / 2];
size_t i;
char *tokenized_directory = NULL;
#ifndef _WIN32
int current_handle = -1;
int next_handle = -1;
#else
bool bNeedDeleteFileAccess = false;
HMODULE ntdll = NULL;
PNTCF pNtCreateFile = NULL;
PRIUS pRtlInitUnicodeString = NULL;
PHANDLE current_handle = NULL;
PHANDLE next_handle = NULL;
ACCESS_MASK desiredAccess = STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_READ_EA;
ULONG fileAttributes = FILE_ATTRIBUTE_DIRECTORY;
ULONG createOptions = FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT;
ULONG shareAccess = FILE_SHARE_READ;
#endif
if (NULL == directory || NULL == out_handle) {
logg(LOGG_INFO, "traverse_to: Invalid arguments!\n");
goto done;
}
#ifdef _WIN32
ntdll = LoadLibraryA("ntdll.dll");
if (NULL == ntdll) {
logg(LOGG_INFO, "traverse_to: failed to load ntdll!\n");
goto done;
}
pNtCreateFile = (PNTCF)GetProcAddress(ntdll, "NtCreateFile");
if (NULL == pNtCreateFile) {
logg(LOGG_INFO, "traverse_to: failed to get NtCreateFile proc address!\n");
goto done;
}
pRtlInitUnicodeString = (PRIUS)GetProcAddress(ntdll, "RtlInitUnicodeString");
if (NULL == pRtlInitUnicodeString) {
logg(LOGG_INFO, "traverse_to: failed to get pRtlInitUnicodeString proc address!\n");
goto done;
}
#endif
tokenized_directory = strdup(directory);
if (NULL == tokenized_directory) {
logg(LOGG_INFO, "traverse_to: Failed to get copy of directory path to be tokenized!\n");
goto done;
}
tokens_count = cli_strtokenize(tokenized_directory, *PATHSEP, PATH_MAX / 2, tokens);
if (0 == tokens_count) {
logg(LOGG_INFO, "traverse_to: tokenize of target directory returned 0 tokens!\n");
goto done;
}
#ifndef _WIN32
/*
* Open the root(/) directory, because it won't be the first token like a
* drive letter (i.e. "C:") would be on Windows.
*/
current_handle = open("/", O_RDONLY | O_NOFOLLOW);
if (-1 == current_handle) {
logg(LOGG_INFO, "traverse_to: Failed to open file descriptor for '/' directory.\n");
goto done;
}
#endif
if (true == want_directory_handle) {
tokens_count -= 1;
}
if (0 == tokens_count) {
logg(LOGG_INFO, "traverse_to: Failed to get copy of directory path to be tokenized!\n");
goto done;
}
for (i = 0; i < tokens_count; i++) {
if (0 == strlen(tokens[i])) {
/* Empty token, likely first / or double // */
continue;
}
#ifndef _WIN32
next_handle = openat(current_handle, tokens[i], O_RDONLY | O_NOFOLLOW);
if (-1 == next_handle) {
logg(LOGG_INFO, "traverse_to: Failed open %s\n", tokens[i]);
goto done;
}
close(current_handle);
current_handle = next_handle;
next_handle = -1;
#else
if (true != want_directory_handle) {
if (i == tokens_count - 1) {
/* Change createfile options for our target file instead of an intermediate directory. */
desiredAccess = FILE_GENERIC_READ | DELETE;
fileAttributes = FILE_ATTRIBUTE_NORMAL;
createOptions = FILE_NON_DIRECTORY_FILE;
shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
}
}
if (i == 0) {
/* NtCreateFile requires the \???\ prefix on drive letters. Eg: \???\C:\ */
size_t driveroot_len = strlen("\\??\\\\") + strlen(tokens[0]) + 1;
char *driveroot = malloc(driveroot_len);
snprintf(driveroot, driveroot_len + 1, "\\??\\%s\\", tokens[0]);
next_handle = win32_openat(current_handle,
driveroot,
pNtCreateFile,
pRtlInitUnicodeString,
desiredAccess,
fileAttributes,
createOptions,
shareAccess);
free(driveroot);
} else {
next_handle = win32_openat(current_handle,
tokens[i],
pNtCreateFile,
pRtlInitUnicodeString,
desiredAccess,
fileAttributes,
createOptions,
shareAccess);
}
if (NULL == next_handle) {
logg(LOGG_INFO, "traverse_to: Failed open %s\n", tokens[i]);
goto done;
}
CloseHandle(current_handle);
current_handle = next_handle;
next_handle = NULL;
#endif
logg(LOGG_DEBUG, "traverse_to: Handle opened for '%s' directory.\n", tokens[i]);
}
status = 0;
*out_handle = current_handle;
done:
#ifndef _WIN32
if ((-1 == status) && (-1 != current_handle)) {
close(current_handle);
}
#else
if ((-1 == status) && (NULL != current_handle)) {
CloseHandle(current_handle);
}
#endif
if (NULL != tokenized_directory) {
free(tokenized_directory);
}
return status;
}
/**
* @brief Rename (move) a file from Source to Destination without following symlinks.
*
* This approach mitigates the possibility that one of the directories
* in the path has been replaces with a malicious symlink.
*
* @param source Source pathname.
* @param destination Destination pathname (including file name)
* @return 0 Rename succeeded.
* @return -1 Rename failed.
*/
static int traverse_rename(const char *source, const char *destination)
{
int status = -1;
#ifndef _WIN32
cl_error_t ret;
int source_directory_fd = -1;
char *source_basename = NULL;
#else
FILE_RENAME_INFO *fileInfo = NULL;
HANDLE source_file_handle = NULL;
HANDLE destination_dir_handle = NULL;
WCHAR *destFilepathW = NULL;
int cchDestFilepath = 0;
#endif
if (NULL == source || NULL == destination) {
logg(LOGG_INFO, "traverse_rename: Invalid arguments!\n");
goto done;
}
#ifndef _WIN32
if (0 != traverse_to(source, true, &source_directory_fd)) {
logg(LOGG_INFO, "traverse_rename: Failed to open file descriptor for source directory!\n");
goto done;
}
#else
if (0 != traverse_to(source, false, &source_file_handle)) {
logg(LOGG_INFO, "traverse_rename: Failed to open file descriptor for source file!\n");
goto done;
}
if (0 != traverse_to(destination, true, &destination_dir_handle)) {
logg(LOGG_INFO, "traverse_rename: Failed to open file descriptor for destination directory!\n");
goto done;
}
#endif
#ifndef _WIN32
ret = cli_basename(source, strlen(source), &source_basename);
if (CL_SUCCESS != ret) {
logg(LOGG_INFO, "traverse_rename: Failed to get basename of source path:%s\n\tError: %d\n", source, (int)ret);
goto done;
}
if (0 != renameat(source_directory_fd, source_basename, -1, destination)) {
logg(LOGG_INFO, "traverse_rename: Failed to rename: %s\n\tto: %s\nError:%s\n", source, destination, strerror(errno));
goto done;
}
#else
/* Convert destination filepath to a PWCHAR */
cchDestFilepath = MultiByteToWideChar(CP_UTF8, 0, destination, strlen(destination), NULL, 0);
destFilepathW = calloc(cchDestFilepath * sizeof(WCHAR), 1);
if (NULL == destFilepathW) {
logg(LOGG_INFO, "traverse_rename: failed to allocate memory for destination basename UTF16LE string\n");
goto done;
}
if (0 == MultiByteToWideChar(CP_UTF8, 0, destination, strlen(destination), destFilepathW, cchDestFilepath)) {
logg(LOGG_INFO, "traverse_rename: failed to allocate buffer for UTF16LE version of destination file basename.\n");
goto done;
}
fileInfo = calloc(1, sizeof(FILE_RENAME_INFO) + cchDestFilepath * sizeof(WCHAR));
if (NULL == fileInfo) {
logg(LOGG_INFO, "traverse_rename: failed to allocate memory for fileInfo struct\n");
goto done;
}
fileInfo->ReplaceIfExists = TRUE;
fileInfo->RootDirectory = NULL;
memcpy(fileInfo->FileName, destFilepathW, cchDestFilepath * sizeof(WCHAR));
fileInfo->FileNameLength = cchDestFilepath;
if (FALSE == SetFileInformationByHandle(
source_file_handle, // FileHandle
FileRenameInfo, // FileInformationClass
fileInfo, // FileInformation
sizeof(FILE_RENAME_INFO) + cchDestFilepath * sizeof(WCHAR))) { // Length
logg(LOGG_INFO, "traverse_rename: Failed to set file rename info for '%s' to '%s'.\nError: %d\n", source, destination, GetLastError());
goto done;
}
#endif
status = 0;
done:
#ifndef _WIN32
if (NULL != source_basename) {
free(source_basename);
}
if (-1 != source_directory_fd) {
close(source_directory_fd);
}
#else
if (NULL != fileInfo) {
free(fileInfo);
}
if (NULL != destFilepathW) {
free(destFilepathW);
}
if (NULL != source_file_handle) {
CloseHandle(source_file_handle);
}
if (NULL != destination_dir_handle) {
CloseHandle(destination_dir_handle);
}
#endif
return status;
}
/**
* @brief Unlink (delete) a target file without following symlinks.
*
* This approach mitigates the possibility that one of the directories
* in the path has been replaces with a malicious symlink.
*
* @param target A file to be deleted.
* @return 0 Unlink succeeded.
* @return -1 Unlink failed.
*/
static int traverse_unlink(const char *target)
{
int status = -1;
cl_error_t ret;
#ifndef _WIN32
int target_directory_fd = -1;
#else
FILE_DISPOSITION_INFO fileInfo = {0};
HANDLE target_file_handle = NULL;
#endif
char *target_basename = NULL;
if (NULL == target) {
logg(LOGG_INFO, "traverse_unlink: Invalid arguments!\n");
goto done;
}
#ifndef _WIN32
/* On posix, we want a file descriptor for the directory */
if (0 != traverse_to(target, true, &target_directory_fd)) {
#else
/* On Windows, we want a handle to the file, not the directory */
if (0 != traverse_to(target, false, &target_file_handle)) {
#endif
logg(LOGG_INFO, "traverse_unlink: Failed to open file descriptor for target directory!\n");
goto done;
}
ret = cli_basename(target, strlen(target), &target_basename);
if (CL_SUCCESS != ret) {
logg(LOGG_INFO, "traverse_unlink: Failed to get basename of target path: %s\n\tError: %d\n", target, (int)ret);
goto done;
}
#ifndef _WIN32
if (0 != unlinkat(target_directory_fd, target_basename, 0)) {
logg(LOGG_INFO, "traverse_unlink: Failed to unlink: %s\nError:%s\n", target, strerror(errno));
goto done;
}
#else
fileInfo.DeleteFileA = TRUE;
if (FALSE == SetFileInformationByHandle(
target_file_handle, // FileHandle
FileDispositionInfo, // FileInformationClass
&fileInfo, // FileInformation
sizeof(FILE_DISPOSITION_INFO))) { // Length
logg(LOGG_INFO, "traverse_unlink: Failed to set file disposition to 'DELETE' for '%s'.\n", target);
goto done;
}
if (FALSE == CloseHandle(target_file_handle)) {
logg(LOGG_INFO, "traverse_unlink: Failed to set close & delete file '%s'.\n", target);
goto done;
}
target_file_handle = NULL;
#endif
status = 0;
done:
if (NULL != target_basename) {
free(target_basename);
}
#ifndef _WIN32
if (-1 != target_directory_fd) {
close(target_directory_fd);
}
#else
if (NULL != target_file_handle) {
CloseHandle(target_file_handle);
}
#endif
return status;
}
static void action_move(const char *filename)
{
char *nuname = NULL;
char *real_filename = NULL;
int fd = -1;
int copied = 0;
if (NULL == filename) {
goto done;
}
fd = getdest(filename, &nuname);
#ifndef _WIN32
if (fd < 0 || (0 != traverse_rename(filename, nuname) && ((copied = 1)) && filecopy(filename, nuname))) {
#else
if (fd < 0 || (((copied = 1)) && filecopy(filename, nuname))) {
#endif
logg(LOGG_ERROR, "Can't move file %s to %s\n", filename, nuname);
notmoved++;
if (nuname) traverse_unlink(nuname);
} else {
if (copied && (0 != traverse_unlink(filename)))
logg(LOGG_ERROR, "Can't unlink '%s' after copy: %s\n", filename, strerror(errno));
else
logg(LOGG_INFO, "%s: moved to '%s'\n", filename, nuname);
}
done:
if (NULL != real_filename) free(real_filename);
if (fd >= 0) close(fd);
if (NULL != nuname) free(nuname);
return;
}
static void action_copy(const char *filename)
{
char *nuname;
int fd = getdest(filename, &nuname);
if (fd < 0 || filecopy(filename, nuname)) {
logg(LOGG_ERROR, "Can't copy file '%s'\n", filename);
notmoved++;
if (nuname) traverse_unlink(nuname);
} else
logg(LOGG_INFO, "%s: copied to '%s'\n", filename, nuname);
if (fd >= 0) close(fd);
if (nuname) free(nuname);
}
static void action_remove(const char *filename)
{
char *real_filename = NULL;
if (NULL == filename) {
goto done;
}
if (0 != traverse_unlink(filename)) {
logg(LOGG_ERROR, "Can't remove file '%s'\n", filename);
notremoved++;
} else {
logg(LOGG_INFO, "%s: Removed.\n", filename);
}
done:
if (NULL != real_filename) free(real_filename);
return;
}
static int isdir(void)
{
STATBUF sb;
if (CLAMSTAT(actarget, &sb) || !S_ISDIR(sb.st_mode)) {
logg(LOGG_ERROR, "'%s' doesn't exist or is not a directory\n", actarget);
return 0;
}
return 1;
}
/*
* Call this function at the beginning to configure the user preference.
* Later, call the "action" callback function to perform the selection action.
*/
int actsetup(const struct optstruct *opts)
{
int move = optget(opts, "move")->enabled;
if (move || optget(opts, "copy")->enabled) {
#ifndef _WIN32
cl_error_t ret;
#endif
actarget = optget(opts, move ? "move" : "copy")->strarg;
#ifndef _WIN32
ret = cli_realpath((const char *)actarget, &actarget);
if (CL_SUCCESS != ret || NULL == actarget) {
logg(LOGG_INFO, "action_setup: Failed to get realpath of %s\n", actarget);
return 0;
}
#endif
if (!isdir()) return 1;
action = move ? action_move : action_copy;
targlen = strlen(actarget);
} else if (optget(opts, "remove")->enabled)
action = action_remove;
return 0;
}

51
clamav/common/actions.h Normal file
View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2009-2013 Sourcefire, Inc.
*
* Authors: aCaB
*
* 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.
*/
#ifndef ACTIONS_H
#define ACTIONS_H
#include "optparser.h"
/**
* @brief Callback function to perform the action requested when actsetup() was invoked.
*
* @param filename
*/
extern void (*action)(const char *);
/**
* @brief Select the appropriate callback function based on the configuration options.
*
* @param opts Application configuration options.
* @return int 0 if success.
* @return int 1 if move or copy were selected but the destination directory does not exist.
*/
int actsetup(const struct optstruct *opts);
extern unsigned int notremoved, notmoved;
#endif

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

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

130
clamav/common/cert_util.h Normal file
View File

@@ -0,0 +1,130 @@
/**
* OpenSSL certificate store
*
* @file cert_util.h
*
* @author Russ Kubik
* @date 2016-05-11
* @copyright Copyright (c) 2016 Cisco Systems, Inc.
*
* @section DESCRIPTION
* OpenSSL certificate store
*/
#ifndef _CERT_UTIL_H
#define _CERT_UTIL_H
#include <openssl/x509.h>
#include <curl/curl.h>
#include "clamav.h"
/* As defined by ub-common-name in https://www.ietf.org/rfc/rfc3280.txt */
#define X509_COMMON_NAME_MAX_LEN (64)
#if !(defined(C_DARWIN) || defined(_WIN32))
/**
* @brief Set the tls ca bundle to a custom value using the CURL_CA_BUNDLE env var
*
* @param curl Pointer to the curl connection handle.
*/
void set_tls_ca_bundle(CURL *curl);
#endif
/**
* @brief Load system and trusted root certificates into memory. Any errors
* while loading trusted certificates will be ignored. If error checking
* is required for trusted certificates please use cert_store_set_trusted
* directly.
*
* @details To load the certificate store with system certificates only pass
* NULL for trusted_certs and 0 (zero) for trusted_cert_count. The
* certificates store will then only load root certificates from the
* system and skip setting trusted certificates (which are
* optional and can be set later with cert_store_set_trusted).
*
* @param[in] trusted_certs - List of X509 trusted root certificates (NULL for
* empty or no trusted certificates)
* @param[in] trusted_cert_count - Number of trusted root certificates (0 for
* empty or no trusted certificates)
*
* @return 0 on success or if the cert store is already loaded, -1 on error
*/
cl_error_t cert_store_load(X509 **trusted_certs, size_t trusted_cert_count);
/**
* @brief Free system and trusted root certificates.
*/
void cert_store_unload(void);
/**
* @brief Set trusted root certificates in the cert store. If trusted
* certificates already exist then they are removed.
*
* @param[in] trusted_certs - List of trusted X509 root certificates
* @param[in] trusted_cert_count - Number of trusted X509 root certificates
*
* @return 0 on success or -1 on error
*/
cl_error_t cert_store_set_trusted(X509 **trusted_certs, size_t trusted_cert_count);
/**
* @brief Remove trusted root certificates from the cert store.
*
* @return a count of how many trusted certificates were removed. 0 (zero) will
* be returned if the cert store is not initialized
*/
size_t cert_store_remove_trusted(void);
/**
* @brief Export all system and trusted root certificates from the cert store
* into an SSL X509_STORE. The additional_ca_cert will also be exported
* if provided (not NULL).
*
* @param[out] store - SSL X509 store context
* @param[in] additional_ca_cert - additional CA certificate to append (if not
* NULL)
*/
void cert_store_export_certs(X509_STORE *store, X509 *additional_ca_cert);
/**
* @brief Export all system and trusted root certificates from the cert store as
* a null-terminated string. Certificates within the string will be
* PEM-encoded.
*
* @details An example user of this method is the EST library which, as part of
* its initialization, will ensure that the length of the CA chain
* matches a given length.
*
* @link common/est/src/src/est_client.c
*
* @param[out] cert_data - Root CA certificate PEM buffer
* @param[out] cert_data_len - Length of cert_data buffer
* @param[in] additional_ca_cert - an additional CA certificate to append
*
* @return 0 on success, -1 on error
*/
cl_error_t cert_store_export_pem(char **cert_data,
int *cert_data_len,
X509 *additional_ca_cert);
/**
* @brief Add certificates to X509 store. Duplicate certificates are skipped
* and errors are printed to the log.
*
* @param[in] store - Pointer to X509 store
* @param[in] certs - List of X509 certificates
* @param[in] cert_count - Number of X509 certificates
*/
void cert_fill_X509_store(X509_STORE *store, X509 **certs, size_t cert_count);
/**
* @brief Callback function for libcurl to verify certificates for HTTPS connections.
*
* @param[in] curl - handle for curl connection.
* @param[in] ssl_ctx - List of X509 certificates
* @param[in] userptr - Number of X509 certificates
*/
CURLcode sslctx_function(CURL *curl, void *ssl_ctx, void *userptr);
#endif

View File

@@ -0,0 +1,93 @@
/*
* Internal certificate utility methods and data structures.
*
* 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.
*/
#ifndef _CERT_UTIL_INT_H
#define _CERT_UTIL_INT_H
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include "clamav.h"
typedef struct {
X509 **certificates;
size_t count;
} cert_list_t;
typedef struct {
pthread_mutex_t mutex;
bool loaded;
cert_list_t system_certs;
cert_list_t trusted_certs;
} cert_store_t;
/**
* @brief Accessor method for cert store.
*
* @return Pointer to cert store
*/
cert_store_t *cert_store_get_int(void);
/**
* @brief Free all certificates loaded by config_store_load.
*
* @details This method does not hold the cert store lock and should not be
* called outside of cert_util.
*/
void cert_store_unload_int(void);
/**
* @brief Free memory allocated by a cert_list_t structure.
*
* @param[in] cert_list - Pointer to a cert_list_t structure
*/
void cert_store_free_cert_list_int(cert_list_t *cert_list);
/**
* @brief Set trusted root certificates in the cert store. If trusted
* certificates already exist in the cert store then they are removed.
*
* @details This method does not hold the cert store lock and should not be
* called outside of cert_util.
*
* @param[in] trusted_certs - List of X509 trusted root certificates
* @param[in] trusted_cert_count - Number of trusted root certificates
*
* @return 0 on success or -1 on error
*/
cl_error_t cert_store_set_trusted_int(X509 **trusted_certs, size_t trusted_cert_count);
/**
* @brief Get the name from an X509 certificate.
* Required if OPENSSL_VERSION_NUMBER >= 0x10100000L ( 1.1.0+ )
* because the X509 structure is now opaque.
*
* The name must be free()'d by the caller.
*
* @param[in] cert - The cert in question.
* @param[out] name - The NULL terminated name.
* @return cl_error_t CL_SUCCESS on success.
*/
cl_error_t x509_get_cert_name(X509 *cert, char **name);
#endif

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

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

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

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

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

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

View File

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

52
clamav/common/fdpassing.h Normal file
View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2009-2013 Sourcefire, Inc.
*
* Authors: Török Edvin
*
* 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 FDPASSING_H
#define FDPASSING_H
#ifdef HAVE_FD_PASSING
#ifdef FDPASS_NEED_XOPEN
/* to expose BSD 4.4/Unix98 semantics instead of BSD 4.3 semantics */
#define _XOPEN_SOURCE 500
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/uio.h>
/* Solaris 8 */
#if !defined CMSG_SPACE || !defined CMSG_LEN
#ifndef ALIGN
#define ALIGN(len) len
#endif
#ifndef CMSG_SPACE
#define CMSG_SPACE(len) (ALIGN(sizeof(struct cmsghdr)) + ALIGN(len))
#endif
#ifndef CMSG_LEN
#define CMSG_LEN(len) (ALIGN(sizeof(struct cmsghdr)) + len)
#endif
#endif
#endif
#endif

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

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

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

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

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

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

41
clamav/common/hostid.h Normal file
View File

@@ -0,0 +1,41 @@
/*
* 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.
*/
#ifndef __LIBFRESHCLAM_H
#define __LIBFRESHCLAM_H
extern char hostid[37];
/**
* @brief
*
* @return int
*/
int is_valid_hostid(void);
/**
* @brief Get the hostid object
*
* @param cbdata
* @return char*
*/
char *get_hostid(void *cbdata);
#endif //__LIBFRESHCLAM_H

View File

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

View File

@@ -0,0 +1,29 @@
/*
* 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.
*/
#ifndef __IDMEF_LOGGING_H_
#define __IDMEF_LOGGING_H_
#endif
#ifdef PRELUDE
int prelude_initialize_client(const char *analyzer_name);
#endif
void prelude_logging(const char *filename, const char *virname, const char *virhash, int virsize);

View File

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

View File

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

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

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

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

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

1483
clamav/common/optparser.c Normal file

File diff suppressed because it is too large Load Diff

95
clamav/common/optparser.h Normal file
View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2008-2013 Sourcefire, Inc.
*
* Author: Tomasz Kojm <tkojm@clamav.net>
*
* 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 __OPTPARSER_H
#define __OPTPARSER_H
/* don't share bits! */
// clang-format off
#define OPT_CLAMD 1
#define OPT_FRESHCLAM 2
#define OPT_MILTER 4
#define OPT_CLAMSCAN 8
#define OPT_CLAMDSCAN 16
#define OPT_SIGTOOL 32
#define OPT_CLAMCONF 64
#define OPT_CLAMDTOP 128
#define OPT_CLAMBC 256
#define OPT_CLAMONACC 512
#define OPT_DEPRECATED 1024
#define CLOPT_TYPE_STRING 1 /* quoted/regular string */
#define CLOPT_TYPE_NUMBER 2 /* raw number */
#define CLOPT_TYPE_SIZE 3 /* number possibly followed by modifiers (M/m or K/k) */
#define CLOPT_TYPE_BOOL 4 /* boolean */
#ifdef _WIN32
extern char _DATADIR[MAX_PATH];
extern char _CONFDIR[MAX_PATH];
extern char _CONFDIR_CLAMD[MAX_PATH];
extern char _CONFDIR_FRESHCLAM[MAX_PATH];
extern char _CONFDIR_MILTER[MAX_PATH];
#define DATADIR _DATADIR
#define CONFDIR _CONFDIR
#define CONFDIR_CLAMD _CONFDIR_CLAMD
#define CONFDIR_FRESHCLAM _CONFDIR_FRESHCLAM
#define CONFDIR_MILTER _CONFDIR_MILTER
#endif
// clang-format on
struct optstruct {
char *name;
char *cmd;
char *strarg;
long long numarg;
int enabled;
int active;
int flags;
int idx;
struct optstruct *nextarg;
struct optstruct *next;
char **filename; /* cmdline */
};
struct clam_option {
const char *name;
const char *longopt;
char shortopt;
int argtype;
const char *regex;
long long numarg;
const char *strarg;
int flags;
int owner;
const char *description;
const char *suggested;
};
const struct optstruct *optget(const struct optstruct *opts, const char *name);
void optfree(struct optstruct *opts);
struct optstruct *optparse(const char *cfgfile, int argc, char **argv, int verbose, int toolmask, int ignore, struct optstruct *oldopts);
struct optstruct *optadditem(const char *name, const char *arg, int verbose, int toolmask, int ignore, struct optstruct *oldopts);
#endif

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

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

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

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

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

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

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

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

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

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

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

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

120
clamav/common/tar.c Normal file
View File

@@ -0,0 +1,120 @@
/*
* A minimalistic tar archiver for sigtool and freshclam.
*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2007-2013 Sourcefire, Inc.
*
* Author: Tomasz Kojm <tkojm@clamav.net>
*
* 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 <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <zlib.h>
// libclamav
#include "clamav.h"
#include "tar.h"
struct tar_header {
char name[100]; /* File name */
char mode[8]; /* File mode */
char uid[8]; /* UID */
char gid[8]; /* GID */
char size[12]; /* File size (octal) */
char mtime[12]; /* Last modification */
char chksum[8]; /* Header checksum */
char type[1]; /* File type */
char lname[100]; /* Linked file name */
char pad[255];
};
#define TARBLK 512
int tar_addfile(int fd, gzFile gzs, const char *file)
{
int s, bytes;
struct tar_header hdr;
STATBUF sb;
unsigned char buff[FILEBUFF], *pt;
unsigned int i, chksum = 0;
if ((s = open(file, O_RDONLY | O_BINARY)) == -1)
return -1;
if (FSTAT(s, &sb) == -1) {
close(s);
return -1;
}
memset(&hdr, 0, TARBLK);
strncpy(hdr.name, file, 100);
hdr.name[99] = '\0';
snprintf(hdr.size, 12, "%o", (unsigned int)sb.st_size);
pt = (unsigned char *)&hdr;
for (i = 0; i < TARBLK; i++)
chksum += *pt++;
snprintf(hdr.chksum, 8, "%06o", chksum + 256);
if (gzs) {
if (!gzwrite(gzs, &hdr, TARBLK)) {
close(s);
return -1;
}
} else {
if (write(fd, &hdr, TARBLK) != TARBLK) {
close(s);
return -1;
}
}
while ((bytes = read(s, buff, FILEBUFF)) > 0) {
if (gzs) {
if (!gzwrite(gzs, buff, bytes)) {
close(s);
return -1;
}
} else {
if (write(fd, buff, bytes) != bytes) {
close(s);
return -1;
}
}
}
close(s);
if (sb.st_size % TARBLK) {
memset(&hdr, 0, TARBLK);
if (gzs) {
if (!gzwrite(gzs, &hdr, TARBLK - (sb.st_size % TARBLK)))
return -1;
} else {
if (write(fd, &hdr, TARBLK - (sb.st_size % TARBLK)) == -1)
return -1;
}
}
return 0;
}

31
clamav/common/tar.h Normal file
View File

@@ -0,0 +1,31 @@
/*
* A minimalistic tar archiver for sigtool and freshclam.
*
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2007-2013 Sourcefire, Inc.
*
* Author: Tomasz Kojm <tkojm@clamav.net>
*
* 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 __TAR_H
#define __TAR_H
#include <zlib.h>
int tar_addfile(int fd, gzFile gzs, const char *file);
#endif

View File

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