更新libclamav库1.0.0版本
This commit is contained in:
84
clamav/common/CMakeLists.txt
Normal file
84
clamav/common/CMakeLists.txt
Normal file
@@ -0,0 +1,84 @@
|
||||
# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-DWIN32_LEAN_AND_MEAN)
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
|
||||
add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE)
|
||||
|
||||
# Windows compatibility headers
|
||||
include_directories(${CMAKE_SOURCE_DIR}/win32/compat)
|
||||
endif()
|
||||
|
||||
# The "common" static library.
|
||||
add_library( common STATIC )
|
||||
target_sources( common
|
||||
PRIVATE
|
||||
cert_util.c
|
||||
actions.c
|
||||
clamdcom.c
|
||||
getopt.c
|
||||
hostid.c
|
||||
idmef_logging.c
|
||||
misc.c
|
||||
optparser.c
|
||||
output.c
|
||||
tar.c
|
||||
PUBLIC
|
||||
cert_util.h
|
||||
actions.h
|
||||
clamdcom.h
|
||||
fdpassing.h
|
||||
getopt.h
|
||||
hostid.h
|
||||
idmef_logging.h
|
||||
misc.h
|
||||
optparser.h
|
||||
output.h
|
||||
tar.h )
|
||||
|
||||
target_include_directories( common
|
||||
PRIVATE ${CMAKE_BINARY_DIR}
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} )
|
||||
if(FOUND_SYSTEMD)
|
||||
target_include_directories( common
|
||||
PRIVATE ${SYSTEMD_INCLUDE_DIRS} )
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
target_sources( common PRIVATE mac/cert_util_mac.m )
|
||||
elseif(WIN32)
|
||||
target_sources( common
|
||||
PRIVATE service.c scanmem.c exescanner.c
|
||||
PUBLIC service.h scanmem.h exescanner.h )
|
||||
target_sources( common PRIVATE win/cert_util_win.c )
|
||||
else()
|
||||
target_sources( common PRIVATE linux/cert_util_linux.c )
|
||||
endif()
|
||||
|
||||
target_link_libraries( common
|
||||
PUBLIC
|
||||
ClamAV::libclamav
|
||||
ZLIB::ZLIB
|
||||
CURL::libcurl
|
||||
OpenSSL::SSL
|
||||
OpenSSL::Crypto )
|
||||
if(WIN32)
|
||||
target_link_libraries( common
|
||||
PUBLIC
|
||||
crypt32
|
||||
psapi)
|
||||
endif()
|
||||
|
||||
if(HAVE_SYSTEMD)
|
||||
target_link_libraries( common
|
||||
PRIVATE
|
||||
SYSTEMD::systemd )
|
||||
endif()
|
||||
|
||||
set_target_properties( common PROPERTIES COMPILE_FLAGS "${WARNCFLAGS}" )
|
||||
if(WIN32)
|
||||
set_target_properties(common PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
endif()
|
||||
|
||||
add_library( ClamAV::common ALIAS common )
|
||||
717
clamav/common/actions.c
Normal file
717
clamav/common/actions.c
Normal file
@@ -0,0 +1,717 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
* Copyright (C) 2009-2013 Sourcefire, Inc.
|
||||
*
|
||||
* Author: aCaB, Micah Snyder
|
||||
*
|
||||
* These functions are actions that may be taken when a sample alerts.
|
||||
* The user may wish to:
|
||||
* - move file to destination directory.
|
||||
* - copy file to destination directory.
|
||||
* - remove (delete) the file.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <winternl.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "clamav-config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#if HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <stdbool.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
|
||||
// libclamav
|
||||
#include "clamav.h"
|
||||
#include "str.h"
|
||||
#include "others.h"
|
||||
#include "optparser.h"
|
||||
#include "output.h"
|
||||
#include "misc.h"
|
||||
#include "actions.h"
|
||||
|
||||
void (*action)(const char *) = NULL;
|
||||
unsigned int notmoved = 0, notremoved = 0;
|
||||
|
||||
static char *actarget;
|
||||
static int targlen;
|
||||
|
||||
static int getdest(const char *fullpath, char **newname)
|
||||
{
|
||||
char *tmps, *filename;
|
||||
int fd, i;
|
||||
|
||||
tmps = strdup(fullpath);
|
||||
if (!tmps) {
|
||||
*newname = NULL;
|
||||
return -1;
|
||||
}
|
||||
filename = basename(tmps);
|
||||
|
||||
if (!(*newname = (char *)malloc(targlen + strlen(filename) + 6))) {
|
||||
free(tmps);
|
||||
return -1;
|
||||
}
|
||||
sprintf(*newname, "%s" PATHSEP "%s", actarget, filename);
|
||||
for (i = 1; i < 1000; i++) {
|
||||
fd = open(*newname, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
if (fd >= 0) {
|
||||
free(tmps);
|
||||
return fd;
|
||||
}
|
||||
if (errno != EEXIST) break;
|
||||
sprintf(*newname, "%s" PATHSEP "%s.%03u", actarget, filename, i);
|
||||
}
|
||||
free(tmps);
|
||||
free(*newname);
|
||||
*newname = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
typedef LONG (*PNTCF)(
|
||||
PHANDLE FileHandle, // OUT
|
||||
ACCESS_MASK DesiredAccess,
|
||||
POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
PIO_STATUS_BLOCK IoStatusBlock, // OUT
|
||||
PLARGE_INTEGER AllocationSize,
|
||||
ULONG FileAttributes,
|
||||
ULONG ShareAccess,
|
||||
ULONG CreateDisposition,
|
||||
ULONG CreateOptions,
|
||||
PVOID EaBuffer,
|
||||
ULONG EaLength);
|
||||
|
||||
typedef void (*PRIUS)(
|
||||
PUNICODE_STRING DestinationString,
|
||||
PCWSTR SourceString);
|
||||
|
||||
/**
|
||||
* @brief A openat equivalent for Win32 with a check to NOFOLLOW soft-links.
|
||||
*
|
||||
* The caller is resposible for closing the HANDLE.
|
||||
*
|
||||
* For the desiredAccess, fileAttributes, createOptions, and shareAccess parameters
|
||||
* see https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
|
||||
*
|
||||
* @param current_handle The current handle. If set to NULL, then filename should be a drive letter.
|
||||
* @param filename The directory to open. If current_handle is valid, should be a directory found in the current directory.
|
||||
* @param pNtCreateFile A function pointer to the NtCreateFile Win32 Native API.
|
||||
* @param pRtlInitUnicodeString A function pointer to the RtlInitUnicodeString Win32 Native API.
|
||||
* @param desiredAccess The DesiredAccess option for NtCreateFile
|
||||
* @param fileAttributes The FileAttributes option for NtCreateFile
|
||||
* @param createOptions The CreateOptions option for NtCreateFile
|
||||
* @param shareAccess The ShareAccess option for NtCreateFile
|
||||
* @return HANDLE A handle on success, NULL on failure.
|
||||
*/
|
||||
static HANDLE win32_openat(
|
||||
HANDLE current_handle,
|
||||
const char *filename,
|
||||
PNTCF pNtCreateFile,
|
||||
PRIUS pRtlInitUnicodeString,
|
||||
ACCESS_MASK desiredAccess,
|
||||
ULONG fileAttributes,
|
||||
ULONG createOptions,
|
||||
ULONG shareAccess)
|
||||
{
|
||||
HANDLE next_handle = NULL;
|
||||
|
||||
LONG ntStatus;
|
||||
WCHAR *filenameW = NULL;
|
||||
UNICODE_STRING filenameU;
|
||||
int cchNextDirectoryName = 0;
|
||||
IO_STATUS_BLOCK ioStatusBlock = {0};
|
||||
OBJECT_ATTRIBUTES objAttributes = {0};
|
||||
FILE_ATTRIBUTE_TAG_INFO tagInfo = {0};
|
||||
|
||||
/* Convert filename to a UNICODE_STRING, required by the native API NtCreateFile() */
|
||||
cchNextDirectoryName = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
|
||||
filenameW = malloc(cchNextDirectoryName * sizeof(WCHAR));
|
||||
if (NULL == filenameW) {
|
||||
logg(LOGG_INFO, "win32_openat: failed to allocate memory for next directory name UTF16LE string\n");
|
||||
goto done;
|
||||
}
|
||||
if (0 == MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, cchNextDirectoryName)) {
|
||||
logg(LOGG_INFO, "win32_openat: failed to allocate buffer for unicode version of intermediate directory name.\n");
|
||||
goto done;
|
||||
}
|
||||
pRtlInitUnicodeString(&filenameU, filenameW);
|
||||
|
||||
InitializeObjectAttributes(
|
||||
&objAttributes, // ObjectAttributes
|
||||
&filenameU, // ObjectName
|
||||
OBJ_CASE_INSENSITIVE, // Attributes
|
||||
current_handle, // Root directory
|
||||
NULL); // SecurityDescriptor
|
||||
|
||||
ntStatus = pNtCreateFile(
|
||||
&next_handle, // FileHandle
|
||||
desiredAccess, // DesiredAccess
|
||||
&objAttributes, // ObjectAttributes
|
||||
&ioStatusBlock, // [out] status
|
||||
0, // AllocationSize
|
||||
fileAttributes, // FileAttributes
|
||||
shareAccess, // ShareAccess
|
||||
FILE_OPEN, // CreateDisposition
|
||||
createOptions, // CreateOptions
|
||||
NULL, // EaBuffer
|
||||
0); // EaLength
|
||||
if (!NT_SUCCESS(ntStatus) || (NULL == next_handle)) {
|
||||
logg(LOGG_INFO, "win32_openat: Failed to open file '%s'. \nError: 0x%x \nioStatusBlock: 0x%x\n", filename, ntStatus, ioStatusBlock.Information);
|
||||
goto done;
|
||||
}
|
||||
logg(LOGG_DEBUG, "win32_openat: Opened file \"%s\"\n", filename);
|
||||
|
||||
if (0 == GetFileInformationByHandleEx(
|
||||
next_handle, // hFile,
|
||||
FileAttributeTagInfo, // FileInformationClass
|
||||
&tagInfo, // lpFileInformation
|
||||
sizeof(FILE_ATTRIBUTE_TAG_INFO))) { // dwBufferSize
|
||||
logg(LOGG_INFO, "win32_openat: Failed to get file information by handle '%s'. Error: %d.\n", filename, GetLastError());
|
||||
|
||||
CloseHandle(next_handle);
|
||||
next_handle = NULL;
|
||||
goto done;
|
||||
}
|
||||
logg(LOGG_DEBUG, "win32_openat: tagInfo.FileAttributes: 0x%0x\n", tagInfo.FileAttributes);
|
||||
logg(LOGG_DEBUG, "win32_openat: tagInfo.ReparseTag: 0x%0x\n", tagInfo.ReparseTag);
|
||||
if (0 != (tagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
|
||||
logg(LOGG_INFO, "win32_openat: File is a soft link: '%s' Aborting path traversal.\n\n", filename);
|
||||
|
||||
CloseHandle(next_handle);
|
||||
next_handle = NULL;
|
||||
goto done;
|
||||
}
|
||||
logg(LOGG_DEBUG, "win32_openat: File or directory is not a soft link.\n\n");
|
||||
|
||||
done:
|
||||
if (NULL != filenameW) {
|
||||
free(filenameW);
|
||||
}
|
||||
|
||||
return next_handle;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Traverse from root to the specified directory without following symlinks.
|
||||
*
|
||||
* The intention is so you can use `unlinkat` or `rename_at` to safely move or
|
||||
* delete the target directory.
|
||||
*
|
||||
* The caller is responsible for closing the output file descriptor if the
|
||||
* traversal succeeded.
|
||||
*
|
||||
* @param directory The directory to traverse to (must be NULL terminated).
|
||||
* @param want_directory_handle Set to true to get the directory handle containing the file, false to get the file handle.
|
||||
* @param[out] out_handle An open file descriptor or HANDLE (win32) for the directory.
|
||||
* @return 0 Traverse succeeded.
|
||||
* @return -1 Traverse failed.
|
||||
*/
|
||||
#ifndef _WIN32
|
||||
static int traverse_to(const char *directory, bool want_directory_handle, int *out_handle)
|
||||
#else
|
||||
static int traverse_to(const char *directory, bool want_directory_handle, HANDLE *out_handle)
|
||||
#endif
|
||||
{
|
||||
int status = -1;
|
||||
size_t tokens_count;
|
||||
const char *tokens[PATH_MAX / 2];
|
||||
size_t i;
|
||||
char *tokenized_directory = NULL;
|
||||
#ifndef _WIN32
|
||||
int current_handle = -1;
|
||||
int next_handle = -1;
|
||||
#else
|
||||
bool bNeedDeleteFileAccess = false;
|
||||
|
||||
HMODULE ntdll = NULL;
|
||||
PNTCF pNtCreateFile = NULL;
|
||||
PRIUS pRtlInitUnicodeString = NULL;
|
||||
|
||||
PHANDLE current_handle = NULL;
|
||||
PHANDLE next_handle = NULL;
|
||||
|
||||
ACCESS_MASK desiredAccess = STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_READ_EA;
|
||||
ULONG fileAttributes = FILE_ATTRIBUTE_DIRECTORY;
|
||||
ULONG createOptions = FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT;
|
||||
ULONG shareAccess = FILE_SHARE_READ;
|
||||
#endif
|
||||
|
||||
if (NULL == directory || NULL == out_handle) {
|
||||
logg(LOGG_INFO, "traverse_to: Invalid arguments!\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
ntdll = LoadLibraryA("ntdll.dll");
|
||||
if (NULL == ntdll) {
|
||||
logg(LOGG_INFO, "traverse_to: failed to load ntdll!\n");
|
||||
goto done;
|
||||
}
|
||||
pNtCreateFile = (PNTCF)GetProcAddress(ntdll, "NtCreateFile");
|
||||
if (NULL == pNtCreateFile) {
|
||||
logg(LOGG_INFO, "traverse_to: failed to get NtCreateFile proc address!\n");
|
||||
goto done;
|
||||
}
|
||||
pRtlInitUnicodeString = (PRIUS)GetProcAddress(ntdll, "RtlInitUnicodeString");
|
||||
if (NULL == pRtlInitUnicodeString) {
|
||||
logg(LOGG_INFO, "traverse_to: failed to get pRtlInitUnicodeString proc address!\n");
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
|
||||
tokenized_directory = strdup(directory);
|
||||
if (NULL == tokenized_directory) {
|
||||
logg(LOGG_INFO, "traverse_to: Failed to get copy of directory path to be tokenized!\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
tokens_count = cli_strtokenize(tokenized_directory, *PATHSEP, PATH_MAX / 2, tokens);
|
||||
if (0 == tokens_count) {
|
||||
logg(LOGG_INFO, "traverse_to: tokenize of target directory returned 0 tokens!\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
/*
|
||||
* Open the root(/) directory, because it won't be the first token like a
|
||||
* drive letter (i.e. "C:") would be on Windows.
|
||||
*/
|
||||
current_handle = open("/", O_RDONLY | O_NOFOLLOW);
|
||||
if (-1 == current_handle) {
|
||||
logg(LOGG_INFO, "traverse_to: Failed to open file descriptor for '/' directory.\n");
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (true == want_directory_handle) {
|
||||
tokens_count -= 1;
|
||||
}
|
||||
|
||||
if (0 == tokens_count) {
|
||||
logg(LOGG_INFO, "traverse_to: Failed to get copy of directory path to be tokenized!\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < tokens_count; i++) {
|
||||
if (0 == strlen(tokens[i])) {
|
||||
/* Empty token, likely first / or double // */
|
||||
continue;
|
||||
}
|
||||
#ifndef _WIN32
|
||||
next_handle = openat(current_handle, tokens[i], O_RDONLY | O_NOFOLLOW);
|
||||
if (-1 == next_handle) {
|
||||
logg(LOGG_INFO, "traverse_to: Failed open %s\n", tokens[i]);
|
||||
goto done;
|
||||
}
|
||||
close(current_handle);
|
||||
current_handle = next_handle;
|
||||
next_handle = -1;
|
||||
#else
|
||||
if (true != want_directory_handle) {
|
||||
if (i == tokens_count - 1) {
|
||||
/* Change createfile options for our target file instead of an intermediate directory. */
|
||||
desiredAccess = FILE_GENERIC_READ | DELETE;
|
||||
fileAttributes = FILE_ATTRIBUTE_NORMAL;
|
||||
createOptions = FILE_NON_DIRECTORY_FILE;
|
||||
shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
||||
}
|
||||
}
|
||||
if (i == 0) {
|
||||
/* NtCreateFile requires the \???\ prefix on drive letters. Eg: \???\C:\ */
|
||||
size_t driveroot_len = strlen("\\??\\\\") + strlen(tokens[0]) + 1;
|
||||
char *driveroot = malloc(driveroot_len);
|
||||
snprintf(driveroot, driveroot_len + 1, "\\??\\%s\\", tokens[0]);
|
||||
next_handle = win32_openat(current_handle,
|
||||
driveroot,
|
||||
pNtCreateFile,
|
||||
pRtlInitUnicodeString,
|
||||
desiredAccess,
|
||||
fileAttributes,
|
||||
createOptions,
|
||||
shareAccess);
|
||||
free(driveroot);
|
||||
} else {
|
||||
next_handle = win32_openat(current_handle,
|
||||
tokens[i],
|
||||
pNtCreateFile,
|
||||
pRtlInitUnicodeString,
|
||||
desiredAccess,
|
||||
fileAttributes,
|
||||
createOptions,
|
||||
shareAccess);
|
||||
}
|
||||
if (NULL == next_handle) {
|
||||
logg(LOGG_INFO, "traverse_to: Failed open %s\n", tokens[i]);
|
||||
goto done;
|
||||
}
|
||||
CloseHandle(current_handle);
|
||||
current_handle = next_handle;
|
||||
next_handle = NULL;
|
||||
#endif
|
||||
logg(LOGG_DEBUG, "traverse_to: Handle opened for '%s' directory.\n", tokens[i]);
|
||||
}
|
||||
|
||||
status = 0;
|
||||
*out_handle = current_handle;
|
||||
|
||||
done:
|
||||
#ifndef _WIN32
|
||||
if ((-1 == status) && (-1 != current_handle)) {
|
||||
close(current_handle);
|
||||
}
|
||||
#else
|
||||
if ((-1 == status) && (NULL != current_handle)) {
|
||||
CloseHandle(current_handle);
|
||||
}
|
||||
#endif
|
||||
if (NULL != tokenized_directory) {
|
||||
free(tokenized_directory);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Rename (move) a file from Source to Destination without following symlinks.
|
||||
*
|
||||
* This approach mitigates the possibility that one of the directories
|
||||
* in the path has been replaces with a malicious symlink.
|
||||
*
|
||||
* @param source Source pathname.
|
||||
* @param destination Destination pathname (including file name)
|
||||
* @return 0 Rename succeeded.
|
||||
* @return -1 Rename failed.
|
||||
*/
|
||||
static int traverse_rename(const char *source, const char *destination)
|
||||
{
|
||||
int status = -1;
|
||||
#ifndef _WIN32
|
||||
cl_error_t ret;
|
||||
int source_directory_fd = -1;
|
||||
char *source_basename = NULL;
|
||||
#else
|
||||
FILE_RENAME_INFO *fileInfo = NULL;
|
||||
HANDLE source_file_handle = NULL;
|
||||
HANDLE destination_dir_handle = NULL;
|
||||
WCHAR *destFilepathW = NULL;
|
||||
int cchDestFilepath = 0;
|
||||
#endif
|
||||
|
||||
if (NULL == source || NULL == destination) {
|
||||
logg(LOGG_INFO, "traverse_rename: Invalid arguments!\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
if (0 != traverse_to(source, true, &source_directory_fd)) {
|
||||
logg(LOGG_INFO, "traverse_rename: Failed to open file descriptor for source directory!\n");
|
||||
goto done;
|
||||
}
|
||||
#else
|
||||
if (0 != traverse_to(source, false, &source_file_handle)) {
|
||||
logg(LOGG_INFO, "traverse_rename: Failed to open file descriptor for source file!\n");
|
||||
goto done;
|
||||
}
|
||||
if (0 != traverse_to(destination, true, &destination_dir_handle)) {
|
||||
logg(LOGG_INFO, "traverse_rename: Failed to open file descriptor for destination directory!\n");
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
ret = cli_basename(source, strlen(source), &source_basename);
|
||||
if (CL_SUCCESS != ret) {
|
||||
logg(LOGG_INFO, "traverse_rename: Failed to get basename of source path:%s\n\tError: %d\n", source, (int)ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (0 != renameat(source_directory_fd, source_basename, -1, destination)) {
|
||||
logg(LOGG_INFO, "traverse_rename: Failed to rename: %s\n\tto: %s\nError:%s\n", source, destination, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
#else
|
||||
/* Convert destination filepath to a PWCHAR */
|
||||
cchDestFilepath = MultiByteToWideChar(CP_UTF8, 0, destination, strlen(destination), NULL, 0);
|
||||
destFilepathW = calloc(cchDestFilepath * sizeof(WCHAR), 1);
|
||||
if (NULL == destFilepathW) {
|
||||
logg(LOGG_INFO, "traverse_rename: failed to allocate memory for destination basename UTF16LE string\n");
|
||||
goto done;
|
||||
}
|
||||
if (0 == MultiByteToWideChar(CP_UTF8, 0, destination, strlen(destination), destFilepathW, cchDestFilepath)) {
|
||||
logg(LOGG_INFO, "traverse_rename: failed to allocate buffer for UTF16LE version of destination file basename.\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
fileInfo = calloc(1, sizeof(FILE_RENAME_INFO) + cchDestFilepath * sizeof(WCHAR));
|
||||
if (NULL == fileInfo) {
|
||||
logg(LOGG_INFO, "traverse_rename: failed to allocate memory for fileInfo struct\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
fileInfo->ReplaceIfExists = TRUE;
|
||||
fileInfo->RootDirectory = NULL;
|
||||
memcpy(fileInfo->FileName, destFilepathW, cchDestFilepath * sizeof(WCHAR));
|
||||
fileInfo->FileNameLength = cchDestFilepath;
|
||||
if (FALSE == SetFileInformationByHandle(
|
||||
source_file_handle, // FileHandle
|
||||
FileRenameInfo, // FileInformationClass
|
||||
fileInfo, // FileInformation
|
||||
sizeof(FILE_RENAME_INFO) + cchDestFilepath * sizeof(WCHAR))) { // Length
|
||||
|
||||
logg(LOGG_INFO, "traverse_rename: Failed to set file rename info for '%s' to '%s'.\nError: %d\n", source, destination, GetLastError());
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
|
||||
status = 0;
|
||||
|
||||
done:
|
||||
|
||||
#ifndef _WIN32
|
||||
if (NULL != source_basename) {
|
||||
free(source_basename);
|
||||
}
|
||||
|
||||
if (-1 != source_directory_fd) {
|
||||
close(source_directory_fd);
|
||||
}
|
||||
#else
|
||||
if (NULL != fileInfo) {
|
||||
free(fileInfo);
|
||||
}
|
||||
if (NULL != destFilepathW) {
|
||||
free(destFilepathW);
|
||||
}
|
||||
if (NULL != source_file_handle) {
|
||||
CloseHandle(source_file_handle);
|
||||
}
|
||||
if (NULL != destination_dir_handle) {
|
||||
CloseHandle(destination_dir_handle);
|
||||
}
|
||||
#endif
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unlink (delete) a target file without following symlinks.
|
||||
*
|
||||
* This approach mitigates the possibility that one of the directories
|
||||
* in the path has been replaces with a malicious symlink.
|
||||
*
|
||||
* @param target A file to be deleted.
|
||||
* @return 0 Unlink succeeded.
|
||||
* @return -1 Unlink failed.
|
||||
*/
|
||||
static int traverse_unlink(const char *target)
|
||||
{
|
||||
int status = -1;
|
||||
cl_error_t ret;
|
||||
#ifndef _WIN32
|
||||
int target_directory_fd = -1;
|
||||
#else
|
||||
FILE_DISPOSITION_INFO fileInfo = {0};
|
||||
HANDLE target_file_handle = NULL;
|
||||
#endif
|
||||
char *target_basename = NULL;
|
||||
|
||||
if (NULL == target) {
|
||||
logg(LOGG_INFO, "traverse_unlink: Invalid arguments!\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
/* On posix, we want a file descriptor for the directory */
|
||||
if (0 != traverse_to(target, true, &target_directory_fd)) {
|
||||
#else
|
||||
/* On Windows, we want a handle to the file, not the directory */
|
||||
if (0 != traverse_to(target, false, &target_file_handle)) {
|
||||
#endif
|
||||
logg(LOGG_INFO, "traverse_unlink: Failed to open file descriptor for target directory!\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = cli_basename(target, strlen(target), &target_basename);
|
||||
if (CL_SUCCESS != ret) {
|
||||
logg(LOGG_INFO, "traverse_unlink: Failed to get basename of target path: %s\n\tError: %d\n", target, (int)ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
if (0 != unlinkat(target_directory_fd, target_basename, 0)) {
|
||||
logg(LOGG_INFO, "traverse_unlink: Failed to unlink: %s\nError:%s\n", target, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
#else
|
||||
fileInfo.DeleteFileA = TRUE;
|
||||
if (FALSE == SetFileInformationByHandle(
|
||||
target_file_handle, // FileHandle
|
||||
FileDispositionInfo, // FileInformationClass
|
||||
&fileInfo, // FileInformation
|
||||
sizeof(FILE_DISPOSITION_INFO))) { // Length
|
||||
|
||||
logg(LOGG_INFO, "traverse_unlink: Failed to set file disposition to 'DELETE' for '%s'.\n", target);
|
||||
goto done;
|
||||
}
|
||||
if (FALSE == CloseHandle(target_file_handle)) {
|
||||
logg(LOGG_INFO, "traverse_unlink: Failed to set close & delete file '%s'.\n", target);
|
||||
goto done;
|
||||
}
|
||||
target_file_handle = NULL;
|
||||
#endif
|
||||
|
||||
status = 0;
|
||||
|
||||
done:
|
||||
|
||||
if (NULL != target_basename) {
|
||||
free(target_basename);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
if (-1 != target_directory_fd) {
|
||||
close(target_directory_fd);
|
||||
}
|
||||
#else
|
||||
if (NULL != target_file_handle) {
|
||||
CloseHandle(target_file_handle);
|
||||
}
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
static void action_move(const char *filename)
|
||||
{
|
||||
char *nuname = NULL;
|
||||
char *real_filename = NULL;
|
||||
int fd = -1;
|
||||
int copied = 0;
|
||||
|
||||
if (NULL == filename) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
fd = getdest(filename, &nuname);
|
||||
|
||||
#ifndef _WIN32
|
||||
if (fd < 0 || (0 != traverse_rename(filename, nuname) && ((copied = 1)) && filecopy(filename, nuname))) {
|
||||
#else
|
||||
if (fd < 0 || (((copied = 1)) && filecopy(filename, nuname))) {
|
||||
#endif
|
||||
logg(LOGG_ERROR, "Can't move file %s to %s\n", filename, nuname);
|
||||
notmoved++;
|
||||
if (nuname) traverse_unlink(nuname);
|
||||
} else {
|
||||
if (copied && (0 != traverse_unlink(filename)))
|
||||
logg(LOGG_ERROR, "Can't unlink '%s' after copy: %s\n", filename, strerror(errno));
|
||||
else
|
||||
logg(LOGG_INFO, "%s: moved to '%s'\n", filename, nuname);
|
||||
}
|
||||
|
||||
done:
|
||||
if (NULL != real_filename) free(real_filename);
|
||||
if (fd >= 0) close(fd);
|
||||
if (NULL != nuname) free(nuname);
|
||||
return;
|
||||
}
|
||||
|
||||
static void action_copy(const char *filename)
|
||||
{
|
||||
char *nuname;
|
||||
int fd = getdest(filename, &nuname);
|
||||
|
||||
if (fd < 0 || filecopy(filename, nuname)) {
|
||||
logg(LOGG_ERROR, "Can't copy file '%s'\n", filename);
|
||||
notmoved++;
|
||||
if (nuname) traverse_unlink(nuname);
|
||||
} else
|
||||
logg(LOGG_INFO, "%s: copied to '%s'\n", filename, nuname);
|
||||
|
||||
if (fd >= 0) close(fd);
|
||||
if (nuname) free(nuname);
|
||||
}
|
||||
|
||||
static void action_remove(const char *filename)
|
||||
{
|
||||
char *real_filename = NULL;
|
||||
|
||||
if (NULL == filename) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (0 != traverse_unlink(filename)) {
|
||||
logg(LOGG_ERROR, "Can't remove file '%s'\n", filename);
|
||||
notremoved++;
|
||||
} else {
|
||||
logg(LOGG_INFO, "%s: Removed.\n", filename);
|
||||
}
|
||||
|
||||
done:
|
||||
if (NULL != real_filename) free(real_filename);
|
||||
return;
|
||||
}
|
||||
|
||||
static int isdir(void)
|
||||
{
|
||||
STATBUF sb;
|
||||
if (CLAMSTAT(actarget, &sb) || !S_ISDIR(sb.st_mode)) {
|
||||
logg(LOGG_ERROR, "'%s' doesn't exist or is not a directory\n", actarget);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call this function at the beginning to configure the user preference.
|
||||
* Later, call the "action" callback function to perform the selection action.
|
||||
*/
|
||||
int actsetup(const struct optstruct *opts)
|
||||
{
|
||||
int move = optget(opts, "move")->enabled;
|
||||
if (move || optget(opts, "copy")->enabled) {
|
||||
#ifndef _WIN32
|
||||
cl_error_t ret;
|
||||
#endif
|
||||
actarget = optget(opts, move ? "move" : "copy")->strarg;
|
||||
#ifndef _WIN32
|
||||
ret = cli_realpath((const char *)actarget, &actarget);
|
||||
if (CL_SUCCESS != ret || NULL == actarget) {
|
||||
logg(LOGG_INFO, "action_setup: Failed to get realpath of %s\n", actarget);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (!isdir()) return 1;
|
||||
action = move ? action_move : action_copy;
|
||||
targlen = strlen(actarget);
|
||||
} else if (optget(opts, "remove")->enabled)
|
||||
action = action_remove;
|
||||
return 0;
|
||||
}
|
||||
51
clamav/common/actions.h
Normal file
51
clamav/common/actions.h
Normal 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
699
clamav/common/cert_util.c
Normal file
@@ -0,0 +1,699 @@
|
||||
/*
|
||||
* OpenSSL certificate caching.
|
||||
*
|
||||
* Copyright (C) 2016-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* Authors: Russ Kubik
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "cert_util.h"
|
||||
#include "cert_util_internal.h"
|
||||
|
||||
#include "output.h"
|
||||
|
||||
static cert_store_t _cert_store = {
|
||||
.mutex = PTHREAD_MUTEX_INITIALIZER};
|
||||
|
||||
static cl_error_t _x509_to_pem(X509 *cert,
|
||||
char **data,
|
||||
int *len)
|
||||
{
|
||||
cl_error_t ret = CL_EFORMAT;
|
||||
|
||||
BIO *out = NULL;
|
||||
long pem_len = 0;
|
||||
char *pem_data = NULL;
|
||||
|
||||
if (cert == NULL || data == NULL || len == NULL) {
|
||||
mprintf(LOGG_ERROR, "_x509_to_pem: Invalid argument\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Output the certs to a new BIO using the PEM format */
|
||||
out = BIO_new(BIO_s_mem());
|
||||
if (!out) {
|
||||
mprintf(LOGG_ERROR, "BIO_new failed\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
PEM_write_bio_X509(out, cert);
|
||||
|
||||
(void)BIO_flush(out);
|
||||
|
||||
/* Convert the BIO to char* */
|
||||
pem_len = BIO_get_mem_data(out, &pem_data);
|
||||
if (pem_len <= 0 || !pem_data) {
|
||||
mprintf(LOGG_ERROR, "BIO_new: BIO_get_mem_data failed\n");
|
||||
BIO_free_all(out);
|
||||
goto done;
|
||||
}
|
||||
|
||||
*data = calloc(1, pem_len + 1);
|
||||
if (!*data) {
|
||||
mprintf(LOGG_ERROR, "BIO_new: malloc failed\n");
|
||||
BIO_free_all(out);
|
||||
goto done;
|
||||
}
|
||||
memcpy(*data, pem_data, pem_len);
|
||||
(*data)[pem_len] = '\0';
|
||||
|
||||
*len = (int)pem_len;
|
||||
|
||||
BIO_free_all(out);
|
||||
|
||||
ret = CL_SUCCESS;
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This method will convert a X509 certificate to PEM format and append
|
||||
* it to a string buffer.
|
||||
*
|
||||
* @note If realloc fails to reserve memory for *cert_data it will free whatever
|
||||
* is currently in *cert_data before returning. total_buf_len is also set
|
||||
* to 0 (zero) in this case.
|
||||
*
|
||||
* @param[in] *ca_cert Pointer to CA certificate
|
||||
* @param[out] **cert_data Pointer to allocated string buffer
|
||||
* @param[out] *total_buf_len Total of string buffer length after appending
|
||||
* CA certificate (ca_cert)
|
||||
* @param[in,out] *remaining_buf_len Remaining data left allowed in CA certificate
|
||||
* chain after appending CA certificate
|
||||
* (ca_cert)
|
||||
*
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
static cl_error_t _x509_to_pem_append(X509 *ca_cert,
|
||||
char **cert_data,
|
||||
int *total_buf_len,
|
||||
size_t *remaining_buf_len)
|
||||
{
|
||||
char *pem_data = NULL;
|
||||
char *tmp;
|
||||
int pem_data_len = 0;
|
||||
cl_error_t ret = CL_EOPEN;
|
||||
int current_len = 0;
|
||||
|
||||
if (ca_cert == NULL || total_buf_len == NULL ||
|
||||
remaining_buf_len == NULL || *cert_data == NULL) {
|
||||
mprintf(LOGG_ERROR, "NULL parameter given\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
current_len = *total_buf_len;
|
||||
|
||||
if (CL_SUCCESS != _x509_to_pem(ca_cert, &pem_data, &pem_data_len)) {
|
||||
mprintf(LOGG_ERROR, "Failed to convert x509 certificate to PEM\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (pem_data_len > (int)*remaining_buf_len) {
|
||||
tmp = realloc(*cert_data, current_len + pem_data_len + 1);
|
||||
if (tmp == NULL) {
|
||||
mprintf(LOGG_ERROR, "Could not realloc enough memory for PEM "
|
||||
"certificate\n");
|
||||
|
||||
free(*cert_data);
|
||||
*cert_data = NULL;
|
||||
*total_buf_len = 0;
|
||||
|
||||
goto done;
|
||||
}
|
||||
*cert_data = tmp;
|
||||
tmp = NULL;
|
||||
*remaining_buf_len = 0;
|
||||
} else {
|
||||
*remaining_buf_len -= pem_data_len;
|
||||
}
|
||||
|
||||
memcpy(&((*cert_data)[current_len]), pem_data, pem_data_len);
|
||||
*total_buf_len = current_len + pem_data_len;
|
||||
(*cert_data)[*total_buf_len] = '\0';
|
||||
|
||||
ret = CL_SUCCESS;
|
||||
|
||||
done:
|
||||
|
||||
free(pem_data);
|
||||
pem_data = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
cert_store_t *cert_store_get_int(void)
|
||||
{
|
||||
return &_cert_store;
|
||||
}
|
||||
|
||||
void cert_store_unload_int(void)
|
||||
{
|
||||
if (_cert_store.loaded) {
|
||||
cert_store_free_cert_list_int(&_cert_store.system_certs);
|
||||
cert_store_free_cert_list_int(&_cert_store.trusted_certs);
|
||||
_cert_store.loaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
void cert_store_free_cert_list_int(cert_list_t *cert_list)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (cert_list && cert_list->certificates) {
|
||||
for (i = 0; i < cert_list->count; ++i) {
|
||||
X509_free(cert_list->certificates[i]);
|
||||
cert_list->certificates[i] = NULL;
|
||||
}
|
||||
|
||||
free(cert_list->certificates);
|
||||
cert_list->certificates = NULL;
|
||||
cert_list->count = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
void cert_store_unload(void)
|
||||
{
|
||||
int pt_err;
|
||||
|
||||
pt_err = pthread_mutex_lock(&_cert_store.mutex);
|
||||
if (pt_err) {
|
||||
errno = pt_err;
|
||||
mprintf(LOGG_ERROR, "Mutex lock failed\n");
|
||||
}
|
||||
|
||||
cert_store_unload_int();
|
||||
|
||||
pt_err = pthread_mutex_unlock(&_cert_store.mutex);
|
||||
if (pt_err) {
|
||||
errno = pt_err;
|
||||
mprintf(LOGG_ERROR, "Mutex unlock failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0+ */
|
||||
static cl_error_t x509_cert_name_cmp(X509 *cert_a, X509 *cert_b, int *cmp_out)
|
||||
{
|
||||
cl_error_t status = CL_EMEM;
|
||||
|
||||
X509_NAME *a = NULL;
|
||||
X509_NAME *b = NULL;
|
||||
|
||||
BIO *bio_out_a = NULL;
|
||||
BIO *bio_out_b = NULL;
|
||||
|
||||
BUF_MEM *biomem_a;
|
||||
BUF_MEM *biomem_b;
|
||||
|
||||
bio_out_a = BIO_new(BIO_s_mem());
|
||||
if (!bio_out_a)
|
||||
goto done;
|
||||
|
||||
bio_out_b = BIO_new(BIO_s_mem());
|
||||
if (!bio_out_b)
|
||||
goto done;
|
||||
|
||||
a = X509_get_subject_name(cert_a);
|
||||
|
||||
if (-1 == X509_NAME_print_ex(bio_out_a, a, 0, XN_FLAG_SEP_SPLUS_SPC)) {
|
||||
mprintf(LOGG_ERROR, "Failed to print x509 certificate name!\n");
|
||||
goto done;
|
||||
}
|
||||
BIO_get_mem_ptr(bio_out_a, &biomem_a);
|
||||
|
||||
b = X509_get_subject_name(cert_b);
|
||||
|
||||
if (-1 == X509_NAME_print_ex(bio_out_b, b, 0, XN_FLAG_SEP_SPLUS_SPC)) {
|
||||
mprintf(LOGG_ERROR, "Failed to print x509 certificate name!\n");
|
||||
goto done;
|
||||
}
|
||||
BIO_get_mem_ptr(bio_out_b, &biomem_b);
|
||||
|
||||
*cmp_out = strncmp(biomem_a->data, biomem_b->data, MIN(biomem_a->length, biomem_b->length));
|
||||
status = CL_SUCCESS;
|
||||
|
||||
done:
|
||||
if (NULL != bio_out_a)
|
||||
BIO_free(bio_out_a);
|
||||
if (NULL != bio_out_b)
|
||||
BIO_free(bio_out_b);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
cl_error_t x509_get_cert_name(X509 *cert, char **name)
|
||||
{
|
||||
cl_error_t status = CL_EMEM;
|
||||
|
||||
X509_NAME *a = NULL;
|
||||
BIO *bio_out = NULL;
|
||||
BUF_MEM *biomem;
|
||||
char *cert_name = NULL;
|
||||
|
||||
if (NULL == cert || NULL == name) {
|
||||
status = CL_EARG;
|
||||
goto done;
|
||||
}
|
||||
|
||||
*name = NULL;
|
||||
|
||||
bio_out = BIO_new(BIO_s_mem());
|
||||
if (!bio_out)
|
||||
goto done;
|
||||
|
||||
a = X509_get_subject_name(cert);
|
||||
|
||||
if (-1 == X509_NAME_print_ex(bio_out, a, 0, XN_FLAG_SEP_SPLUS_SPC)) {
|
||||
mprintf(LOGG_ERROR, "Failed to print x509 certificate name!\n");
|
||||
goto done;
|
||||
}
|
||||
BIO_get_mem_ptr(bio_out, &biomem);
|
||||
|
||||
cert_name = malloc(biomem->length + 1);
|
||||
if (!cert_name) {
|
||||
mprintf(LOGG_ERROR, "Failed to allocate memory for certificate name biomem structure!\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
memcpy(cert_name, biomem->data, biomem->length);
|
||||
cert_name[biomem->length] = '\0';
|
||||
|
||||
*name = cert_name;
|
||||
status = CL_SUCCESS;
|
||||
|
||||
done:
|
||||
if (NULL != bio_out)
|
||||
BIO_free(bio_out);
|
||||
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
cl_error_t cert_store_export_pem(char **cert_data,
|
||||
int *cert_data_len,
|
||||
X509 *additional_ca_cert)
|
||||
{
|
||||
const uint32_t STARTING_RAW_PEM_LENGTH = 350 * 1024;
|
||||
uint32_t i;
|
||||
cl_error_t ret = CL_EOPEN;
|
||||
bool locked = false;
|
||||
int pt_err;
|
||||
|
||||
size_t remaining_buf_len = STARTING_RAW_PEM_LENGTH;
|
||||
bool add_additional_ca_cert = true;
|
||||
|
||||
if ((cert_data == NULL) || (cert_data_len == NULL)) {
|
||||
mprintf(LOGG_ERROR, "One or more arguments are NULL\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
*cert_data = calloc(1, STARTING_RAW_PEM_LENGTH + 1);
|
||||
if (*cert_data == NULL) {
|
||||
mprintf(LOGG_ERROR, "Could not allocate memory for PEM certs\n");
|
||||
goto done;
|
||||
}
|
||||
*cert_data_len = 0;
|
||||
|
||||
pt_err = pthread_mutex_lock(&_cert_store.mutex);
|
||||
if (pt_err) {
|
||||
errno = pt_err;
|
||||
mprintf(LOGG_ERROR, "Mutex lock failed\n");
|
||||
}
|
||||
locked = true;
|
||||
|
||||
if (!_cert_store.loaded) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Load system root ca certs into list */
|
||||
for (i = 0; i < _cert_store.system_certs.count; ++i) {
|
||||
if (_x509_to_pem_append(_cert_store.system_certs.certificates[i],
|
||||
cert_data,
|
||||
cert_data_len,
|
||||
&remaining_buf_len) != 0) {
|
||||
goto done;
|
||||
}
|
||||
/*
|
||||
* Two certs by the same name can cause conflicts. Trust the
|
||||
* one in the OS certificate/key store if the additional CA
|
||||
* name matches that of one in the store.
|
||||
*/
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
/* OpenSSL >= 1.1.0 */
|
||||
if (additional_ca_cert) {
|
||||
int cmp = 0;
|
||||
if (CL_SUCCESS == x509_cert_name_cmp(_cert_store.system_certs.certificates[i],
|
||||
additional_ca_cert,
|
||||
&cmp)) {
|
||||
if (0 == cmp)
|
||||
add_additional_ca_cert = false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* OpenSSL <= 1.0.2 */
|
||||
if (additional_ca_cert && additional_ca_cert->cert_info &&
|
||||
(strcmp(_cert_store.system_certs.certificates[i]->name,
|
||||
additional_ca_cert->name) == 0)) {
|
||||
add_additional_ca_cert = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Load trusted ca certs into list */
|
||||
for (i = 0; i < _cert_store.trusted_certs.count; ++i) {
|
||||
if (_x509_to_pem_append(_cert_store.trusted_certs.certificates[i],
|
||||
cert_data,
|
||||
cert_data_len,
|
||||
&remaining_buf_len) != 0) {
|
||||
goto done;
|
||||
}
|
||||
/*
|
||||
* Two certs by the same name can cause conflicts. Trust the
|
||||
* one in the OS certificate/key store if the additional CA
|
||||
* name matches that of one in the store.
|
||||
*/
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
/* OpenSSL >= 1.1.0 */
|
||||
if (additional_ca_cert) {
|
||||
int cmp = 0;
|
||||
if (CL_SUCCESS == x509_cert_name_cmp(_cert_store.trusted_certs.certificates[i],
|
||||
additional_ca_cert,
|
||||
&cmp)) {
|
||||
if (0 == cmp)
|
||||
add_additional_ca_cert = false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* OpenSSL <= 1.0.2 */
|
||||
if (additional_ca_cert && additional_ca_cert->cert_info &&
|
||||
(strcmp(_cert_store.trusted_certs.certificates[i]->name,
|
||||
additional_ca_cert->name) == 0)) {
|
||||
add_additional_ca_cert = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* End with the additional CA certificate if provided */
|
||||
if (additional_ca_cert && add_additional_ca_cert && *cert_data) {
|
||||
/* Return an error only if we were unable to allocate memory */
|
||||
if (_x509_to_pem_append(additional_ca_cert,
|
||||
cert_data,
|
||||
cert_data_len,
|
||||
&remaining_buf_len) != 0) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
ret = CL_SUCCESS;
|
||||
done:
|
||||
if (locked) {
|
||||
pt_err = pthread_mutex_unlock(&_cert_store.mutex);
|
||||
if (pt_err) {
|
||||
errno = pt_err;
|
||||
mprintf(LOGG_ERROR, "Mutex unlock failed\n");
|
||||
}
|
||||
locked = false;
|
||||
}
|
||||
|
||||
if (ret != CL_SUCCESS && cert_data && *cert_data) {
|
||||
free(*cert_data);
|
||||
*cert_data = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
cl_error_t cert_store_set_trusted_int(X509 **trusted_certs, size_t trusted_cert_count)
|
||||
{
|
||||
cl_error_t ret = CL_EOPEN;
|
||||
size_t i, j;
|
||||
cert_list_t tmp_trusted = {0};
|
||||
|
||||
do {
|
||||
if ((trusted_certs == NULL) || (trusted_cert_count == 0)) {
|
||||
mprintf(LOGG_ERROR, "Empty trusted certificate list\n");
|
||||
break;
|
||||
}
|
||||
|
||||
tmp_trusted.certificates = calloc(trusted_cert_count,
|
||||
sizeof(*tmp_trusted.certificates));
|
||||
if (!tmp_trusted.certificates) {
|
||||
mprintf(LOGG_ERROR, "Failed to reserve memory for trusted certs\n");
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < trusted_cert_count; ++i) {
|
||||
bool found = false;
|
||||
|
||||
/* Check if certificate already exists in system root cert list */
|
||||
for (j = 0; j < _cert_store.system_certs.count; ++j) {
|
||||
if (X509_cmp(trusted_certs[i],
|
||||
_cert_store.system_certs.certificates[j]) == 0) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
continue; /* certificate is already found in cert store */
|
||||
}
|
||||
|
||||
tmp_trusted.certificates[tmp_trusted.count] =
|
||||
X509_dup(trusted_certs[i]);
|
||||
if (!tmp_trusted.certificates[tmp_trusted.count]) {
|
||||
mprintf(LOGG_ERROR, "X509_dup failed at index: %zu\n", i);
|
||||
continue; /* continue on error */
|
||||
}
|
||||
|
||||
tmp_trusted.count++;
|
||||
}
|
||||
|
||||
cert_store_free_cert_list_int(&_cert_store.trusted_certs);
|
||||
|
||||
_cert_store.trusted_certs.certificates = tmp_trusted.certificates;
|
||||
_cert_store.trusted_certs.count = tmp_trusted.count;
|
||||
|
||||
tmp_trusted.certificates = NULL;
|
||||
tmp_trusted.count = 0;
|
||||
|
||||
ret = CL_SUCCESS;
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
cl_error_t cert_store_set_trusted(X509 **trusted_certs, size_t trusted_cert_count)
|
||||
{
|
||||
cl_error_t ret = CL_EOPEN;
|
||||
int pt_err;
|
||||
|
||||
pt_err = pthread_mutex_lock(&_cert_store.mutex);
|
||||
if (pt_err) {
|
||||
errno = pt_err;
|
||||
mprintf(LOGG_ERROR, "Mutex lock failed\n");
|
||||
}
|
||||
|
||||
if (_cert_store.loaded) {
|
||||
ret = cert_store_set_trusted_int(trusted_certs, trusted_cert_count);
|
||||
}
|
||||
|
||||
pt_err = pthread_mutex_unlock(&_cert_store.mutex);
|
||||
if (pt_err) {
|
||||
errno = pt_err;
|
||||
mprintf(LOGG_ERROR, "Mutex unlock failed\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t cert_store_remove_trusted(void)
|
||||
{
|
||||
size_t count = 0;
|
||||
int pt_err;
|
||||
|
||||
pt_err = pthread_mutex_lock(&_cert_store.mutex);
|
||||
if (pt_err) {
|
||||
errno = pt_err;
|
||||
mprintf(LOGG_ERROR, "Mutex lock failed\n");
|
||||
}
|
||||
|
||||
if (_cert_store.loaded) {
|
||||
count = _cert_store.trusted_certs.count;
|
||||
cert_store_free_cert_list_int(&_cert_store.trusted_certs);
|
||||
}
|
||||
|
||||
pt_err = pthread_mutex_unlock(&_cert_store.mutex);
|
||||
if (pt_err) {
|
||||
errno = pt_err;
|
||||
mprintf(LOGG_ERROR, "Mutex unlock failed\n");
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void cert_fill_X509_store(X509_STORE *store, X509 **certs, size_t cert_count)
|
||||
{
|
||||
size_t i;
|
||||
unsigned long err;
|
||||
|
||||
if (store && certs && cert_count > 0) {
|
||||
for (i = 0; i < cert_count; ++i) {
|
||||
if (!certs[i]) {
|
||||
mprintf(LOGG_ERROR, "NULL cert at index %zu in X509 cert list; skipping\n", i);
|
||||
continue;
|
||||
}
|
||||
if (X509_STORE_add_cert(store, certs[i]) != 1) {
|
||||
char *name = NULL;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
x509_get_cert_name(certs[i], &name);
|
||||
#else
|
||||
name = certs[i]->name;
|
||||
#endif
|
||||
err = ERR_get_error();
|
||||
if (X509_R_CERT_ALREADY_IN_HASH_TABLE == ERR_GET_REASON(err)) {
|
||||
mprintf(LOGG_DEBUG, "Certificate skipped; already exists in store: %s\n",
|
||||
(name ? name : ""));
|
||||
} else {
|
||||
mprintf(LOGG_ERROR, "Failed to add certificate to store: %s (%lu) [%s]\n",
|
||||
ERR_error_string(err, NULL), err,
|
||||
(name ? name : ""));
|
||||
}
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
if (NULL != name) {
|
||||
free(name);
|
||||
name = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cert_store_export_certs(X509_STORE *store, X509 *additional_ca_cert)
|
||||
{
|
||||
cert_store_t *cert_store = NULL;
|
||||
int pt_err;
|
||||
|
||||
do {
|
||||
if (!store) {
|
||||
mprintf(LOGG_ERROR, "NULL X509 store\n");
|
||||
break;
|
||||
}
|
||||
|
||||
cert_store = cert_store_get_int();
|
||||
if (!cert_store) {
|
||||
mprintf(LOGG_ERROR, "Failed to retrieve cert store\n");
|
||||
break;
|
||||
}
|
||||
|
||||
pt_err = pthread_mutex_lock(&cert_store->mutex);
|
||||
if (pt_err) {
|
||||
errno = pt_err;
|
||||
mprintf(LOGG_ERROR, "Mutex lock failed\n");
|
||||
}
|
||||
|
||||
if (!cert_store->loaded) {
|
||||
mprintf(LOGG_ERROR, "Cert store not loaded\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* On Linux, system certificates are loaded by OpenSSL */
|
||||
#if defined(_WIN32) || defined(DARWIN)
|
||||
cert_fill_X509_store(store,
|
||||
cert_store->system_certs.certificates,
|
||||
cert_store->system_certs.count);
|
||||
#endif
|
||||
|
||||
cert_fill_X509_store(store,
|
||||
cert_store->trusted_certs.certificates,
|
||||
cert_store->trusted_certs.count);
|
||||
|
||||
/* Adding the additional CA cert to the trustchain */
|
||||
if ((additional_ca_cert != NULL) &&
|
||||
(X509_STORE_add_cert(store, additional_ca_cert) != 1)) {
|
||||
char *name = NULL;
|
||||
unsigned long err = ERR_get_error();
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
x509_get_cert_name(additional_ca_cert, &name);
|
||||
#else
|
||||
name = additional_ca_cert->name;
|
||||
#endif
|
||||
if (X509_R_CERT_ALREADY_IN_HASH_TABLE == ERR_GET_REASON(err)) {
|
||||
mprintf(LOGG_INFO, "Certificate is already in trust [%s]\n",
|
||||
(name ? name : ""));
|
||||
} else {
|
||||
mprintf(LOGG_ERROR, "Failed to add CA certificate for the SSL context. "
|
||||
"Error: %d [%s]\n",
|
||||
ERR_GET_REASON(err),
|
||||
(name ? name : ""));
|
||||
}
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
if (NULL != name) {
|
||||
free(name);
|
||||
name = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (cert_store) {
|
||||
pt_err = pthread_mutex_unlock(&cert_store->mutex);
|
||||
if (pt_err) {
|
||||
errno = pt_err;
|
||||
mprintf(LOGG_ERROR, "Mutex unlock failed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CURLcode sslctx_function(CURL *curl, void *ssl_ctx, void *userptr)
|
||||
{
|
||||
CURLcode status = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
cert_store_t *cert_store = NULL;
|
||||
|
||||
UNUSEDPARAM(curl);
|
||||
UNUSEDPARAM(userptr);
|
||||
|
||||
cert_store = cert_store_get_int();
|
||||
if (!cert_store) {
|
||||
mprintf(LOGG_ERROR, "Failed to retrieve cert store\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!cert_store->loaded) {
|
||||
if (CL_SUCCESS != cert_store_load(NULL, 0)) {
|
||||
mprintf(LOGG_ERROR, "Failed to load cert store\n");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
X509_STORE *store = SSL_CTX_get_cert_store((SSL_CTX *)ssl_ctx);
|
||||
|
||||
cert_store_export_certs(store, NULL);
|
||||
|
||||
status = CURLE_OK;
|
||||
|
||||
done:
|
||||
|
||||
return status;
|
||||
}
|
||||
130
clamav/common/cert_util.h
Normal file
130
clamav/common/cert_util.h
Normal 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
|
||||
93
clamav/common/cert_util_internal.h
Normal file
93
clamav/common/cert_util_internal.h
Normal 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
483
clamav/common/clamdcom.c
Normal file
@@ -0,0 +1,483 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
* Copyright (C) 2009-2013 Sourcefire, Inc.
|
||||
*
|
||||
* Author: aCaB
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "clamav-config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#if HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
#include "clamav.h"
|
||||
#include "actions.h"
|
||||
#include "output.h"
|
||||
#include "clamdcom.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
struct sockaddr_un nixsock;
|
||||
#endif
|
||||
|
||||
static const char *scancmd[] = {"CONTSCAN", "MULTISCAN", "INSTREAM", "FILDES", "ALLMATCHSCAN"};
|
||||
|
||||
/* Sends bytes over a socket
|
||||
* Returns 0 on success */
|
||||
int sendln(int sockd, const char *line, unsigned int len)
|
||||
{
|
||||
while (len) {
|
||||
int sent = send(sockd, line, len, 0);
|
||||
if (sent <= 0) {
|
||||
if (sent && errno == EINTR) continue;
|
||||
logg(LOGG_ERROR, "Can't send to clamd: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
line += sent;
|
||||
len -= sent;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Inits a RECVLN struct before it can be used in recvln() - see below */
|
||||
void recvlninit(struct RCVLN *s, int sockd)
|
||||
{
|
||||
s->sockd = sockd;
|
||||
s->bol = s->cur = s->buf;
|
||||
s->r = 0;
|
||||
}
|
||||
|
||||
/* Receives a full (terminated with \0) line from a socket
|
||||
* Sets rbol to the begin of the received line, and optionally
|
||||
* reol to the end of line.
|
||||
* Should be called repeatedly until all input is consumed
|
||||
* Returns:
|
||||
* - the length of the line (a positive number) on success
|
||||
* - 0 if the connection is closed
|
||||
* - -1 on error
|
||||
*/
|
||||
int recvln(struct RCVLN *s, char **rbol, char **reol)
|
||||
{
|
||||
char *eol;
|
||||
|
||||
while (1) {
|
||||
if (!s->r) {
|
||||
s->r = recv(s->sockd, s->cur, sizeof(s->buf) - (s->cur - s->buf), 0);
|
||||
if (s->r <= 0) {
|
||||
if (s->r && errno == EINTR) {
|
||||
s->r = 0;
|
||||
continue;
|
||||
}
|
||||
if (s->r || s->cur != s->buf) {
|
||||
*s->cur = '\0';
|
||||
if (strcmp(s->buf, "UNKNOWN COMMAND\n"))
|
||||
logg(LOGG_ERROR, "Communication error\n");
|
||||
else
|
||||
logg(LOGG_ERROR, "Command rejected by clamd (wrong clamd version?)\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if ((eol = memchr(s->cur, 0, s->r))) {
|
||||
int ret = 0;
|
||||
eol++;
|
||||
s->r -= eol - s->cur;
|
||||
*rbol = s->bol;
|
||||
if (reol) *reol = eol;
|
||||
ret = eol - s->bol;
|
||||
if (s->r)
|
||||
s->bol = s->cur = eol;
|
||||
else
|
||||
s->bol = s->cur = s->buf;
|
||||
return ret;
|
||||
}
|
||||
s->r += s->cur - s->bol;
|
||||
if (!eol && s->r == sizeof(s->buf)) {
|
||||
logg(LOGG_ERROR, "Overlong reply from clamd\n");
|
||||
return -1;
|
||||
}
|
||||
if (!eol) {
|
||||
if (s->buf != s->bol) { /* old memmove sux */
|
||||
memmove(s->buf, s->bol, s->r);
|
||||
s->bol = s->buf;
|
||||
}
|
||||
s->cur = &s->bol[s->r];
|
||||
s->r = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Determines if a path should be excluded
|
||||
* 0: scan, 1: skip */
|
||||
int chkpath(const char *path, struct optstruct *clamdopts)
|
||||
{
|
||||
int status = 0;
|
||||
const struct optstruct *opt;
|
||||
char *real_path = NULL;
|
||||
|
||||
if (!path) {
|
||||
status = 1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((opt = optget(clamdopts, "ExcludePath"))->enabled) {
|
||||
while (opt) {
|
||||
if (match_regex(path, opt->strarg) == 1) {
|
||||
logg(LOGG_DEBUG, "%s: Excluded\n", path);
|
||||
status = 1;
|
||||
goto done;
|
||||
}
|
||||
opt = opt->nextarg;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (NULL != real_path) {
|
||||
free(real_path);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef HAVE_FD_PASSING
|
||||
/* Issues a FILDES command and pass a FD to clamd
|
||||
* Returns >0 on success, 0 soft fail, -1 hard fail */
|
||||
int send_fdpass(int sockd, const char *filename)
|
||||
{
|
||||
struct iovec iov[1];
|
||||
struct msghdr msg;
|
||||
struct cmsghdr *cmsg;
|
||||
unsigned char fdbuf[CMSG_SPACE(sizeof(int))];
|
||||
char dummy[] = "";
|
||||
int fd;
|
||||
const char zFILDES[] = "zFILDES";
|
||||
|
||||
if (filename) {
|
||||
if ((fd = open(filename, O_RDONLY)) < 0) {
|
||||
logg(LOGG_INFO, "%s: Failed to open file\n", filename);
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
fd = 0;
|
||||
if (sendln(sockd, zFILDES, sizeof(zFILDES))) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
iov[0].iov_base = dummy;
|
||||
iov[0].iov_len = 1;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msg_control = fdbuf;
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_controllen = CMSG_LEN(sizeof(int));
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
*(int *)CMSG_DATA(cmsg) = fd;
|
||||
if (sendmsg(sockd, &msg, 0) == -1) {
|
||||
logg(LOGG_ERROR, "FD send failed: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Issues an INSTREAM command to clamd and streams the given file
|
||||
* Returns >0 on success, 0 soft fail, -1 hard fail */
|
||||
int send_stream(int sockd, const char *filename, struct optstruct *clamdopts)
|
||||
{
|
||||
uint32_t buf[BUFSIZ / sizeof(uint32_t)];
|
||||
int fd, len;
|
||||
unsigned long int todo = optget(clamdopts, "StreamMaxLength")->numarg;
|
||||
const char zINSTREAM[] = "zINSTREAM";
|
||||
|
||||
if (filename) {
|
||||
if ((fd = safe_open(filename, O_RDONLY | O_BINARY)) < 0) {
|
||||
logg(LOGG_INFO, "%s: Failed to open file. ERROR\n", filename);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/* Read stream from STDIN */
|
||||
fd = 0;
|
||||
}
|
||||
|
||||
if (sendln(sockd, zINSTREAM, sizeof(zINSTREAM))) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((len = read(fd, &buf[1], sizeof(buf) - sizeof(uint32_t))) > 0) {
|
||||
if ((unsigned int)len > todo) len = todo;
|
||||
buf[0] = htonl(len);
|
||||
if (sendln(sockd, (const char *)buf, len + sizeof(uint32_t))) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
todo -= len;
|
||||
if (!todo) {
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
if (len) {
|
||||
logg(LOGG_ERROR, "Failed to read from %s.\n", filename ? filename : "STDIN");
|
||||
return 0;
|
||||
}
|
||||
*buf = 0;
|
||||
sendln(sockd, (const char *)buf, 4);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Connects to clamd
|
||||
* Returns a FD or -1 on error */
|
||||
int dconnect(struct optstruct *clamdopts)
|
||||
{
|
||||
int sockd, res;
|
||||
const struct optstruct *opt;
|
||||
struct addrinfo hints, *info, *p;
|
||||
char port[10];
|
||||
char *ipaddr;
|
||||
|
||||
#ifndef _WIN32
|
||||
opt = optget(clamdopts, "LocalSocket");
|
||||
if (opt->enabled) {
|
||||
if ((sockd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) {
|
||||
if (connect(sockd, (struct sockaddr *)&nixsock, sizeof(nixsock)) == 0)
|
||||
return sockd;
|
||||
else {
|
||||
logg(LOGG_ERROR, "Could not connect to clamd on LocalSocket %s: %s\n", opt->strarg, strerror(errno));
|
||||
close(sockd);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
snprintf(port, sizeof(port), "%lld", optget(clamdopts, "TCPSocket")->numarg);
|
||||
|
||||
opt = optget(clamdopts, "TCPAddr");
|
||||
while (opt) {
|
||||
if (opt->enabled) {
|
||||
ipaddr = NULL;
|
||||
if (opt->strarg)
|
||||
ipaddr = (!strcmp(opt->strarg, "any") ? NULL : opt->strarg);
|
||||
|
||||
memset(&hints, 0x00, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if ((res = getaddrinfo(ipaddr, port, &hints, &info))) {
|
||||
logg(LOGG_ERROR, "Could not lookup %s: %s\n", ipaddr ? ipaddr : "", gai_strerror(res));
|
||||
opt = opt->nextarg;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (p = info; p != NULL; p = p->ai_next) {
|
||||
if ((sockd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) {
|
||||
logg(LOGG_ERROR, "Can't create the socket: %s\n", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connect(sockd, p->ai_addr, p->ai_addrlen) < 0) {
|
||||
logg(LOGG_ERROR, "Could not connect to clamd on %s: %s\n", opt->strarg, strerror(errno));
|
||||
closesocket(sockd);
|
||||
continue;
|
||||
}
|
||||
|
||||
freeaddrinfo(info);
|
||||
return sockd;
|
||||
}
|
||||
|
||||
freeaddrinfo(info);
|
||||
}
|
||||
opt = opt->nextarg;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Sends a proper scan request to clamd and parses its replies
|
||||
* This is used only in non IDSESSION mode
|
||||
* Returns the number of infected files or -1 on error
|
||||
* NOTE: filename may be NULL for STREAM scantype. */
|
||||
int dsresult(int sockd, int scantype, const char *filename, int *printok, int *errors, struct optstruct *clamdopts)
|
||||
{
|
||||
int infected = 0, len = 0, beenthere = 0;
|
||||
char *bol, *eol;
|
||||
struct RCVLN rcv;
|
||||
STATBUF sb;
|
||||
|
||||
if (filename) {
|
||||
if (1 == chkpath(filename, clamdopts)) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
recvlninit(&rcv, sockd);
|
||||
|
||||
switch (scantype) {
|
||||
case MULTI:
|
||||
case CONT:
|
||||
case ALLMATCH:
|
||||
if (!filename) {
|
||||
logg(LOGG_INFO, "Filename cannot be NULL for MULTISCAN or CONTSCAN.\n");
|
||||
infected = -1;
|
||||
goto done;
|
||||
}
|
||||
len = strlen(filename) + strlen(scancmd[scantype]) + 3;
|
||||
if (!(bol = malloc(len))) {
|
||||
logg(LOGG_ERROR, "Cannot allocate a command buffer: %s\n", strerror(errno));
|
||||
infected = -1;
|
||||
goto done;
|
||||
}
|
||||
sprintf(bol, "z%s %s", scancmd[scantype], filename);
|
||||
if (sendln(sockd, bol, len)) {
|
||||
free(bol);
|
||||
infected = -1;
|
||||
goto done;
|
||||
}
|
||||
free(bol);
|
||||
break;
|
||||
|
||||
case STREAM:
|
||||
/* NULL filename safe in send_stream() */
|
||||
len = send_stream(sockd, filename, clamdopts);
|
||||
break;
|
||||
#ifdef HAVE_FD_PASSING
|
||||
case FILDES:
|
||||
/* NULL filename safe in send_fdpass() */
|
||||
len = send_fdpass(sockd, filename);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (len <= 0) {
|
||||
if (printok)
|
||||
*printok = 0;
|
||||
if (errors)
|
||||
(*errors)++;
|
||||
infected = len;
|
||||
goto done;
|
||||
}
|
||||
|
||||
while ((len = recvln(&rcv, &bol, &eol))) {
|
||||
if (len == -1) {
|
||||
infected = -1;
|
||||
goto done;
|
||||
}
|
||||
beenthere = 1;
|
||||
if (!filename) logg(LOGG_INFO, "%s\n", bol);
|
||||
if (len > 7) {
|
||||
char *colon = strrchr(bol, ':');
|
||||
if (colon && colon[1] != ' ') {
|
||||
char *br;
|
||||
*colon = 0;
|
||||
br = strrchr(bol, '(');
|
||||
if (br)
|
||||
*br = 0;
|
||||
colon = strrchr(bol, ':');
|
||||
}
|
||||
if (!colon) {
|
||||
char *unkco = "UNKNOWN COMMAND";
|
||||
if (!strncmp(bol, unkco, sizeof(unkco) - 1))
|
||||
logg(LOGG_INFO, "clamd replied \"UNKNOWN COMMAND\". Command was %s\n",
|
||||
(scantype < 0 || scantype > MAX_SCANTYPE) ? "unidentified" : scancmd[scantype]);
|
||||
else
|
||||
logg(LOGG_INFO, "Failed to parse reply: \"%s\"\n", bol);
|
||||
infected = -1;
|
||||
goto done;
|
||||
} else if (!memcmp(eol - 7, " FOUND", 6)) {
|
||||
static char last_filename[PATH_MAX + 1] = {'\0'};
|
||||
*(eol - 7) = 0;
|
||||
if (printok)
|
||||
*printok = 0;
|
||||
if (scantype != ALLMATCH) {
|
||||
infected++;
|
||||
} else {
|
||||
if (filename != NULL && strcmp(filename, last_filename)) {
|
||||
infected++;
|
||||
strncpy(last_filename, filename, PATH_MAX);
|
||||
last_filename[PATH_MAX] = '\0';
|
||||
}
|
||||
}
|
||||
if (filename) {
|
||||
if (scantype >= STREAM) {
|
||||
logg(LOGG_INFO, "%s%s FOUND\n", filename, colon);
|
||||
if (action) action(filename);
|
||||
} else {
|
||||
logg(LOGG_INFO, "%s FOUND\n", bol);
|
||||
*colon = '\0';
|
||||
if (action)
|
||||
action(bol);
|
||||
}
|
||||
}
|
||||
} else if (!memcmp(eol - 7, " ERROR", 6)) {
|
||||
if (errors)
|
||||
(*errors)++;
|
||||
if (printok)
|
||||
*printok = 0;
|
||||
if (filename) {
|
||||
if (scantype >= STREAM)
|
||||
logg(LOGG_INFO, "%s%s\n", filename, colon);
|
||||
else
|
||||
logg(LOGG_INFO, "%s\n", bol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!beenthere) {
|
||||
if (!filename) {
|
||||
logg(LOGG_INFO, "STDIN: noreply from clamd\n.");
|
||||
infected = -1;
|
||||
goto done;
|
||||
}
|
||||
if (CLAMSTAT(filename, &sb) == -1) {
|
||||
logg(LOGG_INFO, "%s: stat() failed with %s, clamd may not be responding\n",
|
||||
filename, strerror(errno));
|
||||
infected = -1;
|
||||
goto done;
|
||||
}
|
||||
if (!S_ISDIR(sb.st_mode)) {
|
||||
logg(LOGG_INFO, "%s: no reply from clamd\n", filename);
|
||||
infected = -1;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
return infected;
|
||||
}
|
||||
67
clamav/common/clamdcom.h
Normal file
67
clamav/common/clamdcom.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
* Copyright (C) 2009-2013 Sourcefire, Inc.
|
||||
*
|
||||
* Author: aCaB
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __CLAMDCOM_H
|
||||
#define __CLAMDCOM_H
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "clamav-config.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
enum {
|
||||
CONT,
|
||||
MULTI,
|
||||
STREAM,
|
||||
FILDES,
|
||||
ALLMATCH,
|
||||
MAX_SCANTYPE = ALLMATCH
|
||||
};
|
||||
|
||||
struct RCVLN {
|
||||
char buf[PATH_MAX + 1024]; /* FIXME must match that in clamd - bb1349 */
|
||||
int sockd;
|
||||
int r;
|
||||
char *cur;
|
||||
char *bol;
|
||||
};
|
||||
|
||||
#ifndef _WIN32
|
||||
extern struct sockaddr_un nixsock;
|
||||
#endif
|
||||
|
||||
int sendln(int sockd, const char *line, unsigned int len);
|
||||
void recvlninit(struct RCVLN *s, int sockd);
|
||||
int recvln(struct RCVLN *s, char **rbol, char **reol);
|
||||
|
||||
int chkpath(const char *path, struct optstruct *clamdopts);
|
||||
#ifdef HAVE_FD_PASSING
|
||||
int send_fdpass(int sockd, const char *filename);
|
||||
#endif
|
||||
int send_stream(int sockd, const char *filename, struct optstruct *clamdopts);
|
||||
int dconnect(struct optstruct *clamdopts);
|
||||
int dsresult(int sockd, int scantype, const char *filename, int *printok, int *errors, struct optstruct *clamdopts);
|
||||
#endif
|
||||
330
clamav/common/exeScanner.c
Normal file
330
clamav/common/exeScanner.c
Normal file
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
* Copyright (C) 2005-2010 Gianluigi Tiesi <sherpya@netfarm.it>
|
||||
*
|
||||
* Authors: Gianluigi Tiesi
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "exescanner.h"
|
||||
|
||||
/* -1 = wildchar - -2 = stop here */
|
||||
sigs_t signatures[] = {
|
||||
{{0x60, 0xbe, -1, -1, -1, -1, 0x8d, 0xbe, -1, -1, -1, 0xff, 0x57, -2},
|
||||
"UPX",
|
||||
.0f},
|
||||
{{0x94, 0xbc, 0x5d, 0x07, 0x42, 0x00, 0xb9, 0x1d, 0x00, 0x00, 0x00, 0x80,
|
||||
0x34, 0x0c, 0x44, 0xe2},
|
||||
"UPXSHiT",
|
||||
.0f},
|
||||
{{0xbe, 0xa4, 0x01, 0x40, 0x00, 0xad, 0x93, 0xad, 0x97, 0xad, 0x56, 0x96,
|
||||
0xb2, 0x80, 0xa4, 0xb6},
|
||||
"FSG 1.33",
|
||||
.0f},
|
||||
{{0x4d, 0x5a, -1, -1, -1, -1, -1, -1, -1, -1, 0x00, 0x00, 0x50, 0x45, 0x00,
|
||||
0x00},
|
||||
"FSG 2.00",
|
||||
.0f},
|
||||
{{0x4d, 0x5a, 0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c, 0x33, 0x32, 0x2e, 0x44,
|
||||
0x4c, 0x4c, 0x00, 0x00},
|
||||
"WinUpack 0.39",
|
||||
.0f},
|
||||
{{0xbe, 0x88, 0x01, 0x40, 0x00, 0xad, 0x8b, 0xf8, 0x95, 0xad, 0x91, 0xf3,
|
||||
0xa5, 0xad, 0xb5, 0x1c},
|
||||
"Upack 2.4/2.9",
|
||||
.0f},
|
||||
{{0xbe, 0x48, 0x01, 0x40, 0x00, 0xad, 0x8b, 0xf8, 0x95, 0xa5, 0x33, 0xc0,
|
||||
0x33, 0xc9, 0xab, 0x48},
|
||||
"Upack 1.1/1.2",
|
||||
.0f},
|
||||
{{0x83, 0xec, 0x20, 0x53, 0x55, 0x56, 0x33, 0xdb, 0x57, 0x89, 0x5c, 0x24,
|
||||
0x18, 0xc7, 0x44, 0x24},
|
||||
"NullSoft PiMP",
|
||||
.0f},
|
||||
{{0xe9, -1, -1, -1, 0xff, 0x0c, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00},
|
||||
"Mew 11 1.2",
|
||||
.0f},
|
||||
{{0x60, 0xe9, 0x3d, 0x04, 0x00, 0x00, -2}, "ASPack 2.11", .0f},
|
||||
{{0x60, 0xe8, 0x03, 0x00, 0x00, 0x00, 0xe9, 0xeb, 0x04, 0x5d, 0x45, 0x55,
|
||||
0xc3, 0xe8, 0x01, 0x00},
|
||||
"ASPack 2.12",
|
||||
.0f},
|
||||
{{0x55, 0x83, 0xc4, 0x04, 0x76, 0x08, 0x7a, 0x06, 0x74, 0x04, 0x66, 0x83,
|
||||
0xea, 0x00, 0xf5, 0x50},
|
||||
"Morphine 1.4/2.7",
|
||||
.0f},
|
||||
{{0x56, 0x72, 0x05, 0x05, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x0b, 0xd2, 0xf9,
|
||||
0x84, 0xdb, 0x68, 0x34},
|
||||
"Morphine 1.4/2.7 [2]",
|
||||
.0f},
|
||||
{{0x53, 0x51, 0x52, 0x56, 0x57, 0x55, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x5d,
|
||||
0x8b, 0xd5, 0x81, 0xed},
|
||||
"PEDiminisher 0.1",
|
||||
.0f},
|
||||
|
||||
{{0xe8, 0xf6, 0x03, 0x00, 0x00, 0xe9, 0x9e, 0xfd, 0xff, 0xff, 0xcc, 0xcc,
|
||||
0xcc, 0xcc, 0xcc, 0xcc},
|
||||
"MSVC8 Release",
|
||||
-1.0f},
|
||||
{{0xe9, -1, -1, -1, 0x00, 0xe9, -1, -1, -1, 0x00, 0xe9, -1, -1, -1, 0x00,
|
||||
0xe9},
|
||||
"MSVC8 Debug",
|
||||
-1.0f},
|
||||
|
||||
{{0xe8, -1, -1, 0x00, 0x00, 0xe9, 0x16, 0xfe, 0xff, 0xff, -2},
|
||||
"MSVC6 Release",
|
||||
-2.0f},
|
||||
|
||||
{{0xe9, 0x96, 0xee, 0x0e, 0x00, 0xb8, 0x6c, 0x02, 0x58, 0x00, 0xe8, 0xae,
|
||||
0xe4, 0x0e, 0x00, 0x83},
|
||||
"MSVC6 Release (2)",
|
||||
-1.0f},
|
||||
{{0x55, 0x8b, 0xec, 0x6a, 0xff, 0x68, 0xb0, 0x41, 0x40, 0x00, 0x68, 0x10,
|
||||
0x36, 0x40, 0x00, 0x64},
|
||||
"MSVC6 Release (3)",
|
||||
-1.0f},
|
||||
{{0x55, 0x8b, 0xec, 0x53, 0x8b, 0x5d, 0x08, 0x56, 0x8b, 0x75, 0x0c, 0x57,
|
||||
0x8b, 0x7d, 0x10, 0x85},
|
||||
"MSVC6 Release (4)",
|
||||
-1.0f},
|
||||
|
||||
{{0x83, 0x7c, 0x24, 0x08, 0x01, 0x75, 0x05, 0xe8, -1, -1, 0x00, 0x00, 0xff,
|
||||
0x74, 0x24, 0x04},
|
||||
"MSVC6 Release DLL",
|
||||
-1.0f},
|
||||
{{0xff, 0x25, -1, -1, -1, -1, 0xcc, 0xcc, 0x03, 0x30, 0x01, 0x00, 0x07,
|
||||
0x00, 0x00, 0x00},
|
||||
"DotNet",
|
||||
-1.0f},
|
||||
{{0x55, 0x89, 0xe5, -2}, "MinGW", -1.0f},
|
||||
{{0}, 0, 0}};
|
||||
|
||||
int sigcmp(const uint8_t *data, const int16_t *sig, size_t n)
|
||||
{
|
||||
uint8_t *d = (uint8_t *)data;
|
||||
int16_t *s = (int16_t *)sig;
|
||||
while (n-- != 0) {
|
||||
if (*s == -2)
|
||||
return 0;
|
||||
if ((*s != -1) && (*d != *s))
|
||||
return (*d < *s) ? -1 : +1;
|
||||
d++;
|
||||
s++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sigs_t *checksig(uint8_t *data)
|
||||
{
|
||||
int i = 0;
|
||||
while (signatures[i].name) {
|
||||
if (!sigcmp(data, signatures[i].sig, 16))
|
||||
return &signatures[i];
|
||||
i++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
double calc_entropy(const unsigned char *data, size_t size)
|
||||
{
|
||||
double entropy = .0f;
|
||||
size_t p[256];
|
||||
size_t c, i;
|
||||
|
||||
memset(p, 0, sizeof(p));
|
||||
|
||||
for (c = 0; c < size; c++)
|
||||
p[data[c]]++;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
if (p[i])
|
||||
entropy -= ((double)p[i] / size) * log((double)p[i] / size);
|
||||
return entropy;
|
||||
}
|
||||
|
||||
#define FILLBYTES(dst) \
|
||||
if (IsBadReadPtr(seek, sizeof(dst))) { \
|
||||
logg(LOGG_ERROR, "exeScanner: Bad pointer!!!\n"); \
|
||||
goto cleanup; \
|
||||
} \
|
||||
memcpy(&dst, seek, sizeof(dst));
|
||||
|
||||
/* Packed exe heuristic detection, errors are handled as like of non packed data
|
||||
*/
|
||||
int is_packed(const char *filename)
|
||||
{
|
||||
int packed = 0;
|
||||
int i = 0, c = 0;
|
||||
int badsection = 0;
|
||||
double entropy = 0.0;
|
||||
sigs_t *sig = NULL;
|
||||
uint16_t e_mz;
|
||||
uint32_t e_lfanew, e_magic;
|
||||
uint32_t epoff = 0;
|
||||
unsigned char *seek = NULL, *s_start = NULL, *ep = NULL, *lpMapAddress = NULL;
|
||||
PIMAGE_FILE_HEADER pehdr;
|
||||
PIMAGE_OPTIONAL_HEADER32 opthdr;
|
||||
PIMAGE_SECTION_HEADER sechdr;
|
||||
char secname[IMAGE_SIZEOF_SHORT_NAME];
|
||||
|
||||
HANDLE hFile = INVALID_HANDLE_VALUE, hMapFile = NULL;
|
||||
|
||||
hFile = CreateFileA(filename, GENERIC_READ, 0, NULL, OPEN_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
elogg(LOGG_INFO, "exeScanner: CreateFileA failed %lu\n", GetLastError());
|
||||
return packed; /* Returning packed, the module is loaded so it must exists
|
||||
on disk */
|
||||
}
|
||||
|
||||
hMapFile = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, "exeScanner");
|
||||
if (!hMapFile) {
|
||||
elogg(LOGG_INFO, "exeScanner: CreateFileMappingA() failed %lu\n", GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
lpMapAddress = (LPBYTE)MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0);
|
||||
if (!lpMapAddress) {
|
||||
elogg(LOGG_INFO, "exeScanner: MapViewOfFile() failed %lu\n", GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
seek = lpMapAddress;
|
||||
|
||||
/* DOS Signature 'MZ' */
|
||||
FILLBYTES(e_mz);
|
||||
if (e_mz != IMAGE_DOS_SIGNATURE) {
|
||||
elogg(LOGG_INFO, "exeScanner: DOS Signature not found\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
seek += 0x3c;
|
||||
|
||||
FILLBYTES(e_lfanew);
|
||||
if (!e_lfanew) {
|
||||
elogg(LOGG_INFO, "exeScanner: Invalid PE offset\n");
|
||||
goto cleanup;
|
||||
}
|
||||
seek = lpMapAddress + e_lfanew;
|
||||
|
||||
/* PE Signature 'PE' */
|
||||
FILLBYTES(e_magic);
|
||||
if (e_magic != IMAGE_NT_SIGNATURE) {
|
||||
elogg(LOGG_INFO, "exeScanner: PE Signature not found\n");
|
||||
goto cleanup;
|
||||
}
|
||||
seek += sizeof(e_magic);
|
||||
|
||||
if (IsBadReadPtr(seek, sizeof(IMAGE_FILE_HEADER)))
|
||||
goto cleanup;
|
||||
pehdr = (PIMAGE_FILE_HEADER)seek;
|
||||
seek += sizeof(IMAGE_FILE_HEADER);
|
||||
|
||||
if (IsBadReadPtr(seek, sizeof(IMAGE_OPTIONAL_HEADER32)))
|
||||
goto cleanup;
|
||||
opthdr = (PIMAGE_OPTIONAL_HEADER32)seek;
|
||||
seek += sizeof(IMAGE_OPTIONAL_HEADER32);
|
||||
|
||||
if (pehdr->Machine != IMAGE_FILE_MACHINE_I386) {
|
||||
elogg(LOGG_INFO, "exeScanner: Not an x86 executable\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Invalid sections number */
|
||||
if ((pehdr->NumberOfSections < 1) || (pehdr->NumberOfSections > 32)) {
|
||||
elogg(LOGG_INFO, "exeScanner: Invalid sections number\n");
|
||||
packed = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0; i < pehdr->NumberOfSections; i++) {
|
||||
double section_entropy = .0f;
|
||||
if (IsBadReadPtr(seek, sizeof(IMAGE_SECTION_HEADER)))
|
||||
goto cleanup;
|
||||
sechdr = (PIMAGE_SECTION_HEADER)seek;
|
||||
seek += sizeof(IMAGE_SECTION_HEADER);
|
||||
|
||||
if (opthdr->AddressOfEntryPoint >= sechdr->VirtualAddress)
|
||||
epoff = opthdr->AddressOfEntryPoint - sechdr->VirtualAddress +
|
||||
sechdr->PointerToRawData;
|
||||
|
||||
s_start = lpMapAddress + sechdr->PointerToRawData;
|
||||
if (!IsBadReadPtr(s_start, sechdr->SizeOfRawData))
|
||||
section_entropy = calc_entropy(s_start, sechdr->SizeOfRawData);
|
||||
|
||||
entropy = MAX(entropy, section_entropy);
|
||||
|
||||
/* Sanitize the section name */
|
||||
memcpy(secname, sechdr->Name, IMAGE_SIZEOF_SHORT_NAME);
|
||||
for (c = 0; (c < IMAGE_SIZEOF_SHORT_NAME) && secname[c]; c++)
|
||||
if (!isprint(secname[c]))
|
||||
secname[c] = '?';
|
||||
secname[IMAGE_SIZEOF_SHORT_NAME - 1] = 0;
|
||||
|
||||
elogg(LOGG_INFO, "exeScanner: Section name: [%s] - Entropy %f\n", secname,
|
||||
section_entropy);
|
||||
|
||||
if (!sechdr->SizeOfRawData)
|
||||
badsection = 1;
|
||||
}
|
||||
|
||||
elogg(LOGG_INFO, "exeScanner: Max entropy = %f\n", entropy);
|
||||
/* EP Check */
|
||||
elogg(LOGG_INFO, "exeScanner: Entry Point rva: 0x%lx - raw: 0x%lx\n",
|
||||
opthdr->AddressOfEntryPoint, epoff);
|
||||
|
||||
ep = lpMapAddress + epoff;
|
||||
if (!IsBadReadPtr(ep, EP_SIGNATURE_SIZE)) {
|
||||
#ifdef DUMP_SIGNATURE
|
||||
int i;
|
||||
for (i = 0; i < EP_SIGNATURE_SIZE; i++)
|
||||
elogg(LOGG_INFO, "%02x ", ep[i]);
|
||||
elogg(LOGG_INFO, "\n[C Code]: ");
|
||||
for (i = 0; i < EP_SIGNATURE_SIZE - 1; i++)
|
||||
elogg(LOGG_INFO, "0x%02x, ", ep[i]);
|
||||
elogg(LOGG_INFO, "0x%02x\n", ep[i]);
|
||||
#endif
|
||||
if ((sig = checksig(ep))) {
|
||||
elogg(LOGG_INFO, "exeScanner: Signature check: %s\n", sig->name);
|
||||
entropy += sig->score;
|
||||
packed = (sig->score >= .0f);
|
||||
if (sig->score < .0f)
|
||||
elogg(
|
||||
"exeScanner: Whitelisted signature found, lowering entropy to %f\n",
|
||||
entropy);
|
||||
} else
|
||||
elogg(LOGG_INFO, "exeScanner: Signature check: Nothing found\n");
|
||||
} else
|
||||
elogg(LOGG_INFO, "exeScanner: Invalid address of Entry Point\n");
|
||||
|
||||
if (badsection) {
|
||||
if ((entropy == .0f) || (entropy > ENTROPY_THRESHOLD)) {
|
||||
elogg(LOGG_INFO, "exeScanner: found zero SizeOfRawData and entropy %f\n", entropy);
|
||||
packed = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (lpMapAddress)
|
||||
UnmapViewOfFile(lpMapAddress);
|
||||
if (hMapFile)
|
||||
CloseHandle(hMapFile);
|
||||
CloseHandle(hFile);
|
||||
return packed;
|
||||
}
|
||||
72
clamav/common/exeScanner.h
Normal file
72
clamav/common/exeScanner.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
* Copyright (C) 2006-2008 Gianluigi Tiesi <sherpya@netfarm.it>
|
||||
*
|
||||
* Authors: Gianluigi Tiesi
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef _EXESCANNER_H_
|
||||
#define _EXESCANNER_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#ifdef EXESCANNER_STANDALONE
|
||||
#define DUMP_SIGNATURE
|
||||
#include <windows.h>
|
||||
#define logg printf
|
||||
#define elogg printf
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef __int16 int16_t;
|
||||
#else
|
||||
#include "output.h"
|
||||
#include <others.h>
|
||||
static inline void elogg(const char *fmt, ...){};
|
||||
#endif /* EXESCANNER_STANDALONE */
|
||||
|
||||
#define ENTROPY_THRESHOLD 4.0
|
||||
#define EP_SIGNATURE_SIZE 16
|
||||
|
||||
#ifndef IMAGE_DOS_SIGNATURE
|
||||
#define IMAGE_DOS_SIGNATURE 0x5A4D /* MZ */
|
||||
#endif
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
typedef struct _sigs_t {
|
||||
int16_t sig[16];
|
||||
const char *name;
|
||||
double score;
|
||||
} sigs_t;
|
||||
|
||||
extern int is_packed(const char *filename);
|
||||
|
||||
static const char screv[] =
|
||||
{
|
||||
0x65, 0x78, 0x65, 0x53, 0x63, 0x61, 0x6e, 0x6e,
|
||||
0x65, 0x72, 0x7c, 0x47, 0x50, 0x4c, 0x7c, 0x47,
|
||||
0x69, 0x61, 0x6e, 0x6c, 0x75, 0x69, 0x67, 0x69,
|
||||
0x20, 0x54, 0x69, 0x65, 0x73, 0x69, 0x7c, 0x3c,
|
||||
0x73, 0x68, 0x65, 0x72, 0x70, 0x79, 0x61, 0x40,
|
||||
0x6e, 0x65, 0x74, 0x66, 0x61, 0x72, 0x6d, 0x2e,
|
||||
0x69, 0x74, 0x3e};
|
||||
|
||||
#endif /* _EXESCANNER_H_ */
|
||||
52
clamav/common/fdpassing.h
Normal file
52
clamav/common/fdpassing.h
Normal 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
302
clamav/common/getopt.c
Normal file
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* getopt.c - my re-implementation of getopt.
|
||||
* Copyright 1997, 2000, 2001, 2002, 2006, Benjamin Sittler
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "clamav-config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "getopt.h"
|
||||
|
||||
int optind = 1, opterr = 1, optopt = 0;
|
||||
char *optarg = 0;
|
||||
|
||||
/* reset argument parser to start-up values */
|
||||
/*
|
||||
int getopt_reset(void)
|
||||
{
|
||||
optind = 1;
|
||||
opterr = 1;
|
||||
optopt = 0;
|
||||
optarg = 0;
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
/* this is the plain old UNIX getopt, with GNU-style extensions. */
|
||||
/* if you're porting some piece of UNIX software, this is all you need. */
|
||||
/* this supports GNU-style permution and optional arguments */
|
||||
|
||||
int my_getopt(int argc, char *argvc[], const char *opts)
|
||||
{
|
||||
char **argv = (char **)argvc;
|
||||
static int charind = 0;
|
||||
const char *s;
|
||||
char mode, colon_mode;
|
||||
int off = 0, opt = -1;
|
||||
|
||||
if (getenv("POSIXLY_CORRECT"))
|
||||
colon_mode = mode = '+';
|
||||
else {
|
||||
if ((colon_mode = *opts) == ':') off++;
|
||||
if (((mode = opts[off]) == '+') || (mode == '-')) {
|
||||
off++;
|
||||
if ((colon_mode != ':') && ((colon_mode = opts[off]) == ':'))
|
||||
off++;
|
||||
}
|
||||
}
|
||||
optarg = 0;
|
||||
if (charind) {
|
||||
optopt = argv[optind][charind];
|
||||
for (s = opts + off; *s; s++)
|
||||
if (optopt == *s) {
|
||||
charind++;
|
||||
if ((*(++s) == ':') || ((optopt == 'W') && (*s == ';'))) {
|
||||
if (argv[optind][charind]) {
|
||||
optarg = &(argv[optind++][charind]);
|
||||
charind = 0;
|
||||
} else if (*(++s) != ':') {
|
||||
charind = 0;
|
||||
if (++optind >= argc) {
|
||||
if (opterr) fprintf(stderr,
|
||||
"%s: option requires an argument -- %c\n",
|
||||
argv[0], optopt);
|
||||
opt = (colon_mode == ':') ? ':' : '?';
|
||||
goto getopt_ok;
|
||||
}
|
||||
optarg = argv[optind++];
|
||||
}
|
||||
}
|
||||
opt = optopt;
|
||||
goto getopt_ok;
|
||||
}
|
||||
if (opterr) fprintf(stderr,
|
||||
"%s: illegal option -- %c\n",
|
||||
argv[0], optopt);
|
||||
opt = '?';
|
||||
if (argv[optind][++charind] == '\0') {
|
||||
optind++;
|
||||
charind = 0;
|
||||
}
|
||||
getopt_ok:
|
||||
if (charind && !argv[optind][charind]) {
|
||||
optind++;
|
||||
charind = 0;
|
||||
}
|
||||
} else if ((optind >= argc) ||
|
||||
((argv[optind][0] == '-') &&
|
||||
(argv[optind][1] == '-') &&
|
||||
(argv[optind][2] == '\0'))) {
|
||||
optind++;
|
||||
opt = -1;
|
||||
} else if ((argv[optind][0] != '-') ||
|
||||
(argv[optind][1] == '\0')) {
|
||||
char *tmp;
|
||||
int i, j, k;
|
||||
|
||||
if (mode == '+')
|
||||
opt = -1;
|
||||
else if (mode == '-') {
|
||||
optarg = argv[optind++];
|
||||
charind = 0;
|
||||
opt = 1;
|
||||
} else {
|
||||
for (i = j = optind; i < argc; i++)
|
||||
if ((argv[i][0] == '-') &&
|
||||
(argv[i][1] != '\0')) {
|
||||
optind = i;
|
||||
opt = my_getopt(argc, argv, opts);
|
||||
while (i > j) {
|
||||
tmp = argv[--i];
|
||||
for (k = i; k + 1 < optind; k++) argv[k] = argv[k + 1];
|
||||
argv[--optind] = tmp;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (i == argc) opt = -1;
|
||||
}
|
||||
} else {
|
||||
charind++;
|
||||
opt = my_getopt(argc, argv, opts);
|
||||
}
|
||||
if (optind > argc) optind = argc;
|
||||
return opt;
|
||||
}
|
||||
|
||||
/* this is the extended getopt_long{,_only}, with some GNU-like
|
||||
* extensions. Implements _getopt_internal in case any programs
|
||||
* expecting GNU libc getopt call it.
|
||||
*/
|
||||
|
||||
static int _getopt_internal(int argc, char *argv[], const char *shortopts,
|
||||
const struct option *longopts, int *longind,
|
||||
int long_only)
|
||||
{
|
||||
char mode, colon_mode;
|
||||
int shortoff = 0, opt = -1;
|
||||
|
||||
if (getenv("POSIXLY_CORRECT")) {
|
||||
colon_mode = mode = '+';
|
||||
} else {
|
||||
if ((colon_mode = *shortopts) == ':') shortoff++;
|
||||
if (((mode = shortopts[shortoff]) == '+') || (mode == '-')) {
|
||||
shortoff++;
|
||||
if ((colon_mode != ':') && ((colon_mode = shortopts[shortoff]) == ':'))
|
||||
shortoff++;
|
||||
}
|
||||
}
|
||||
optarg = 0;
|
||||
if ((optind >= argc) ||
|
||||
((argv[optind][0] == '-') &&
|
||||
(argv[optind][1] == '-') &&
|
||||
(argv[optind][2] == '\0'))) {
|
||||
optind++;
|
||||
opt = -1;
|
||||
} else if ((argv[optind][0] != '-') ||
|
||||
(argv[optind][1] == '\0')) {
|
||||
char *tmp;
|
||||
int i, j, k;
|
||||
|
||||
opt = -1;
|
||||
if (mode == '+')
|
||||
return -1;
|
||||
else if (mode == '-') {
|
||||
optarg = argv[optind++];
|
||||
return 1;
|
||||
}
|
||||
for (i = j = optind; i < argc; i++)
|
||||
if ((argv[i][0] == '-') &&
|
||||
(argv[i][1] != '\0')) {
|
||||
optind = i;
|
||||
|
||||
opt = _getopt_internal(argc, argv, shortopts,
|
||||
longopts, longind,
|
||||
long_only);
|
||||
while (i > j) {
|
||||
tmp = argv[--i];
|
||||
for (k = i; k + 1 < optind; k++)
|
||||
argv[k] = argv[k + 1];
|
||||
argv[--optind] = tmp;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if ((!long_only) && (argv[optind][1] != '-'))
|
||||
opt = my_getopt(argc, argv, shortopts);
|
||||
else {
|
||||
int charind, offset;
|
||||
int found = 0, ind, hits = 0;
|
||||
|
||||
if (((optopt = argv[optind][1]) != '-') && !argv[optind][2]) {
|
||||
int c;
|
||||
|
||||
ind = shortoff;
|
||||
while ((c = shortopts[ind++])) {
|
||||
if (((shortopts[ind] == ':') ||
|
||||
((c == 'W') && (shortopts[ind] == ';'))) &&
|
||||
(shortopts[++ind] == ':'))
|
||||
ind++;
|
||||
if (optopt == c) return my_getopt(argc, argv, shortopts);
|
||||
}
|
||||
}
|
||||
offset = 2 - (argv[optind][1] != '-');
|
||||
for (charind = offset;
|
||||
(argv[optind][charind] != '\0') &&
|
||||
(argv[optind][charind] != '=');
|
||||
charind++)
|
||||
;
|
||||
for (ind = 0; longopts[ind].name && !hits; ind++)
|
||||
if ((strlen(longopts[ind].name) == (size_t)(charind - offset)) &&
|
||||
(strncmp(longopts[ind].name,
|
||||
argv[optind] + offset, charind - offset) == 0))
|
||||
found = ind, hits++;
|
||||
if (!hits)
|
||||
for (ind = 0; longopts[ind].name; ind++)
|
||||
if (strncmp(longopts[ind].name,
|
||||
argv[optind] + offset, charind - offset) == 0)
|
||||
found = ind, hits++;
|
||||
if (hits == 1) {
|
||||
opt = 0;
|
||||
|
||||
if (argv[optind][charind] == '=') {
|
||||
if (longopts[found].has_arg == 0) {
|
||||
opt = '?';
|
||||
if (opterr) fprintf(stderr,
|
||||
"%s: option `--%s' doesn't allow an argument\n",
|
||||
argv[0], longopts[found].name);
|
||||
} else {
|
||||
optarg = argv[optind] + ++charind;
|
||||
// charind = 0; // Never used again past here
|
||||
}
|
||||
} else if (longopts[found].has_arg == 1) {
|
||||
if (++optind >= argc) {
|
||||
opt = (colon_mode == ':') ? ':' : '?';
|
||||
if (opterr) fprintf(stderr,
|
||||
"%s: option `--%s' requires an argument\n",
|
||||
argv[0], longopts[found].name);
|
||||
} else
|
||||
optarg = argv[optind];
|
||||
}
|
||||
if (!opt) {
|
||||
if (longind) *longind = found;
|
||||
if (!longopts[found].flag)
|
||||
opt = longopts[found].val;
|
||||
else
|
||||
*(longopts[found].flag) = longopts[found].val;
|
||||
}
|
||||
optind++;
|
||||
} else if (!hits) {
|
||||
if (offset == 1)
|
||||
opt = my_getopt(argc, argv, shortopts);
|
||||
else {
|
||||
opt = '?';
|
||||
if (opterr) fprintf(stderr,
|
||||
"%s: unrecognized option `%s'\n",
|
||||
argv[0], argv[optind++]);
|
||||
}
|
||||
} else {
|
||||
opt = '?';
|
||||
if (opterr) fprintf(stderr,
|
||||
"%s: option `%s' is ambiguous\n",
|
||||
argv[0], argv[optind++]);
|
||||
}
|
||||
}
|
||||
if (optind > argc) optind = argc;
|
||||
return opt;
|
||||
}
|
||||
|
||||
int my_getopt_long(int argc, char *argv[], const char *shortopts,
|
||||
const struct option *longopts, int *longind)
|
||||
{
|
||||
return _getopt_internal(argc, argv, shortopts, longopts, longind, 0);
|
||||
}
|
||||
|
||||
int my_getopt_long_only(int argc, char *argv[], const char *shortopts,
|
||||
const struct option *longopts, int *longind)
|
||||
{
|
||||
return _getopt_internal(argc, argv, shortopts, longopts, longind, 1);
|
||||
}
|
||||
65
clamav/common/getopt.h
Normal file
65
clamav/common/getopt.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* getopt.h - interface to my re-implementation of getopt.
|
||||
* Copyright 1997, 2000, 2001, 2002, 2006, Benjamin Sittler
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _GETOPT_H_INCLUDED
|
||||
#define _GETOPT_H_INCLUDED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* UNIX-style short-argument parser */
|
||||
extern int my_getopt(int argc, char *argv[], const char *opts);
|
||||
|
||||
extern int optind, opterr, optopt;
|
||||
extern char *optarg;
|
||||
|
||||
struct option {
|
||||
const char *name;
|
||||
int has_arg;
|
||||
int *flag;
|
||||
int val;
|
||||
};
|
||||
|
||||
/* human-readable values for has_arg */
|
||||
#undef no_argument
|
||||
#define no_argument 0
|
||||
#undef required_argument
|
||||
#define required_argument 1
|
||||
#undef optional_argument
|
||||
#define optional_argument 2
|
||||
|
||||
/* GNU-style long-argument parsers */
|
||||
extern int my_getopt_long(int argc, char *argv[], const char *shortopts,
|
||||
const struct option *longopts, int *longind);
|
||||
|
||||
extern int my_getopt_long_only(int argc, char *argv[], const char *shortopts,
|
||||
const struct option *longopts, int *longind);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _GETOPT_H_INCLUDED */
|
||||
64
clamav/common/hostid.c
Normal file
64
clamav/common/hostid.c
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* Author: Shawn Webb
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// libclamav
|
||||
#include "others.h"
|
||||
|
||||
#include "output.h"
|
||||
|
||||
char hostid[37];
|
||||
|
||||
int is_valid_hostid(void)
|
||||
{
|
||||
int count, i;
|
||||
|
||||
if (strlen(hostid) != 36)
|
||||
return 0;
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < 36; i++)
|
||||
if (hostid[i] == '-')
|
||||
count++;
|
||||
|
||||
if (count != 4)
|
||||
return 0;
|
||||
|
||||
if (hostid[8] != '-' || hostid[13] != '-' || hostid[18] != '-' || hostid[23] != '-')
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *get_hostid(void *cbdata)
|
||||
{
|
||||
UNUSEDPARAM(cbdata);
|
||||
|
||||
if (!strcmp(hostid, "none"))
|
||||
return NULL;
|
||||
|
||||
if (!is_valid_hostid())
|
||||
return strdup(STATS_ANON_UUID);
|
||||
|
||||
logg(LOGG_INFO, "HostID is valid: %s\n", hostid);
|
||||
|
||||
return strdup(hostid);
|
||||
}
|
||||
41
clamav/common/hostid.h
Normal file
41
clamav/common/hostid.h
Normal 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
|
||||
258
clamav/common/idmef_logging.c
Normal file
258
clamav/common/idmef_logging.c
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2013 Sourcefire, Inc.
|
||||
*
|
||||
* Authors: Selim Menouar, Verene Houdebine
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "clamav.h"
|
||||
#include "misc.h"
|
||||
#include "output.h"
|
||||
|
||||
#ifndef PRELUDE
|
||||
void prelude_logging(const char *filename, const char *virname, const char *virhash, int virsize)
|
||||
{
|
||||
UNUSEDPARAM(filename);
|
||||
UNUSEDPARAM(virname);
|
||||
UNUSEDPARAM(virhash);
|
||||
UNUSEDPARAM(virsize);
|
||||
|
||||
logg(LOGG_INFO, "You have to compile with libprelude using ./configure --enable-prelude\n");
|
||||
}
|
||||
#else
|
||||
|
||||
#include <libprelude/prelude.h>
|
||||
|
||||
#define ANALYZER_MODEL "ClamAV"
|
||||
#define ANALYZER_CLASS "AntiVirus"
|
||||
#define ANALYZER_MANUFACTURER "http://www.sourcefire.com"
|
||||
|
||||
static prelude_client_t *prelude_client;
|
||||
|
||||
int idmef_analyzer_setup(idmef_analyzer_t *analyzer, const char *analyzer_name)
|
||||
{
|
||||
int ret;
|
||||
prelude_string_t *str;
|
||||
|
||||
/* alert->analyzer->name */
|
||||
ret = idmef_analyzer_new_name(analyzer, &str);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
prelude_string_set_constant(str, analyzer_name);
|
||||
|
||||
/* alert->analyzer->model */
|
||||
ret = idmef_analyzer_new_model(analyzer, &str);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
prelude_string_set_constant(str, ANALYZER_MODEL);
|
||||
|
||||
/* alert->analyzer->class */
|
||||
ret = idmef_analyzer_new_class(analyzer, &str);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
prelude_string_set_constant(str, ANALYZER_CLASS);
|
||||
|
||||
/* alert->analyzer->manufacturer */
|
||||
ret = idmef_analyzer_new_manufacturer(analyzer, &str);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
prelude_string_set_constant(str, ANALYZER_MANUFACTURER);
|
||||
|
||||
/* alert->analyzer->version */
|
||||
ret = idmef_analyzer_new_version(analyzer, &str);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
prelude_string_set_constant(str, get_version());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prelude_initialize_client(const char *analyzer_name)
|
||||
{
|
||||
int ret;
|
||||
|
||||
prelude_client = NULL;
|
||||
|
||||
ret = prelude_init(0, NULL);
|
||||
if (ret < 0) {
|
||||
logg(LOGG_INFO, "Unable to initialize the prelude library : %s", prelude_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = prelude_client_new(&prelude_client, analyzer_name);
|
||||
if (ret < 0) {
|
||||
logg(LOGG_INFO, "Unable to create a prelude client object : %s", prelude_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = idmef_analyzer_setup(prelude_client_get_analyzer(prelude_client), analyzer_name);
|
||||
if (ret < 0) {
|
||||
logg(LOGG_INFO, "%s", prelude_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = prelude_client_start(prelude_client);
|
||||
if (ret < 0 || !prelude_client) {
|
||||
logg(LOGG_INFO, "Unable to start prelude client : %s", prelude_strerror(ret));
|
||||
prelude_client_destroy(prelude_client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = prelude_client_set_flags(prelude_client, PRELUDE_CLIENT_FLAGS_ASYNC_SEND | PRELUDE_CLIENT_FLAGS_ASYNC_TIMER);
|
||||
if (ret < 0) {
|
||||
logg(LOGG_INFO, "Unable to send asynchronous send and timer : %s", prelude_strerror(ret));
|
||||
prelude_client_destroy(prelude_client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int add_string_additional_data(idmef_alert_t *alert, const char *meaning, const char *ptr)
|
||||
{
|
||||
int ret;
|
||||
prelude_string_t *str;
|
||||
idmef_additional_data_t *ad;
|
||||
idmef_data_t *data;
|
||||
|
||||
ret = idmef_alert_new_additional_data(alert, &ad, IDMEF_LIST_APPEND);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
idmef_additional_data_set_type(ad, IDMEF_ADDITIONAL_DATA_TYPE_STRING);
|
||||
|
||||
idmef_additional_data_new_data(ad, &data);
|
||||
|
||||
ret = idmef_data_set_char_string_ref(data, ptr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = idmef_additional_data_new_meaning(ad, &str);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = prelude_string_set_ref(str, meaning);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int add_int_additional_data(idmef_alert_t *alert, const char *meaning, int data)
|
||||
{
|
||||
int ret;
|
||||
prelude_string_t *str;
|
||||
idmef_additional_data_t *ad;
|
||||
|
||||
ret = idmef_alert_new_additional_data(alert, &ad, IDMEF_LIST_APPEND);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
idmef_additional_data_set_integer(ad, data);
|
||||
|
||||
ret = idmef_additional_data_new_meaning(ad, &str);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = prelude_string_set_ref(str, meaning);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void prelude_logging(const char *filename, const char *virname, const char *virhash, int virsize)
|
||||
{
|
||||
int ret;
|
||||
idmef_message_t *idmef = NULL;
|
||||
idmef_alert_t *alert;
|
||||
idmef_classification_t *class;
|
||||
prelude_string_t *str;
|
||||
idmef_target_t *target;
|
||||
idmef_file_t *file;
|
||||
|
||||
ret = idmef_message_new(&idmef);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = idmef_message_new_alert(idmef, &alert);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = idmef_alert_new_classification(alert, &class);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = idmef_classification_new_text(class, &str);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
prelude_string_set_constant(str, "Virus Found");
|
||||
|
||||
ret = idmef_alert_new_target(alert, &target, 0);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = idmef_target_new_file(target, &file, 0);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = idmef_file_new_path(file, &str);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
prelude_string_set_ref(str, filename);
|
||||
|
||||
if (virname != NULL) {
|
||||
ret = add_string_additional_data(alert, "virname", virname);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (virhash != NULL) {
|
||||
ret = add_string_additional_data(alert, "virhash", virhash);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = add_int_additional_data(alert, "virsize", virsize);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
logg(LOGG_INFO, "le client : %s", prelude_client_get_config_filename(prelude_client));
|
||||
prelude_client_send_idmef(prelude_client, idmef);
|
||||
idmef_message_destroy(idmef);
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
if (idmef != NULL)
|
||||
idmef_message_destroy(idmef);
|
||||
|
||||
logg(LOGG_INFO, "%s error: %s", prelude_strsource(ret), prelude_strerror(ret));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
29
clamav/common/idmef_logging.h
Normal file
29
clamav/common/idmef_logging.h
Normal 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);
|
||||
102
clamav/common/linux/cert_util_linux.c
Normal file
102
clamav/common/linux/cert_util_linux.c
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* OpenSSL certificate verification for Linux.
|
||||
*
|
||||
* Copyright (C) 2016-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* Authors: Russ Kubik
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "output.h"
|
||||
#include "cert_util.h"
|
||||
#include "cert_util_internal.h"
|
||||
|
||||
void set_tls_ca_bundle(CURL *curl)
|
||||
{
|
||||
char *ca_bundle;
|
||||
|
||||
ca_bundle = getenv("CURL_CA_BUNDLE");
|
||||
if (ca_bundle == NULL)
|
||||
return;
|
||||
|
||||
if (curl_easy_setopt(curl, CURLOPT_CAINFO, ca_bundle) != CURLE_OK) {
|
||||
fprintf(stderr, "Failed to set CURLOPT_CAINFO!\n");
|
||||
}
|
||||
}
|
||||
|
||||
cl_error_t cert_store_load(X509 **trusted_certs, size_t trusted_cert_count)
|
||||
{
|
||||
cl_error_t ret = CL_EOPEN;
|
||||
cert_store_t *store = NULL;
|
||||
int pt_err;
|
||||
|
||||
do {
|
||||
store = cert_store_get_int();
|
||||
if (!store) {
|
||||
mprintf(LOGG_ERROR, "Failed to retrieve cert store\n");
|
||||
break;
|
||||
}
|
||||
|
||||
pt_err = pthread_mutex_lock(&store->mutex);
|
||||
if (pt_err) {
|
||||
errno = pt_err;
|
||||
mprintf(LOGG_ERROR, "Mutex lock failed\n");
|
||||
}
|
||||
|
||||
if (store->loaded) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* System certs do not need to be added as they can be accessed directly
|
||||
* by the SSL library. */
|
||||
store->system_certs.count = 0;
|
||||
store->system_certs.certificates = NULL;
|
||||
|
||||
if (trusted_certs && trusted_cert_count > 0) {
|
||||
if (cert_store_set_trusted_int(trusted_certs, trusted_cert_count) == 0) {
|
||||
mprintf(LOGG_DEBUG, "Trusted certificates loaded: %zu\n",
|
||||
store->trusted_certs.count);
|
||||
} else {
|
||||
mprintf(LOGG_WARNING, "Continuing without trusted certificates\n");
|
||||
/* proceed as if we succeeded using only certificates from the
|
||||
* system */
|
||||
}
|
||||
}
|
||||
|
||||
store->loaded = true;
|
||||
ret = 0;
|
||||
} while (0);
|
||||
|
||||
if (store) {
|
||||
pt_err = pthread_mutex_unlock(&store->mutex);
|
||||
if (pt_err) {
|
||||
errno = pt_err;
|
||||
mprintf(LOGG_ERROR, "Mutex unlock failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
402
clamav/common/mac/cert_util_mac.m
Normal file
402
clamav/common/mac/cert_util_mac.m
Normal file
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* OpenSSL certificate verification for macOS.
|
||||
*
|
||||
* Copyright (C) 2016-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* Authors: Russ Kubik
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
#import <Security/SecRequirement.h>
|
||||
#import <Security/SecBase.h>
|
||||
#import <Security/SecCode.h>
|
||||
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
#include <Security/Security.h>
|
||||
|
||||
#include <sys/syslimits.h>
|
||||
#import <sys/proc_info.h>
|
||||
#import <libproc.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <libgen.h>
|
||||
#include <mach-o/dyld.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "output.h"
|
||||
|
||||
#include "cert_util.h"
|
||||
#include "cert_util_internal.h"
|
||||
|
||||
/* Macro to obtain the number of elements in a fixed sized array that was either
|
||||
* statically declared or declared on the stack in the same scope. The macro
|
||||
* will generate a divide-by-zero compiler warning if the input is a pointer.
|
||||
*
|
||||
* See also:
|
||||
* http://stackoverflow.com/questions/8018843/macro-definition-array-size
|
||||
*/
|
||||
#define ARRAY_SIZE(a) ((sizeof(a) / sizeof(*(a))) / ((size_t)(!(sizeof(a) % sizeof(*(a))))))
|
||||
|
||||
/* Keychain types available on macOS. User specific keychains are omitted for
|
||||
* simplicity. */
|
||||
typedef enum keychain_type {
|
||||
KEYCHAIN_TYPE_SYSTEM_ROOT,
|
||||
KEYCHAIN_TYPE_SYSTEM
|
||||
} keychain_type_t;
|
||||
|
||||
/* Basic information about a keychain */
|
||||
typedef struct keychain_info {
|
||||
const char *name;
|
||||
const char *file_path;
|
||||
} keychain_info_t;
|
||||
|
||||
/* Table to support name and file path lookup for each keychain type */
|
||||
static const keychain_info_t _KEYCHAIN_INFO[] =
|
||||
{
|
||||
{.name = "system root",
|
||||
.file_path = "/System/Library/Keychains/SystemRootCertificates.keychain"},
|
||||
{.name = "system",
|
||||
.file_path = "/Library/Keychains/System.keychain"}};
|
||||
|
||||
/*!
|
||||
* @brief Get basic information about the specified keychain.
|
||||
* @param[in] keychain_type Keychain type
|
||||
* @return The keychain information. All pointers contained in this
|
||||
* point to read only data and so do not need to be freed.
|
||||
*/
|
||||
static keychain_info_t _get_keychain_info(keychain_type_t keychain_type)
|
||||
{
|
||||
return _KEYCHAIN_INFO[keychain_type];
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Get a reference to an allocated array of certifcates contained
|
||||
* in the specified keychain.
|
||||
* @param[in] keychain_type Keychain type
|
||||
* @return If successful, reference to allocated array of certifcates. The
|
||||
* caller is responsible for calling CFRelease on the returned
|
||||
* reference after use.
|
||||
* @return NULL otherwise
|
||||
*/
|
||||
static CFTypeRef _get_cert_ref(keychain_type_t keychain_type)
|
||||
{
|
||||
keychain_info_t kc_info = _get_keychain_info(keychain_type);
|
||||
|
||||
CFTypeRef keys[] = {
|
||||
kSecMatchSearchList,
|
||||
kSecClass,
|
||||
kSecReturnRef,
|
||||
kSecMatchLimit,
|
||||
kSecMatchTrustedOnly,
|
||||
kSecMatchValidOnDate,
|
||||
};
|
||||
CFTypeRef values[] = {
|
||||
/* NOTE: must match the order specified above */
|
||||
kCFNull, /* place holder for match search list */
|
||||
kSecClassCertificate, /* kSecClass */
|
||||
kCFBooleanTrue, /* kSecReturnRef */
|
||||
kSecMatchLimitAll, /* kSecMatchLimit */
|
||||
kCFBooleanTrue, /* kSecMatchTrustedOnly */
|
||||
kCFNull, /* kSecMatchValidOnDate */
|
||||
};
|
||||
|
||||
CFDictionaryRef query = NULL;
|
||||
CFTypeRef items = NULL;
|
||||
|
||||
SecKeychainRef keychain = NULL;
|
||||
CFArrayRef search_list = NULL;
|
||||
|
||||
SecKeychainStatus keychainStatus = 0;
|
||||
|
||||
OSStatus status;
|
||||
|
||||
status = SecKeychainOpen(kc_info.file_path, &keychain);
|
||||
|
||||
if (status != errSecSuccess) {
|
||||
mprintf(LOGG_ERROR, "Failed to open %s keychain: %s (%d)\n",
|
||||
kc_info.name,
|
||||
kc_info.file_path,
|
||||
status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = SecKeychainGetStatus(keychain, &keychainStatus);
|
||||
if (status != errSecSuccess) {
|
||||
mprintf(LOGG_ERROR, "Failed to get the status of the %s keychain: %d\n",
|
||||
kc_info.name,
|
||||
status);
|
||||
goto done;
|
||||
}
|
||||
if (!(keychainStatus & kSecReadPermStatus)) {
|
||||
mprintf(LOGG_ERROR, "The %s keychain is not readable: %" PRIu32 "\n",
|
||||
kc_info.name,
|
||||
keychainStatus);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (keychain_type == KEYCHAIN_TYPE_SYSTEM_ROOT) {
|
||||
/*
|
||||
* The SystemRootCertificates.keychain is a system keychain file that should be locked
|
||||
* and should definitely not have writable permissions. This may indicate that the file
|
||||
* has been tampered with.
|
||||
*/
|
||||
if (keychainStatus & (kSecUnlockStateStatus | kSecWritePermStatus)) {
|
||||
mprintf(LOGG_ERROR, "System Root Certificates Keychain has invalid permissions: %" PRIu32 "\n",
|
||||
keychainStatus);
|
||||
/* continue on error */
|
||||
}
|
||||
}
|
||||
|
||||
search_list = CFArrayCreate(kCFAllocatorDefault,
|
||||
(const void **)&keychain, 1, &kCFTypeArrayCallBacks);
|
||||
if (search_list == NULL) {
|
||||
mprintf(LOGG_ERROR, "Failed to create %s keychain search list\n",
|
||||
kc_info.name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* set the search list for the secItemCopyMatching call */
|
||||
values[0] = search_list;
|
||||
|
||||
query = CFDictionaryCreate(NULL, keys, values, ARRAY_SIZE(keys),
|
||||
&kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
|
||||
if (query == NULL) {
|
||||
mprintf(LOGG_ERROR, "Failed to create %s keychain query dictionary\n",
|
||||
kc_info.name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = SecItemCopyMatching(query, &items);
|
||||
if (status != errSecSuccess) {
|
||||
if (status == errSecItemNotFound) {
|
||||
mprintf(LOGG_DEBUG, "No items found in %s keychain\n",
|
||||
kc_info.name);
|
||||
} else {
|
||||
mprintf(LOGG_ERROR, "Unable to copy certificates from %s keychain (%d)\n",
|
||||
kc_info.name,
|
||||
status);
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(query);
|
||||
query = NULL;
|
||||
done:
|
||||
if (keychain) {
|
||||
CFRelease(keychain);
|
||||
keychain = NULL;
|
||||
}
|
||||
if (search_list) {
|
||||
CFRelease(search_list);
|
||||
search_list = NULL;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
cl_error_t cert_store_load(X509 **trusted_certs, size_t trusted_cert_count)
|
||||
{
|
||||
static const keychain_type_t keychains[] = {
|
||||
KEYCHAIN_TYPE_SYSTEM_ROOT,
|
||||
KEYCHAIN_TYPE_SYSTEM};
|
||||
|
||||
typedef struct keychain_cert_data {
|
||||
CFArrayRef certs;
|
||||
CFIndex certs_count;
|
||||
} keychain_cert_data_t;
|
||||
|
||||
keychain_cert_data_t keychain_cert_data_array[ARRAY_SIZE(keychains)] = {
|
||||
{.certs = NULL,
|
||||
.certs_count = 0},
|
||||
/* All other array values initialized to 0 by default */
|
||||
};
|
||||
|
||||
size_t kc_index = 0;
|
||||
|
||||
cl_error_t ret = CL_EOPEN;
|
||||
int pt_err;
|
||||
|
||||
cert_store_t *store = NULL;
|
||||
CFIndex total_certificates = 0;
|
||||
CFIndex i = 0;
|
||||
bool locked = false;
|
||||
|
||||
store = cert_store_get_int();
|
||||
if (!store) {
|
||||
mprintf(LOGG_ERROR, "Failed to retrieve cert store\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Load certificates from keychains before entering the critical section.
|
||||
* On a default 10.12 installation loading the the system roots keychain
|
||||
* could take up to 300 ms to complete. */
|
||||
|
||||
for (kc_index = 0; kc_index < ARRAY_SIZE(keychains); kc_index++) {
|
||||
keychain_type_t kc = keychains[kc_index];
|
||||
keychain_info_t kc_info = _get_keychain_info(kc);
|
||||
keychain_cert_data_t *kc_data = &keychain_cert_data_array[kc_index];
|
||||
CFTypeRef items = NULL;
|
||||
|
||||
items = _get_cert_ref(kc);
|
||||
if (!items) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CFGetTypeID(items) != CFArrayGetTypeID()) {
|
||||
mprintf(LOGG_ERROR, "Expected array of certificates from %s keychain, "
|
||||
"got type %lu\n",
|
||||
kc_info.name,
|
||||
CFGetTypeID(items));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CFArrayGetCount(items) < 1) {
|
||||
CFRelease(items);
|
||||
items = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
kc_data->certs = (CFArrayRef)items;
|
||||
kc_data->certs_count = CFArrayGetCount(items);
|
||||
|
||||
mprintf(LOGG_DEBUG, "Found %ld certificates from %s keychain\n",
|
||||
kc_data->certs_count,
|
||||
kc_info.name);
|
||||
|
||||
total_certificates += kc_data->certs_count;
|
||||
}
|
||||
|
||||
if (total_certificates < 1) {
|
||||
mprintf(LOGG_ERROR, "No certificate found in keychains. Expect at least one "
|
||||
"certificate to be found in system root and system "
|
||||
"keychains\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
store = cert_store_get_int();
|
||||
if (!store) {
|
||||
mprintf(LOGG_ERROR, "Failed to retrieve cert store\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
pt_err = pthread_mutex_lock(&store->mutex);
|
||||
if (pt_err) {
|
||||
errno = pt_err;
|
||||
mprintf(LOGG_ERROR, "Mutex lock failed\n");
|
||||
}
|
||||
locked = true;
|
||||
|
||||
if (store->loaded) {
|
||||
mprintf(LOGG_DEBUG, "Cert store already loaded\n");
|
||||
ret = CL_SUCCESS;
|
||||
goto done;
|
||||
}
|
||||
|
||||
store->system_certs.count = 0;
|
||||
store->system_certs.certificates = calloc(total_certificates,
|
||||
sizeof(*store->system_certs.certificates));
|
||||
if (store->system_certs.certificates == NULL) {
|
||||
mprintf(LOGG_ERROR, "Failed to reserve memory for system cert list\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (kc_index = 0; kc_index < ARRAY_SIZE(keychains); kc_index++) {
|
||||
keychain_type_t kc = keychains[kc_index];
|
||||
keychain_info_t kc_info = _get_keychain_info(kc);
|
||||
keychain_cert_data_t *kc_data = &keychain_cert_data_array[kc_index];
|
||||
|
||||
for (i = 0; i < kc_data->certs_count; i++) {
|
||||
const void *value = CFArrayGetValueAtIndex(kc_data->certs, i);
|
||||
|
||||
if (CFGetTypeID(value) == SecCertificateGetTypeID()) {
|
||||
SecCertificateRef cert = (SecCertificateRef)value;
|
||||
CFDataRef cert_data = SecCertificateCopyData(cert); /* DER representation of X.509 */
|
||||
|
||||
if (cert_data) {
|
||||
const unsigned char *der = CFDataGetBytePtr(cert_data);
|
||||
CFIndex length = CFDataGetLength(cert_data);
|
||||
|
||||
char *name = NULL;
|
||||
X509 *x509 = d2i_X509(NULL, &der, length);
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
x509_get_cert_name(x509, &name);
|
||||
#else
|
||||
name = x509->name;
|
||||
#endif
|
||||
|
||||
if (x509) {
|
||||
mprintf(LOGG_DEBUG, "Found %s trusted certificate %s\n",
|
||||
kc_info.name,
|
||||
(name ? name : "<no name>"));
|
||||
|
||||
store->system_certs.certificates[store->system_certs.count++] = x509;
|
||||
} else {
|
||||
mprintf(LOGG_ERROR, "Failed conversion of DER format to X.509\n");
|
||||
}
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
if (NULL != name) {
|
||||
free(name);
|
||||
name = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
CFRelease(cert_data);
|
||||
cert_data = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (trusted_certs && trusted_cert_count > 0) {
|
||||
if (cert_store_set_trusted_int(trusted_certs, trusted_cert_count) == 0) {
|
||||
mprintf(LOGG_DEBUG, "Trusted certificates loaded: %zu\n",
|
||||
store->trusted_certs.count);
|
||||
} else {
|
||||
mprintf(LOGG_WARNING, "Continuing without trusted certificates\n");
|
||||
/* proceed as if we succeeded using only certificates from the
|
||||
* system */
|
||||
}
|
||||
}
|
||||
|
||||
store->loaded = true;
|
||||
ret = CL_SUCCESS;
|
||||
|
||||
done:
|
||||
if (locked) {
|
||||
pt_err = pthread_mutex_unlock(&store->mutex);
|
||||
if (pt_err) {
|
||||
errno = pt_err;
|
||||
mprintf(LOGG_ERROR, "Mutex unlock failed\n");
|
||||
}
|
||||
locked = false;
|
||||
}
|
||||
|
||||
for (kc_index = 0; kc_index < ARRAY_SIZE(keychains); kc_index++) {
|
||||
keychain_cert_data_t *kc_data = &keychain_cert_data_array[kc_index];
|
||||
|
||||
if (kc_data->certs) {
|
||||
CFRelease(kc_data->certs);
|
||||
kc_data->certs = NULL;
|
||||
kc_data->certs_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
489
clamav/common/misc.c
Normal file
489
clamav/common/misc.c
Normal file
@@ -0,0 +1,489 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
* Copyright (C) 2007-2013 Sourcefire, Inc.
|
||||
*
|
||||
* Authors: Tomasz Kojm
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "clamav-config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#ifndef _WIN32
|
||||
#include <sys/socket.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
// libclamav
|
||||
#include "clamav.h"
|
||||
#include "cvd.h"
|
||||
#include "others.h" /* for cli_rmdirs() */
|
||||
#include "regex/regex.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "optparser.h"
|
||||
#include "output.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#ifndef REPO_VERSION
|
||||
#define REPO_VERSION "exported"
|
||||
#endif
|
||||
|
||||
const char *get_version(void)
|
||||
{
|
||||
if (!strncmp("devel-", VERSION, 6) && strcmp("exported", REPO_VERSION)) {
|
||||
return REPO_VERSION "" VERSION_SUFFIX;
|
||||
}
|
||||
/* it is a release, or we have nothing better */
|
||||
return VERSION "" VERSION_SUFFIX;
|
||||
}
|
||||
|
||||
char *freshdbdir(void)
|
||||
{
|
||||
struct cl_cvd *d1, *d2;
|
||||
struct optstruct *opts;
|
||||
const struct optstruct *opt;
|
||||
const char *dbdir;
|
||||
char *retdir;
|
||||
|
||||
/* try to find the most up-to-date db directory */
|
||||
dbdir = cl_retdbdir();
|
||||
if ((opts = optparse(CONFDIR_FRESHCLAM, 0, NULL, 0, OPT_FRESHCLAM, 0, NULL))) {
|
||||
if ((opt = optget(opts, "DatabaseDirectory"))->enabled) {
|
||||
if (strcmp(dbdir, opt->strarg)) {
|
||||
char *daily = (char *)malloc(strlen(opt->strarg) + strlen(dbdir) + 30);
|
||||
if (daily == NULL) {
|
||||
fprintf(stderr, "Unable to allocate memory for db directory...\n");
|
||||
return NULL;
|
||||
}
|
||||
sprintf(daily, "%s" PATHSEP "daily.cvd", opt->strarg);
|
||||
if (access(daily, R_OK))
|
||||
sprintf(daily, "%s" PATHSEP "daily.cld", opt->strarg);
|
||||
|
||||
if (!access(daily, R_OK) && (d1 = cl_cvdhead(daily))) {
|
||||
sprintf(daily, "%s" PATHSEP "daily.cvd", dbdir);
|
||||
if (access(daily, R_OK))
|
||||
sprintf(daily, "%s" PATHSEP "daily.cld", dbdir);
|
||||
|
||||
if (!access(daily, R_OK) && (d2 = cl_cvdhead(daily))) {
|
||||
free(daily);
|
||||
if (d1->version > d2->version)
|
||||
dbdir = opt->strarg;
|
||||
cl_cvdfree(d2);
|
||||
} else {
|
||||
free(daily);
|
||||
dbdir = opt->strarg;
|
||||
}
|
||||
cl_cvdfree(d1);
|
||||
} else {
|
||||
free(daily);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
retdir = strdup(dbdir);
|
||||
|
||||
if (opts)
|
||||
optfree(opts);
|
||||
|
||||
return retdir;
|
||||
}
|
||||
|
||||
void print_version(const char *dbdir)
|
||||
{
|
||||
char *fdbdir = NULL, *path;
|
||||
const char *pt;
|
||||
struct cl_cvd *daily;
|
||||
time_t db_time;
|
||||
unsigned int db_version = 0;
|
||||
|
||||
if (dbdir)
|
||||
pt = dbdir;
|
||||
else
|
||||
pt = fdbdir = freshdbdir();
|
||||
|
||||
if (!pt) {
|
||||
printf("ClamAV %s\n", get_version());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(path = malloc(strlen(pt) + 11))) {
|
||||
if (!dbdir)
|
||||
free(fdbdir);
|
||||
return;
|
||||
}
|
||||
|
||||
sprintf(path, "%s" PATHSEP "daily.cvd", pt);
|
||||
if (!access(path, R_OK)) {
|
||||
daily = cl_cvdhead(path);
|
||||
if (daily) {
|
||||
db_version = daily->version;
|
||||
db_time = daily->stime;
|
||||
cl_cvdfree(daily);
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(path, "%s" PATHSEP "daily.cld", pt);
|
||||
if (!access(path, R_OK)) {
|
||||
daily = cl_cvdhead(path);
|
||||
if (daily) {
|
||||
if (daily->version > db_version) {
|
||||
db_version = daily->version;
|
||||
db_time = daily->stime;
|
||||
}
|
||||
cl_cvdfree(daily);
|
||||
}
|
||||
}
|
||||
|
||||
if (!dbdir)
|
||||
free(fdbdir);
|
||||
|
||||
if (db_version) {
|
||||
printf("ClamAV %s/%u/%s", get_version(), db_version, ctime(&db_time));
|
||||
} else {
|
||||
printf("ClamAV %s\n", get_version());
|
||||
}
|
||||
|
||||
free(path);
|
||||
}
|
||||
|
||||
int check_flevel(void)
|
||||
{
|
||||
if (cl_retflevel() < CL_FLEVEL) {
|
||||
fprintf(stderr, "ERROR: This tool requires libclamav with functionality level %u or higher (current f-level: %u)\n", CL_FLEVEL, cl_retflevel());
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *filelist(const struct optstruct *opts, int *err)
|
||||
{
|
||||
static char buff[1025];
|
||||
static unsigned int cnt = 0;
|
||||
const struct optstruct *opt;
|
||||
static FILE *fs = NULL;
|
||||
size_t len;
|
||||
|
||||
if (!cnt && (opt = optget(opts, "file-list"))->enabled) {
|
||||
if (!fs) {
|
||||
fs = fopen(opt->strarg, "r");
|
||||
if (!fs) {
|
||||
fprintf(stderr, "ERROR: --file-list: Can't open file %s\n", opt->strarg);
|
||||
if (err)
|
||||
*err = 54;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (fgets(buff, 1024, fs)) {
|
||||
buff[1024] = 0;
|
||||
len = strlen(buff);
|
||||
if (!len) {
|
||||
fclose(fs);
|
||||
return NULL;
|
||||
}
|
||||
len--;
|
||||
while (len && ((buff[len] == '\n') || (buff[len] == '\r')))
|
||||
buff[len--] = '\0';
|
||||
return buff;
|
||||
} else {
|
||||
fclose(fs);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return opts->filename ? opts->filename[cnt++] : NULL;
|
||||
}
|
||||
|
||||
int filecopy(const char *src, const char *dest)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (!CopyFileA(src, dest, 0));
|
||||
#elif defined(C_DARWIN)
|
||||
pid_t pid;
|
||||
|
||||
/* On Mac OS X use ditto and copy resource fork, too. */
|
||||
switch (pid = fork()) {
|
||||
case -1:
|
||||
return -1;
|
||||
case 0:
|
||||
execl("/usr/bin/ditto", "ditto", src, dest, NULL);
|
||||
perror("execl(ditto)");
|
||||
break;
|
||||
default:
|
||||
wait(NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
||||
#else /* C_DARWIN */
|
||||
return cli_filecopy(src, dest);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
int close_std_descriptors()
|
||||
{
|
||||
int fds[3], i;
|
||||
|
||||
fds[0] = open("/dev/null", O_RDONLY);
|
||||
fds[1] = open("/dev/null", O_WRONLY);
|
||||
fds[2] = open("/dev/null", O_WRONLY);
|
||||
if (fds[0] == -1 || fds[1] == -1 || fds[2] == -1) {
|
||||
fputs("Can't open /dev/null\n", stderr);
|
||||
for (i = 0; i <= 2; i++)
|
||||
if (fds[i] != -1)
|
||||
close(fds[i]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i <= 2; i++) {
|
||||
if (dup2(fds[i], i) == -1) {
|
||||
fprintf(stderr, "dup2(%d, %d) failed\n", fds[i], i); /* may not be printed */
|
||||
for (i = 0; i <= 2; i++)
|
||||
if (fds[i] != -1)
|
||||
close(fds[i]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i <= 2; i++)
|
||||
if (fds[i] > 2)
|
||||
close(fds[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int daemonize_all_return(void)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (0 == pid) {
|
||||
setsid();
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
int daemonize(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = close_std_descriptors();
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = daemonize_all_return();
|
||||
pid_t pid = (pid_t)ret;
|
||||
/*parent process.*/
|
||||
if (pid > 0) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
static void daemonize_child_initialized_handler(int sig)
|
||||
{
|
||||
(void)(sig);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int daemonize_parent_wait(const char *const user, const char *const log_file)
|
||||
{
|
||||
int daemonizePid = daemonize_all_return();
|
||||
if (daemonizePid == -1) {
|
||||
return -1;
|
||||
} else if (daemonizePid) { // parent
|
||||
/* The parent will wait until either the child process
|
||||
* exits, or signals the parent that it's initialization is
|
||||
* complete. If it exits, it is due to an error condition,
|
||||
* so the parent should exit with the same error code as the child.
|
||||
* If the child signals the parent that initialization is complete, it
|
||||
* the parent will exit from the signal handler (initDoneSignalHandler)
|
||||
* with exit code 0.
|
||||
*/
|
||||
struct sigaction sig;
|
||||
memset(&sig, 0, sizeof(sig));
|
||||
sigemptyset(&(sig.sa_mask));
|
||||
sig.sa_handler = daemonize_child_initialized_handler;
|
||||
|
||||
if (0 != sigaction(SIGINT, &sig, NULL)) {
|
||||
perror("sigaction");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (NULL != user) {
|
||||
if (drop_privileges(user, log_file)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int exitStatus;
|
||||
wait(&exitStatus);
|
||||
if (WIFEXITED(exitStatus)) { // error
|
||||
exitStatus = WEXITSTATUS(exitStatus);
|
||||
exit(exitStatus);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void daemonize_signal_parent(pid_t parentPid)
|
||||
{
|
||||
close_std_descriptors();
|
||||
kill(parentPid, SIGINT);
|
||||
}
|
||||
|
||||
int drop_privileges(const char *const user_name, const char *const log_file)
|
||||
{
|
||||
int ret = 1;
|
||||
|
||||
/*This function is called in a bunch of places, and rather than change the error checking
|
||||
* in every function, we are just going to return success if there is no work to do.
|
||||
*/
|
||||
if ((0 == geteuid()) && (NULL != user_name)) {
|
||||
struct passwd *user = NULL;
|
||||
|
||||
if ((user = getpwnam(user_name)) == NULL) {
|
||||
logg(LOGG_WARNING, "Can't get information about user %s.\n", user_name);
|
||||
fprintf(stderr, "ERROR: Can't get information about user %s.\n", user_name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
#ifdef HAVE_INITGROUPS
|
||||
if (initgroups(user_name, user->pw_gid)) {
|
||||
fprintf(stderr, "ERROR: initgroups() failed.\n");
|
||||
logg(LOGG_WARNING, "initgroups() failed.\n");
|
||||
goto done;
|
||||
}
|
||||
#elif HAVE_SETGROUPS
|
||||
if (setgroups(1, &user->pw_gid)) {
|
||||
fprintf(stderr, "ERROR: setgroups() failed.\n");
|
||||
logg(LOGG_WARNING, "setgroups() failed.\n");
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*Change ownership of the log file to the user we are going to switch to.*/
|
||||
if (NULL != log_file) {
|
||||
int ret = lchown(log_file, user->pw_uid, user->pw_gid);
|
||||
if (ret) {
|
||||
fprintf(stderr, "ERROR: lchown to user '%s' failed on\n", user->pw_name);
|
||||
fprintf(stderr, "log file '%s'.\n", log_file);
|
||||
fprintf(stderr, "Error was '%s'\n", strerror(errno));
|
||||
logg(LOGG_WARNING, "lchown to user '%s' failed on log file '%s'. Error was '%s'\n",
|
||||
user->pw_name, log_file, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (setgid(user->pw_gid)) {
|
||||
fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int)user->pw_gid);
|
||||
logg(LOGG_WARNING, "setgid(%d) failed.\n", (int)user->pw_gid);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (setuid(user->pw_uid)) {
|
||||
fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int)user->pw_uid);
|
||||
logg(LOGG_WARNING, "setuid(%d) failed.\n", (int)user->pw_uid);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
#endif /*_WIN32*/
|
||||
|
||||
int match_regex(const char *filename, const char *pattern)
|
||||
{
|
||||
regex_t reg;
|
||||
int match, flags = REG_EXTENDED | REG_NOSUB;
|
||||
char fname[513];
|
||||
#ifdef _WIN32
|
||||
flags |= REG_ICASE; /* case insensitive on Windows */
|
||||
#endif
|
||||
if (cli_regcomp(®, pattern, flags) != 0)
|
||||
return 2;
|
||||
|
||||
if (pattern[strlen(pattern) - 1] == *PATHSEP) {
|
||||
snprintf(fname, 511, "%s" PATHSEP, filename);
|
||||
fname[512] = 0;
|
||||
} else {
|
||||
strncpy(fname, filename, 513);
|
||||
fname[512] = '\0';
|
||||
}
|
||||
|
||||
match = (cli_regexec(®, fname, 0, NULL, 0) == REG_NOMATCH) ? 0 : 1;
|
||||
cli_regfree(®);
|
||||
return match;
|
||||
}
|
||||
|
||||
int cli_is_abspath(const char *path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
int len = strlen(path);
|
||||
return (len > 2 && path[0] == '\\' && path[1] == '\\') || (len >= 2 && ((*path >= 'a' && *path <= 'z') || (*path >= 'A' && *path <= 'Z')) && path[1] == ':');
|
||||
#else
|
||||
return *path == '/';
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned int countlines(const char *filename)
|
||||
{
|
||||
FILE *fh;
|
||||
char buff[1024];
|
||||
unsigned int lines = 0;
|
||||
|
||||
if ((fh = fopen(filename, "r")) == NULL)
|
||||
return 0;
|
||||
|
||||
while (fgets(buff, sizeof(buff), fh)) {
|
||||
if (buff[0] == '#') continue;
|
||||
lines++;
|
||||
}
|
||||
|
||||
fclose(fh);
|
||||
return lines;
|
||||
}
|
||||
108
clamav/common/misc.h
Normal file
108
clamav/common/misc.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
* Copyright (C) 2007-2013 Sourcefire, Inc.
|
||||
*
|
||||
* Authors: Tomasz Kojm
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __MISC_H
|
||||
#define __MISC_H
|
||||
#ifndef _WIN32
|
||||
#include <sys/types.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "platform.h"
|
||||
#include "optparser.h"
|
||||
/* Maximum filenames under various systems - njh */
|
||||
#ifndef NAME_MAX /* e.g. Linux */
|
||||
#ifdef MAXNAMELEN /* e.g. Solaris */
|
||||
#define NAME_MAX MAXNAMELEN
|
||||
#else
|
||||
#ifdef FILENAME_MAX /* e.g. SCO */
|
||||
#define NAME_MAX FILENAME_MAX
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
#include <systemd/sd-daemon.h>
|
||||
#else
|
||||
#define sd_listen_fds(u) 0
|
||||
#define SD_LISTEN_FDS_START 3
|
||||
#define sd_is_socket(f, a, s, l) 1
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 1024
|
||||
#endif
|
||||
|
||||
#ifndef ADDR_LEN
|
||||
#define ADDR_LEN 13
|
||||
#endif
|
||||
|
||||
char *freshdbdir(void);
|
||||
void print_version(const char *dbdir);
|
||||
int check_flevel(void);
|
||||
const char *filelist(const struct optstruct *opts, int *err);
|
||||
int filecopy(const char *src, const char *dest);
|
||||
|
||||
#ifndef _WIN32
|
||||
/*Returns 0 on success (only the child process returns.*/
|
||||
int daemonize(void);
|
||||
|
||||
/*closes stdin, stdout, stderr. This is called by daemonize, but not
|
||||
* daemonize_all_return. Users of daemonize_all_return should call this
|
||||
* when initialization is complete.*/
|
||||
int close_std_descriptors(void);
|
||||
|
||||
/*Returns the return value of fork. All processes return */
|
||||
int daemonize_all_return(void);
|
||||
|
||||
/*Parent waits for a SIGINT or the child process to exit. If
|
||||
* it receives a SIGINT, it exits with exit code 0. If the child
|
||||
* exits (error), it exits with the child process's exit code.
|
||||
*
|
||||
* @param user If user is supplied and this function is being called
|
||||
* as root, daemonize_parent_wait will change the parent process
|
||||
* to user before calling wait so that the child process can signal
|
||||
* the parent when it is time to exit. The child process will still
|
||||
* return as root.
|
||||
*
|
||||
* @param log_file If user AND log_file are both supplied and this
|
||||
* function is being called as root, the ownership of log_file will
|
||||
* be changed to user.
|
||||
*/
|
||||
int daemonize_parent_wait(const char *const user, const char *const log_file);
|
||||
|
||||
/*Sends a SIGINT to the parent process. It also closes stdin, stdout,
|
||||
* and stderr.*/
|
||||
void daemonize_signal_parent(pid_t parentPid);
|
||||
|
||||
int drop_privileges(const char *const user, const char *const log_file);
|
||||
#endif /* _WIN32 */
|
||||
|
||||
const char *get_version(void);
|
||||
int match_regex(const char *filename, const char *pattern);
|
||||
int cli_is_abspath(const char *path);
|
||||
unsigned int countlines(const char *filename);
|
||||
|
||||
#endif
|
||||
1483
clamav/common/optparser.c
Normal file
1483
clamav/common/optparser.c
Normal file
File diff suppressed because it is too large
Load Diff
95
clamav/common/optparser.h
Normal file
95
clamav/common/optparser.h
Normal 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
617
clamav/common/output.c
Normal file
@@ -0,0 +1,617 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
* Copyright (C) 2007-2013 Sourcefire, Inc.
|
||||
*
|
||||
* Authors: Tomasz Kojm
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "clamav-config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#ifndef _WIN32
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#if HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_SELECT_H
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
#if defined(USE_SYSLOG) && !defined(C_AIX)
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
// libclamav
|
||||
#include "clamav.h"
|
||||
#include "others.h"
|
||||
#include "str.h"
|
||||
|
||||
#include "output.h"
|
||||
|
||||
#ifdef CL_THREAD_SAFE
|
||||
#include <pthread.h>
|
||||
pthread_mutex_t logg_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_t mdprintf_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
#endif
|
||||
|
||||
#if defined(C_LINUX) && defined(HAVE_LIBINTL_H)
|
||||
#include <libintl.h>
|
||||
#include <locale.h>
|
||||
|
||||
#define gettext_noop(s) s
|
||||
#define _(s) gettext(s)
|
||||
#define N_(s) gettext_noop(s)
|
||||
|
||||
#else
|
||||
|
||||
#define _(s) s
|
||||
#define N_(s) s
|
||||
|
||||
#endif
|
||||
|
||||
FILE *logg_fp = NULL;
|
||||
|
||||
short int logg_verbose = 0, logg_nowarn = 0, logg_lock = 1, logg_time = 0, logg_foreground = 1, logg_noflush = 0, logg_rotate = 0;
|
||||
off_t logg_size = 0;
|
||||
const char *logg_file = NULL;
|
||||
#if defined(USE_SYSLOG) && !defined(C_AIX)
|
||||
short logg_syslog;
|
||||
#endif
|
||||
|
||||
short int mprintf_disabled = 0, mprintf_verbose = 0, mprintf_quiet = 0,
|
||||
mprintf_stdout = 0, mprintf_nowarn = 0, mprintf_send_timeout = 100, mprintf_progress = 0;
|
||||
|
||||
#define ARGLEN(args, str, len) \
|
||||
{ \
|
||||
size_t arglen = 1, i; \
|
||||
char *pt; \
|
||||
va_start(args, str); \
|
||||
len = strlen(str); \
|
||||
for (i = 0; i < len - 1; i++) { \
|
||||
if (str[i] == '%') { \
|
||||
switch (str[++i]) { \
|
||||
case 's': \
|
||||
pt = va_arg(args, char *); \
|
||||
if (pt) \
|
||||
arglen += strlen(pt); \
|
||||
break; \
|
||||
case 'f': \
|
||||
va_arg(args, double); \
|
||||
arglen += 25; \
|
||||
break; \
|
||||
case 'l': \
|
||||
va_arg(args, long); \
|
||||
arglen += 20; \
|
||||
break; \
|
||||
default: \
|
||||
va_arg(args, int); \
|
||||
arglen += 10; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
va_end(args); \
|
||||
len += arglen; \
|
||||
}
|
||||
|
||||
int mdprintf(int desc, const char *str, ...)
|
||||
{
|
||||
va_list args;
|
||||
char buffer[512], *abuffer = NULL, *buff;
|
||||
int bytes, todo, ret = 0;
|
||||
size_t len;
|
||||
|
||||
ARGLEN(args, str, len);
|
||||
if (len <= sizeof(buffer)) {
|
||||
len = sizeof(buffer);
|
||||
buff = buffer;
|
||||
} else {
|
||||
abuffer = malloc(len);
|
||||
if (!abuffer) {
|
||||
len = sizeof(buffer);
|
||||
buff = buffer;
|
||||
} else {
|
||||
buff = abuffer;
|
||||
}
|
||||
}
|
||||
va_start(args, str);
|
||||
bytes = vsnprintf(buff, len, str, args);
|
||||
va_end(args);
|
||||
buff[len - 1] = 0;
|
||||
|
||||
if (bytes < 0) {
|
||||
if (len > sizeof(buffer))
|
||||
free(abuffer);
|
||||
return bytes;
|
||||
}
|
||||
if ((size_t)bytes >= len)
|
||||
bytes = len - 1;
|
||||
|
||||
todo = bytes;
|
||||
#ifdef CL_THREAD_SAFE
|
||||
/* make sure we don't mix sends from multiple threads,
|
||||
* important for IDSESSION */
|
||||
pthread_mutex_lock(&mdprintf_mutex);
|
||||
#endif
|
||||
while (todo > 0) {
|
||||
ret = send(desc, buff, bytes, 0);
|
||||
if (ret < 0) {
|
||||
struct timeval tv;
|
||||
if (errno != EWOULDBLOCK)
|
||||
break;
|
||||
/* didn't send anything yet */
|
||||
#ifdef CL_THREAD_SAFE
|
||||
pthread_mutex_unlock(&mdprintf_mutex);
|
||||
#endif
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = mprintf_send_timeout * 1000;
|
||||
do {
|
||||
fd_set wfds;
|
||||
FD_ZERO(&wfds);
|
||||
FD_SET(desc, &wfds);
|
||||
ret = select(desc + 1, NULL, &wfds, NULL, &tv);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
#ifdef CL_THREAD_SAFE
|
||||
pthread_mutex_lock(&mdprintf_mutex);
|
||||
#endif
|
||||
if (!ret) {
|
||||
/* timed out */
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
todo -= ret;
|
||||
buff += ret;
|
||||
}
|
||||
}
|
||||
#ifdef CL_THREAD_SAFE
|
||||
pthread_mutex_unlock(&mdprintf_mutex);
|
||||
#endif
|
||||
|
||||
if (len > sizeof(buffer))
|
||||
free(abuffer);
|
||||
|
||||
return ret < 0 ? -1 : bytes;
|
||||
}
|
||||
|
||||
static int rename_logg(STATBUF *sb)
|
||||
{
|
||||
char *rotate_file;
|
||||
size_t rotate_file_len;
|
||||
time_t t;
|
||||
struct tm tmp;
|
||||
|
||||
if (!logg_rotate) {
|
||||
if (logg_fp) {
|
||||
fprintf(logg_fp, "Log size = %lld, max = %lld\n", (long long int)sb->st_size, (long long int)logg_size);
|
||||
fprintf(logg_fp, "WARNING: Log size limit met but log file rotation turned off. Forcing log file rotation anyways.\n");
|
||||
}
|
||||
|
||||
logg_rotate = 1;
|
||||
}
|
||||
|
||||
rotate_file_len = strlen(logg_file) + strlen("-YYYY-MM-DD_HH:MM:SS.log");
|
||||
rotate_file = calloc(1, rotate_file_len + 1);
|
||||
if (!rotate_file) {
|
||||
if (logg_fp)
|
||||
fprintf(logg_fp, "Need to rotate log file due to size but ran out of memory.\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
t = time(NULL);
|
||||
|
||||
#ifdef _WIN32
|
||||
if (0 != localtime_s(&tmp, &t)) {
|
||||
#else
|
||||
if (!localtime_r(&t, &tmp)) {
|
||||
#endif
|
||||
if (logg_fp)
|
||||
fprintf(logg_fp, "Need to rotate log file due to size but could not get local time.\n");
|
||||
|
||||
free(rotate_file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
strcpy(rotate_file, logg_file);
|
||||
strftime(rotate_file + strlen(rotate_file) - strlen(".log"), rotate_file_len - strlen(rotate_file), "-%Y%m%d_%H%M%S.log", &tmp);
|
||||
|
||||
if (logg_fp) {
|
||||
fclose(logg_fp);
|
||||
logg_fp = NULL;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (0 == MoveFileA(logg_file, rotate_file)) {
|
||||
fprintf(stderr, "Failed to rename file with error code: %d\n", GetLastError());
|
||||
#else
|
||||
if (rename(logg_file, rotate_file)) {
|
||||
#endif
|
||||
free(rotate_file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(rotate_file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int logg_open(void)
|
||||
{
|
||||
STATBUF sb;
|
||||
|
||||
if (logg_file)
|
||||
if (logg_size > 0)
|
||||
if (CLAMSTAT(logg_file, &sb) != -1)
|
||||
if (sb.st_size > logg_size)
|
||||
if (rename_logg(&sb))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void logg_close(void)
|
||||
{
|
||||
#if defined(USE_SYSLOG) && !defined(C_AIX)
|
||||
if (logg_syslog)
|
||||
closelog();
|
||||
#endif
|
||||
|
||||
#ifdef CL_THREAD_SAFE
|
||||
pthread_mutex_lock(&logg_mutex);
|
||||
#endif
|
||||
if (logg_fp) {
|
||||
fclose(logg_fp);
|
||||
logg_fp = NULL;
|
||||
}
|
||||
#ifdef CL_THREAD_SAFE
|
||||
pthread_mutex_unlock(&logg_mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
int logg(loglevel_t loglevel, const char *str, ...)
|
||||
{
|
||||
va_list args;
|
||||
char buffer[1025], *abuffer = NULL, *buff;
|
||||
time_t currtime;
|
||||
size_t len;
|
||||
mode_t old_umask;
|
||||
#ifdef F_WRLCK
|
||||
struct flock fl;
|
||||
#endif
|
||||
|
||||
if ((loglevel == LOGG_DEBUG_NV && logg_verbose < 2) ||
|
||||
(loglevel == LOGG_DEBUG && !logg_verbose))
|
||||
return 0;
|
||||
|
||||
ARGLEN(args, str, len);
|
||||
if (len <= sizeof(buffer)) {
|
||||
len = sizeof(buffer);
|
||||
buff = buffer;
|
||||
} else {
|
||||
abuffer = malloc(len);
|
||||
if (!abuffer) {
|
||||
len = sizeof(buffer);
|
||||
buff = buffer;
|
||||
} else {
|
||||
buff = abuffer;
|
||||
}
|
||||
}
|
||||
va_start(args, str);
|
||||
vsnprintf(buff, len, str, args);
|
||||
va_end(args);
|
||||
buff[len - 1] = 0;
|
||||
|
||||
#ifdef CL_THREAD_SAFE
|
||||
pthread_mutex_lock(&logg_mutex);
|
||||
#endif
|
||||
|
||||
logg_open();
|
||||
|
||||
if (!logg_fp && logg_file) {
|
||||
old_umask = umask(0037);
|
||||
if ((logg_fp = fopen(logg_file, "at")) == NULL) {
|
||||
umask(old_umask);
|
||||
#ifdef CL_THREAD_SAFE
|
||||
pthread_mutex_unlock(&logg_mutex);
|
||||
#endif
|
||||
printf("ERROR: Can't open %s in append mode (check permissions!).\n", logg_file);
|
||||
if (len > sizeof(buffer))
|
||||
free(abuffer);
|
||||
return -1;
|
||||
} else
|
||||
umask(old_umask);
|
||||
|
||||
#ifdef F_WRLCK
|
||||
if (logg_lock) {
|
||||
memset(&fl, 0, sizeof(fl));
|
||||
fl.l_type = F_WRLCK;
|
||||
if (fcntl(fileno(logg_fp), F_SETLK, &fl) == -1) {
|
||||
#ifdef EOPNOTSUPP
|
||||
if (errno == EOPNOTSUPP)
|
||||
printf("WARNING: File locking not supported (NFS?)\n");
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#ifdef CL_THREAD_SAFE
|
||||
pthread_mutex_unlock(&logg_mutex);
|
||||
#endif
|
||||
printf("ERROR: %s is locked by another process\n", logg_file);
|
||||
if (len > sizeof(buffer))
|
||||
free(abuffer);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (logg_fp) {
|
||||
char flush = !logg_noflush;
|
||||
/* Need to avoid logging time for verbose messages when logverbose
|
||||
is not set or we get a bunch of timestamps in the log without
|
||||
newlines... */
|
||||
if (logg_time && ((loglevel != LOGG_DEBUG) || logg_verbose)) {
|
||||
char timestr[32];
|
||||
time(&currtime);
|
||||
cli_ctime(&currtime, timestr, sizeof(timestr));
|
||||
/* cut trailing \n */
|
||||
timestr[strlen(timestr) - 1] = '\0';
|
||||
fprintf(logg_fp, "%s -> ", timestr);
|
||||
}
|
||||
|
||||
if (loglevel == LOGG_ERROR) {
|
||||
fprintf(logg_fp, "ERROR: %s", buff);
|
||||
flush = 1;
|
||||
} else if (loglevel == LOGG_WARNING) {
|
||||
if (!logg_nowarn)
|
||||
fprintf(logg_fp, "WARNING: %s", buff);
|
||||
flush = 1;
|
||||
} else if (loglevel == LOGG_DEBUG || loglevel == LOGG_DEBUG_NV) {
|
||||
fprintf(logg_fp, "%s", buff);
|
||||
} else if (loglevel == LOGG_INFO_NF || loglevel == LOGG_INFO) {
|
||||
fprintf(logg_fp, "%s", buff);
|
||||
} else
|
||||
fprintf(logg_fp, "%s", buff);
|
||||
|
||||
if (flush)
|
||||
fflush(logg_fp);
|
||||
}
|
||||
|
||||
if (logg_foreground) {
|
||||
if (loglevel != LOGG_INFO_NF) {
|
||||
if (logg_time) {
|
||||
char timestr[32];
|
||||
time(&currtime);
|
||||
cli_ctime(&currtime, timestr, sizeof(timestr));
|
||||
/* cut trailing \n */
|
||||
timestr[strlen(timestr) - 1] = '\0';
|
||||
mprintf(loglevel, "%s -> %s", timestr, buff);
|
||||
} else {
|
||||
mprintf(loglevel, "%s", buff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(USE_SYSLOG) && !defined(C_AIX)
|
||||
if (logg_syslog) {
|
||||
cli_chomp(buff);
|
||||
if (loglevel == LOGG_ERROR) {
|
||||
syslog(LOG_ERR, "%s", buff);
|
||||
} else if (loglevel == LOGG_WARNING) {
|
||||
if (!logg_nowarn)
|
||||
syslog(LOG_WARNING, "%s", buff);
|
||||
} else if (loglevel == LOGG_DEBUG || loglevel == LOGG_DEBUG_NV) {
|
||||
syslog(LOG_DEBUG, "%s", buff);
|
||||
} else
|
||||
syslog(LOG_INFO, "%s", buff);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CL_THREAD_SAFE
|
||||
pthread_mutex_unlock(&logg_mutex);
|
||||
#endif
|
||||
|
||||
if (len > sizeof(buffer))
|
||||
free(abuffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mprintf(loglevel_t loglevel, const char *str, ...)
|
||||
{
|
||||
va_list args;
|
||||
FILE *fd;
|
||||
char buffer[512], *abuffer = NULL, *buff;
|
||||
size_t len;
|
||||
|
||||
if (mprintf_disabled)
|
||||
return;
|
||||
|
||||
fd = stdout;
|
||||
|
||||
ARGLEN(args, str, len);
|
||||
if (len <= sizeof(buffer)) {
|
||||
len = sizeof(buffer);
|
||||
buff = buffer;
|
||||
} else {
|
||||
abuffer = malloc(len);
|
||||
if (!abuffer) {
|
||||
len = sizeof(buffer);
|
||||
buff = buffer;
|
||||
} else {
|
||||
buff = abuffer;
|
||||
}
|
||||
}
|
||||
va_start(args, str);
|
||||
vsnprintf(buff, len, str, args);
|
||||
va_end(args);
|
||||
buff[len - 1] = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
do {
|
||||
int tmplen = len + 1;
|
||||
wchar_t *tmpw = malloc(tmplen * sizeof(wchar_t));
|
||||
char *nubuff;
|
||||
if (!tmpw)
|
||||
break;
|
||||
if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, buff, -1, tmpw, tmplen)) {
|
||||
free(tmpw);
|
||||
break;
|
||||
}
|
||||
/* FIXME CHECK IT'S REALLY UTF8 */
|
||||
nubuff = (char *)malloc(tmplen);
|
||||
if (!nubuff) {
|
||||
free(tmpw);
|
||||
break;
|
||||
}
|
||||
if (!WideCharToMultiByte(CP_OEMCP, 0, tmpw, -1, nubuff, tmplen, NULL, NULL)) {
|
||||
free(nubuff);
|
||||
free(tmpw);
|
||||
break;
|
||||
}
|
||||
free(tmpw);
|
||||
if (len > sizeof(buffer))
|
||||
free(abuffer);
|
||||
abuffer = buff = nubuff;
|
||||
len = sizeof(buffer) + 1;
|
||||
} while (0);
|
||||
#endif
|
||||
if (loglevel == LOGG_ERROR) {
|
||||
if (!mprintf_stdout)
|
||||
fd = stderr;
|
||||
fprintf(fd, "ERROR: %s", buff);
|
||||
} else if (!mprintf_quiet) {
|
||||
if (loglevel == LOGG_WARNING) {
|
||||
if (!mprintf_nowarn) {
|
||||
if (!mprintf_stdout)
|
||||
fd = stderr;
|
||||
fprintf(fd, "WARNING: %s", buff);
|
||||
}
|
||||
} else if (loglevel == LOGG_DEBUG) {
|
||||
if (mprintf_verbose)
|
||||
fprintf(fd, "%s", buff);
|
||||
} else if (loglevel == LOGG_INFO) {
|
||||
fprintf(fd, "%s", buff);
|
||||
} else
|
||||
fprintf(fd, "%s", buff);
|
||||
}
|
||||
|
||||
if (fd == stdout)
|
||||
fflush(stdout);
|
||||
|
||||
if (len > sizeof(buffer))
|
||||
free(abuffer);
|
||||
}
|
||||
|
||||
struct facstruct {
|
||||
const char *name;
|
||||
int code;
|
||||
};
|
||||
|
||||
#if defined(USE_SYSLOG) && !defined(C_AIX)
|
||||
static const struct facstruct facilitymap[] = {
|
||||
#ifdef LOG_AUTH
|
||||
{"LOG_AUTH", LOG_AUTH},
|
||||
#endif
|
||||
#ifdef LOG_AUTHPRIV
|
||||
{"LOG_AUTHPRIV", LOG_AUTHPRIV},
|
||||
#endif
|
||||
#ifdef LOG_CRON
|
||||
{"LOG_CRON", LOG_CRON},
|
||||
#endif
|
||||
#ifdef LOG_DAEMON
|
||||
{"LOG_DAEMON", LOG_DAEMON},
|
||||
#endif
|
||||
#ifdef LOG_FTP
|
||||
{"LOG_FTP", LOG_FTP},
|
||||
#endif
|
||||
#ifdef LOG_KERN
|
||||
{"LOG_KERN", LOG_KERN},
|
||||
#endif
|
||||
#ifdef LOG_LPR
|
||||
{"LOG_LPR", LOG_LPR},
|
||||
#endif
|
||||
#ifdef LOG_MAIL
|
||||
{"LOG_MAIL", LOG_MAIL},
|
||||
#endif
|
||||
#ifdef LOG_NEWS
|
||||
{"LOG_NEWS", LOG_NEWS},
|
||||
#endif
|
||||
#ifdef LOG_AUTH
|
||||
{"LOG_AUTH", LOG_AUTH},
|
||||
#endif
|
||||
#ifdef LOG_SYSLOG
|
||||
{"LOG_SYSLOG", LOG_SYSLOG},
|
||||
#endif
|
||||
#ifdef LOG_USER
|
||||
{"LOG_USER", LOG_USER},
|
||||
#endif
|
||||
#ifdef LOG_UUCP
|
||||
{"LOG_UUCP", LOG_UUCP},
|
||||
#endif
|
||||
#ifdef LOG_LOCAL0
|
||||
{"LOG_LOCAL0", LOG_LOCAL0},
|
||||
#endif
|
||||
#ifdef LOG_LOCAL1
|
||||
{"LOG_LOCAL1", LOG_LOCAL1},
|
||||
#endif
|
||||
#ifdef LOG_LOCAL2
|
||||
{"LOG_LOCAL2", LOG_LOCAL2},
|
||||
#endif
|
||||
#ifdef LOG_LOCAL3
|
||||
{"LOG_LOCAL3", LOG_LOCAL3},
|
||||
#endif
|
||||
#ifdef LOG_LOCAL4
|
||||
{"LOG_LOCAL4", LOG_LOCAL4},
|
||||
#endif
|
||||
#ifdef LOG_LOCAL5
|
||||
{"LOG_LOCAL5", LOG_LOCAL5},
|
||||
#endif
|
||||
#ifdef LOG_LOCAL6
|
||||
{"LOG_LOCAL6", LOG_LOCAL6},
|
||||
#endif
|
||||
#ifdef LOG_LOCAL7
|
||||
{"LOG_LOCAL7", LOG_LOCAL7},
|
||||
#endif
|
||||
{NULL, -1}};
|
||||
|
||||
int logg_facility(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; facilitymap[i].name; i++)
|
||||
if (!strcmp(facilitymap[i].name, name))
|
||||
return facilitymap[i].code;
|
||||
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
97
clamav/common/output.h
Normal file
97
clamav/common/output.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
* Copyright (C) 2007-2013 Sourcefire, Inc.
|
||||
*
|
||||
* Authors: Tomasz Kojm
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __OUTPUT_H
|
||||
#define __OUTPUT_H
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "clamav-config.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#if HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
int mdprintf(int desc, const char *str, ...) __attribute__((format(printf, 2, 3)));
|
||||
#else
|
||||
int mdprintf(int desc, const char *str, ...);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* legend:
|
||||
* NAME EXPLAIN
|
||||
* LOGG_INFO normal
|
||||
* LOGG_INFO_NF normal, no foreground (logfile and syslog only)
|
||||
* LOGG_DEBUG debug, verbose
|
||||
* LOGG_DEBUG_NV debug, non-verbose
|
||||
* LOGG_WARNING warning
|
||||
* LOGG_ERROR ERROR
|
||||
*/
|
||||
typedef enum loglevel {
|
||||
LOGG_INFO,
|
||||
LOGG_INFO_NF,
|
||||
LOGG_DEBUG,
|
||||
LOGG_DEBUG_NV,
|
||||
LOGG_WARNING,
|
||||
LOGG_ERROR
|
||||
} loglevel_t;
|
||||
|
||||
/*
|
||||
* @param loglevel legend:
|
||||
* NAME EXPLAIN
|
||||
* LOGG_INFO normal
|
||||
* LOGG_INFO_NF normal, no foreground (logfile and syslog only)
|
||||
* LOGG_DEBUG debug, verbose
|
||||
* LOGG_DEBUG_NV debug, non-verbose
|
||||
* LOGG_WARNING warning
|
||||
* LOGG_ERROR ERROR
|
||||
*
|
||||
* @return 0 fur success and -1 for error, e.g. log file access problems
|
||||
*/
|
||||
#ifdef __GNUC__
|
||||
int logg(loglevel_t loglevel, const char *str, ...) __attribute__((format(printf, 2, 3)));
|
||||
#else
|
||||
int logg(loglevel_t loglevel, const char *str, ...);
|
||||
#endif
|
||||
|
||||
void logg_close(void);
|
||||
extern short int logg_verbose, logg_nowarn, logg_lock, logg_time, logg_noflush, logg_rotate;
|
||||
extern off_t logg_size;
|
||||
extern const char *logg_file;
|
||||
|
||||
#if defined(USE_SYSLOG) && !defined(C_AIX)
|
||||
extern short logg_syslog;
|
||||
int logg_facility(const char *name);
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
void mprintf(loglevel_t loglevel, const char *str, ...) __attribute__((format(printf, 2, 3)));
|
||||
#else
|
||||
void mprintf(loglevel_t loglevel, const char *str, ...);
|
||||
#endif
|
||||
|
||||
extern short int mprintf_disabled, mprintf_verbose, mprintf_quiet, mprintf_nowarn, mprintf_stdout, mprintf_send_timeout, mprintf_progress;
|
||||
|
||||
#endif
|
||||
710
clamav/common/scanmem.c
Normal file
710
clamav/common/scanmem.c
Normal file
@@ -0,0 +1,710 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
* Copyright (C) 2005-2010 Gianluigi Tiesi <sherpya@netfarm.it>
|
||||
*
|
||||
* Authors: Gianluigi Tiesi
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <tlhelp32.h>
|
||||
|
||||
#include <psapi.h>
|
||||
#include <windns.h>
|
||||
|
||||
#include <clamav.h>
|
||||
#include <others.h>
|
||||
|
||||
#include "actions.h"
|
||||
#include "output.h"
|
||||
#include "clamdcom.h"
|
||||
#include "exescanner.h"
|
||||
#include "scanmem.h"
|
||||
|
||||
typedef int (*proc_callback)(PROCESSENTRY32 ProcStruct, MODULEENTRY32 me32, void *data, struct mem_info *info);
|
||||
int sock;
|
||||
struct optstruct *clamdopts;
|
||||
|
||||
static inline int lookup_cache(filelist_t **list, const char *filename)
|
||||
{
|
||||
filelist_t *current = *list;
|
||||
while (current) {
|
||||
/* Cache hit */
|
||||
if (!_stricmp(filename, current->filename)) {
|
||||
return current->res;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline void insert_cache(filelist_t **list, const char *filename,
|
||||
int res)
|
||||
{
|
||||
filelist_t *current = *list, *prev = NULL;
|
||||
|
||||
if (!current) /* New */
|
||||
*list = current = malloc(sizeof(filelist_t));
|
||||
else {
|
||||
while (current->next)
|
||||
current = current->next;
|
||||
prev = current;
|
||||
prev->next = current = malloc(sizeof(filelist_t));
|
||||
}
|
||||
|
||||
current->next = NULL;
|
||||
current->res = res;
|
||||
current->filename[0] = 0;
|
||||
strncat(current->filename, filename,
|
||||
MAX_PATH - 1 - strlen(current->filename));
|
||||
current->filename[MAX_PATH - 1] = 0;
|
||||
}
|
||||
|
||||
static inline void free_cache(filelist_t **list)
|
||||
{
|
||||
filelist_t *current, *prev;
|
||||
current = prev = *list;
|
||||
|
||||
if (!current)
|
||||
return;
|
||||
|
||||
do {
|
||||
prev = current;
|
||||
current = prev->next;
|
||||
free(prev);
|
||||
} while (current);
|
||||
}
|
||||
|
||||
static inline char *wc2mb(const wchar_t *wc, DWORD flags)
|
||||
{
|
||||
BOOL invalid = FALSE;
|
||||
DWORD len = 0, res = 0;
|
||||
char *mb = NULL;
|
||||
|
||||
len = WideCharToMultiByte(CP_ACP, flags, wc, -1, NULL, 0, NULL, &invalid);
|
||||
if (!len && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
|
||||
fprintf(stderr, "WideCharToMultiByte() failed with %d\n", GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mb = cli_malloc(len + 1);
|
||||
if (!mb) return NULL;
|
||||
|
||||
res = WideCharToMultiByte(CP_ACP, flags, wc, -1, mb, len, NULL, &invalid);
|
||||
if (res && ((!invalid || (flags != WC_NO_BEST_FIT_CHARS)))) return mb;
|
||||
free(mb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Needed to Scan System Processes */
|
||||
int EnablePrivilege(LPCSTR PrivilegeName, DWORD yesno)
|
||||
{
|
||||
HANDLE hToken;
|
||||
TOKEN_PRIVILEGES tp;
|
||||
LUID luid;
|
||||
|
||||
if (!LoadLibraryA("advapi32.dll")) {
|
||||
logg(LOGG_WARNING, "EnablePrivilege functions are missing\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!OpenProcessToken(
|
||||
GetCurrentProcess(),
|
||||
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_READ, &hToken))
|
||||
return 0;
|
||||
|
||||
if (!LookupPrivilegeValue(NULL, PrivilegeName, &luid))
|
||||
return 0;
|
||||
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
tp.Privileges[0].Attributes = yesno;
|
||||
|
||||
AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
|
||||
|
||||
CloseHandle(hToken);
|
||||
return (GetLastError() == ERROR_SUCCESS) ? 1 : 0;
|
||||
}
|
||||
|
||||
static char *getaltpath(const wchar_t *filename)
|
||||
{
|
||||
WIN32_FIND_DATAW wfdw;
|
||||
HANDLE hf = INVALID_HANDLE_VALUE;
|
||||
wchar_t *part = _wcsdup(filename);
|
||||
wchar_t comprev[MAX_PATH + 1] = L"", compose[MAX_PATH + 1];
|
||||
wchar_t *rev = comprev, *slash = part, *c = NULL;
|
||||
size_t l, la;
|
||||
size_t i;
|
||||
|
||||
do {
|
||||
if (slash != part)
|
||||
*slash = 0;
|
||||
|
||||
/* c: d: etc */
|
||||
if ((wcslen(part) == 2) && (part[1] == L':')) {
|
||||
*rev++ = L':';
|
||||
*rev++ = part[0];
|
||||
break;
|
||||
}
|
||||
|
||||
hf = FindFirstFileW(part, &wfdw);
|
||||
if (hf == INVALID_HANDLE_VALUE) /* Network path */
|
||||
{
|
||||
for (i = wcslen(part); i > 0; i--)
|
||||
*rev++ = part[i - 1];
|
||||
break;
|
||||
}
|
||||
FindClose(hf);
|
||||
l = wcslen(wfdw.cFileName);
|
||||
la = wcslen(wfdw.cAlternateFileName);
|
||||
|
||||
if (la)
|
||||
for (i = la; i > 0; i--)
|
||||
*rev++ = *(wfdw.cAlternateFileName + i - 1);
|
||||
else
|
||||
for (i = l; i > 0; i--)
|
||||
*rev++ = *(wfdw.cFileName + i - 1);
|
||||
*rev++ = '\\';
|
||||
|
||||
} while ((slash = wcsrchr(part, L'\\')));
|
||||
|
||||
rev = comprev;
|
||||
c = compose;
|
||||
for (i = wcslen(rev); i > 0; i--)
|
||||
*c++ = *(rev + i - 1);
|
||||
*c = 0;
|
||||
|
||||
free(part);
|
||||
return wc2mb(compose, WC_NO_BEST_FIT_CHARS);
|
||||
}
|
||||
|
||||
int walkmodules_th(proc_callback callback, void *data, struct mem_info *info)
|
||||
{
|
||||
HANDLE hSnap = INVALID_HANDLE_VALUE, hModuleSnap = INVALID_HANDLE_VALUE;
|
||||
PROCESSENTRY32 ps;
|
||||
MODULEENTRY32 me32;
|
||||
|
||||
logg(LOGG_INFO, " *** Memory Scan: using ToolHelp ***\n\n");
|
||||
|
||||
hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (hSnap == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
|
||||
ps.dwSize = sizeof(PROCESSENTRY32);
|
||||
|
||||
if (!Process32First(hSnap, &ps)) {
|
||||
CloseHandle(hSnap);
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
/* system process */
|
||||
if (!ps.th32ProcessID)
|
||||
continue;
|
||||
hModuleSnap = CreateToolhelp32Snapshot(
|
||||
TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, ps.th32ProcessID);
|
||||
if (hModuleSnap == INVALID_HANDLE_VALUE)
|
||||
continue;
|
||||
|
||||
me32.dwSize = sizeof(MODULEENTRY32);
|
||||
if (!Module32First(hModuleSnap, &me32)) {
|
||||
CloseHandle(hModuleSnap);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check and transform non ANSI filenames to ANSI using altnames */
|
||||
if (GetModuleFileNameEx) {
|
||||
HANDLE hFile = CreateFile(
|
||||
me32.szExePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
DWORD err = GetLastError();
|
||||
wchar_t name[MAX_PATH + 1];
|
||||
char *converted = NULL;
|
||||
HANDLE p;
|
||||
|
||||
if (err == ERROR_BAD_NETPATH) {
|
||||
logg(LOGG_WARNING, "Warning scanning files on non-ansi network paths is not "
|
||||
"supported\n");
|
||||
logg(LOGG_WARNING, "File: %s\n", me32.szExePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((err != ERROR_INVALID_NAME) && (err != ERROR_PATH_NOT_FOUND)) {
|
||||
logg(LOGG_WARNING, "Expected ERROR_INVALID_NAME/ERROR_PATH_NOT_FOUND but got %d\n",
|
||||
err);
|
||||
continue;
|
||||
}
|
||||
|
||||
p = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE,
|
||||
ps.th32ProcessID);
|
||||
if (!GetModuleFileNameEx(p, NULL, name, MAX_PATH)) {
|
||||
logg(LOGG_WARNING, "GetModuleFileNameExW() failed %d\n", GetLastError());
|
||||
CloseHandle(p);
|
||||
continue;
|
||||
}
|
||||
CloseHandle(p);
|
||||
|
||||
if (!(converted = getaltpath(name))) {
|
||||
logg(LOGG_WARNING, "Cannot map filename to ANSI codepage\n");
|
||||
continue;
|
||||
}
|
||||
strcpy(me32.szExePath, converted);
|
||||
free(converted);
|
||||
} else
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
|
||||
do
|
||||
if (callback(ps, me32, data, info))
|
||||
break;
|
||||
while (Module32Next(hModuleSnap, &me32));
|
||||
|
||||
CloseHandle(hModuleSnap);
|
||||
} while (Process32Next(hSnap, &ps));
|
||||
CloseHandle(hSnap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int walkmodules_psapi(proc_callback callback, void *data, struct mem_info *info)
|
||||
{
|
||||
DWORD procs[1024], needed, nprocs, mneeded;
|
||||
HANDLE hProc;
|
||||
HMODULE mods[1024];
|
||||
PROCESSENTRY32 ps;
|
||||
MODULEENTRY32 me32;
|
||||
MODULEINFO mi;
|
||||
int i, j;
|
||||
|
||||
logg(LOGG_INFO, " *** Memory Scan: using PsApi ***\n\n");
|
||||
|
||||
if (!EnumProcesses(procs, sizeof(procs), &needed))
|
||||
return -1;
|
||||
|
||||
nprocs = needed / sizeof(DWORD);
|
||||
|
||||
memset(&ps, 0, sizeof(PROCESSENTRY32));
|
||||
memset(&me32, 0, sizeof(MODULEENTRY32));
|
||||
ps.dwSize = sizeof(PROCESSENTRY32);
|
||||
me32.dwSize = sizeof(MODULEENTRY32);
|
||||
|
||||
for (i = 0; i < nprocs; i++) {
|
||||
if (!procs[i])
|
||||
continue; /* System process */
|
||||
|
||||
hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE,
|
||||
procs[i]);
|
||||
|
||||
if (!hProc)
|
||||
continue;
|
||||
|
||||
if (!EnumProcessModules(hProc, mods, sizeof(mods),
|
||||
&mneeded)) {
|
||||
CloseHandle(hProc);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!GetModuleBaseName(hProc, mods[0], ps.szExeFile,
|
||||
MAX_PATH - 1)) {
|
||||
CloseHandle(hProc);
|
||||
continue;
|
||||
}
|
||||
|
||||
ps.th32ProcessID = procs[i];
|
||||
|
||||
for (j = 0; j < (mneeded / sizeof(HMODULE)); j++) {
|
||||
if (!GetModuleBaseNameA(hProc, mods[j], me32.szModule,
|
||||
MAX_PATH - 1))
|
||||
continue;
|
||||
|
||||
if (!GetModuleFileNameExA(hProc, mods[j], me32.szExePath,
|
||||
MAX_PATH - 1))
|
||||
continue;
|
||||
|
||||
if (!GetModuleInformation(hProc, mods[j], &mi,
|
||||
sizeof(mi)))
|
||||
continue;
|
||||
|
||||
me32.hModule = mods[j];
|
||||
me32.th32ProcessID = procs[i];
|
||||
me32.modBaseAddr = mi.lpBaseOfDll;
|
||||
me32.modBaseSize = mi.SizeOfImage;
|
||||
if (callback(ps, me32, data, info))
|
||||
break;
|
||||
}
|
||||
CloseHandle(hProc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kill_process(DWORD pid)
|
||||
{
|
||||
HANDLE hProc;
|
||||
if (GetCurrentProcessId() == pid) {
|
||||
logg(LOGG_WARNING, "Don't want to kill myself\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((hProc = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, pid))) {
|
||||
TerminateProcess(hProc, 0);
|
||||
if (WaitForSingleObject(hProc, TIMEOUT_MODULE) != WAIT_OBJECT_0)
|
||||
logg(LOGG_WARNING, "Unable to unload process from memory\n");
|
||||
CloseHandle(hProc);
|
||||
} else
|
||||
logg(LOGG_WARNING, "OpenProcess() failed %lu\n", GetLastError());
|
||||
return 1; /* Skip to next process anyway */
|
||||
}
|
||||
|
||||
/* Not so safe ;) */
|
||||
int unload_module(DWORD pid, HANDLE hModule)
|
||||
{
|
||||
DWORD rc = 1;
|
||||
HANDLE ht;
|
||||
HANDLE hProc;
|
||||
|
||||
if (GetCurrentProcessId() == pid) {
|
||||
logg(LOGG_WARNING, "Don't want to unload modules from myself\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
hProc = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |
|
||||
PROCESS_VM_WRITE | PROCESS_VM_READ,
|
||||
FALSE, pid);
|
||||
|
||||
if (!hProc) {
|
||||
logg(LOGG_WARNING, "OpenProcess() failed %lu\n", GetLastError());
|
||||
return 1; /* Skip to next process */
|
||||
}
|
||||
|
||||
if ((ht = CreateRemoteThread(
|
||||
hProc, 0, 0, (LPTHREAD_START_ROUTINE)FreeLibrary, hModule, 0,
|
||||
&rc))) {
|
||||
if (WaitForSingleObject(ht, TIMEOUT_MODULE) == WAIT_TIMEOUT) {
|
||||
CloseHandle(ht);
|
||||
CloseHandle(hProc);
|
||||
logg(LOGG_INFO, "The module may trying to trick us, killing the process, please "
|
||||
"rescan\n");
|
||||
return kill_process(pid);
|
||||
}
|
||||
CloseHandle(ht);
|
||||
rc = 0; /* Continue scanning this process */
|
||||
} else {
|
||||
DWORD res = GetLastError();
|
||||
if (res == ERROR_CALL_NOT_IMPLEMENTED) {
|
||||
logg(LOGG_WARNING, "Module unloading is not supported on this OS\n");
|
||||
rc = -1; /* Don't complain about removing/moving the file */
|
||||
} else {
|
||||
logg(LOGG_ERROR, "CreateRemoteThread() failed %lu\n", res);
|
||||
rc = 1; /* Skip to next process */
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(hProc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define FILLBYTES(dst) \
|
||||
if (IsBadReadPtr(seek, sizeof(dst))) { \
|
||||
logg(LOGG_ERROR, "ScanMem Align: Bad pointer!!!\n"); \
|
||||
return 1; \
|
||||
} \
|
||||
memcpy(&dst, seek, sizeof(dst))
|
||||
|
||||
/* PE Realignment - FIXME: a lot of code is copy/paste from exeScanner.c */
|
||||
int align_pe(unsigned char *buffer, size_t size)
|
||||
{
|
||||
int i = 0;
|
||||
uint16_t e_mz;
|
||||
uint32_t e_lfanew, e_magic;
|
||||
unsigned char *seek = buffer;
|
||||
PIMAGE_FILE_HEADER pehdr;
|
||||
PIMAGE_OPTIONAL_HEADER32 opthdr;
|
||||
PIMAGE_SECTION_HEADER sechdr;
|
||||
|
||||
FILLBYTES(e_mz);
|
||||
if (e_mz != IMAGE_DOS_SIGNATURE) {
|
||||
/* cli_dbgmsg("ScanMem Align: DOS Signature not found\n"); */
|
||||
return 0;
|
||||
}
|
||||
|
||||
seek += 0x3c;
|
||||
|
||||
FILLBYTES(e_lfanew);
|
||||
if (!e_lfanew) {
|
||||
/* cli_dbgmsg("ScanMem Align: Invalid PE offset\n"); */
|
||||
return 0;
|
||||
}
|
||||
seek = buffer + e_lfanew;
|
||||
|
||||
/* PE Signature 'PE' */
|
||||
FILLBYTES(e_magic);
|
||||
if (e_magic != IMAGE_NT_SIGNATURE) {
|
||||
/* cli_dbgmsg("ScanMem Align: PE Signature not found\n"); */
|
||||
return 0;
|
||||
}
|
||||
seek += sizeof(e_magic);
|
||||
|
||||
if (IsBadReadPtr(seek, sizeof(IMAGE_FILE_HEADER)))
|
||||
return 0;
|
||||
pehdr = (PIMAGE_FILE_HEADER)seek;
|
||||
seek += sizeof(IMAGE_FILE_HEADER);
|
||||
|
||||
if (IsBadReadPtr(seek, sizeof(IMAGE_OPTIONAL_HEADER32)))
|
||||
return 0;
|
||||
opthdr = (PIMAGE_OPTIONAL_HEADER32)seek;
|
||||
seek += sizeof(IMAGE_OPTIONAL_HEADER32);
|
||||
|
||||
/* Invalid sections number */
|
||||
if ((pehdr->NumberOfSections < 1) || (pehdr->NumberOfSections > 32)) {
|
||||
/* cli_dbgmsg("ScanMem Align: Invalid sections number\n"); */
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < pehdr->NumberOfSections; i++) {
|
||||
if (IsBadWritePtr(seek, sizeof(IMAGE_SECTION_HEADER)))
|
||||
return 0;
|
||||
sechdr = (PIMAGE_SECTION_HEADER)seek;
|
||||
seek += sizeof(IMAGE_SECTION_HEADER);
|
||||
sechdr->PointerToRawData = sechdr->VirtualAddress;
|
||||
sechdr->SizeOfRawData = sechdr->Misc.VirtualSize;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dump_pe(const char *filename, PROCESSENTRY32 ProcStruct,
|
||||
MODULEENTRY32 me32)
|
||||
{
|
||||
#ifdef _WIN64 /* MinGW has a broken header for ReadProcessMemory() */
|
||||
size_t bytesread = 0;
|
||||
#else
|
||||
DWORD bytesread = 0;
|
||||
#endif
|
||||
DWORD byteswrite = 0;
|
||||
int ret = -1;
|
||||
HANDLE hFile = INVALID_HANDLE_VALUE, hProc = NULL;
|
||||
unsigned char *buffer = NULL;
|
||||
|
||||
if (!(hProc = OpenProcess(PROCESS_VM_READ, FALSE, ProcStruct.th32ProcessID)))
|
||||
return -1;
|
||||
|
||||
buffer = malloc((size_t)me32.modBaseSize);
|
||||
if (!ReadProcessMemory(hProc, me32.modBaseAddr, buffer,
|
||||
(size_t)me32.modBaseSize, &bytesread)) {
|
||||
free(buffer);
|
||||
CloseHandle(hProc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
CloseHandle(hProc);
|
||||
|
||||
/* PE Realignment */
|
||||
align_pe(buffer, me32.modBaseSize);
|
||||
|
||||
hFile = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
logg(LOGG_INFO, "Error creating %s\n", filename);
|
||||
free(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (WriteFile(hFile, buffer, (DWORD)bytesread, &byteswrite, NULL))
|
||||
ret = _open_osfhandle((intptr_t)hFile, O_RDONLY | O_BINARY);
|
||||
free(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int scanfile(const char *filename, scanmem_data *scan_data, struct mem_info *info)
|
||||
{
|
||||
int fd;
|
||||
int scantype;
|
||||
int ret = CL_CLEAN;
|
||||
const char *virname = NULL;
|
||||
|
||||
logg(LOGG_DEBUG, "Scanning %s\n", filename);
|
||||
|
||||
if ((fd = safe_open(filename, O_RDONLY | O_BINARY)) == -1) {
|
||||
logg(LOGG_WARNING, "Can't open file %s, %s\n", filename, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (info->d) { // clamdscan
|
||||
if (optget(info->opts, "stream")->enabled)
|
||||
scantype = STREAM;
|
||||
else if (optget(info->opts, "multiscan")->enabled)
|
||||
scantype = MULTI;
|
||||
else if (optget(info->opts, "allmatch")->enabled)
|
||||
scantype = ALLMATCH;
|
||||
else
|
||||
scantype = CONT;
|
||||
|
||||
if ((sock = dconnect(clamdopts)) < 0) {
|
||||
info->errors++;
|
||||
return -1;
|
||||
}
|
||||
if (dsresult(sock, scantype, filename, NULL, &info->errors, clamdopts) > 0) {
|
||||
info->ifiles++;
|
||||
ret = CL_VIRUS;
|
||||
}
|
||||
} else { // clamscan
|
||||
ret = cl_scandesc(fd, filename, &virname, &info->blocks, info->engine, info->options);
|
||||
if (ret == CL_VIRUS) {
|
||||
logg(LOGG_INFO, "%s: %s FOUND\n", filename, virname);
|
||||
info->ifiles++;
|
||||
} else if (scan_data->printclean) {
|
||||
logg(LOGG_INFO, "%s: OK \n", filename);
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int scanmem_cb(PROCESSENTRY32 ProcStruct, MODULEENTRY32 me32, void *data, struct mem_info *info)
|
||||
{
|
||||
scanmem_data *scan_data = data;
|
||||
int rc = 0;
|
||||
int isprocess = 0;
|
||||
char modulename[MAX_PATH] = "";
|
||||
char expandmodule[MAX_PATH] = "";
|
||||
|
||||
if (!scan_data)
|
||||
return 0;
|
||||
scan_data->res = CL_CLEAN;
|
||||
|
||||
modulename[0] = 0;
|
||||
/* Special case, btw why I get \SystemRoot\ in process szExePath?
|
||||
There are also other cases? */
|
||||
if ((strlen(me32.szExePath) > 12) &&
|
||||
!strncmp(me32.szExePath, "\\SystemRoot\\", 12)) {
|
||||
expandmodule[0] = 0;
|
||||
strncat(expandmodule, me32.szExePath, MAX_PATH - 1 - strlen(expandmodule));
|
||||
expandmodule[MAX_PATH - 1] = 0;
|
||||
snprintf(expandmodule, MAX_PATH - 1, "%%SystemRoot%%\\%s",
|
||||
&me32.szExePath[12]);
|
||||
expandmodule[MAX_PATH - 1] = 0;
|
||||
ExpandEnvironmentStrings(expandmodule, modulename, MAX_PATH - 1);
|
||||
modulename[MAX_PATH - 1] = 0;
|
||||
}
|
||||
|
||||
if (!modulename[0]) {
|
||||
strncpy(modulename, me32.szExePath, MAX_PATH - 1);
|
||||
modulename[MAX_PATH - 1] = 0;
|
||||
}
|
||||
|
||||
scan_data->res = lookup_cache(&scan_data->files, modulename);
|
||||
isprocess = !_stricmp(ProcStruct.szExeFile, modulename) ||
|
||||
!_stricmp(ProcStruct.szExeFile, me32.szModule);
|
||||
|
||||
if (scan_data->res == -1) {
|
||||
if (isprocess)
|
||||
scan_data->processes++;
|
||||
else
|
||||
scan_data->modules++;
|
||||
|
||||
info->files++;
|
||||
|
||||
/* check for module exclusion */
|
||||
scan_data->res = CL_CLEAN;
|
||||
if (!(scan_data->exclude && chkpath(modulename, clamdopts)))
|
||||
scan_data->res = scanfile(modulename, scan_data, info);
|
||||
|
||||
if ((scan_data->res != CL_VIRUS) && is_packed(modulename)) {
|
||||
char *dumped = cli_gentemp(NULL);
|
||||
int fd = -1;
|
||||
if ((fd = dump_pe(dumped, ProcStruct, me32)) > 0) {
|
||||
close(fd);
|
||||
scan_data->res = scanfile(dumped, scan_data, info);
|
||||
DeleteFile(dumped);
|
||||
}
|
||||
free(dumped);
|
||||
}
|
||||
insert_cache(&scan_data->files, modulename, scan_data->res);
|
||||
}
|
||||
|
||||
if (scan_data->res == CL_VIRUS) {
|
||||
if (isprocess && scan_data->kill) {
|
||||
logg(LOGG_INFO, "Unloading program %s from memory\n", modulename);
|
||||
rc = kill_process(ProcStruct.th32ProcessID);
|
||||
} else if (scan_data->unload) {
|
||||
logg(LOGG_INFO, "Unloading module %s from %s\n", me32.szModule, modulename);
|
||||
if ((rc = unload_module(ProcStruct.th32ProcessID, me32.hModule)) == -1)
|
||||
/* CreateProcessThread() is not implemented */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (action)
|
||||
action(modulename);
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int scanmem(struct mem_info *info)
|
||||
{
|
||||
scanmem_data data;
|
||||
data.files = NULL;
|
||||
data.printclean = 1;
|
||||
data.kill = 0;
|
||||
data.unload = 0;
|
||||
data.exclude = 0;
|
||||
data.res = CL_CLEAN;
|
||||
data.processes = 0;
|
||||
data.modules = 0;
|
||||
|
||||
HMODULE psapi_ok = LoadLibrary("psapi.dll");
|
||||
HMODULE k32_ok = LoadLibrary("kernel32.dll");
|
||||
|
||||
if (!(psapi_ok || k32_ok)) {
|
||||
logg(LOGG_INFO, " *** Memory Scanning is not supported on this OS ***\n\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (optget(info->opts, "infected")->enabled)
|
||||
data.printclean = 0;
|
||||
if (optget(info->opts, "kill")->enabled)
|
||||
data.kill = 1;
|
||||
if (optget(info->opts, "unload")->enabled)
|
||||
data.unload = 1;
|
||||
if (optget(info->opts, "exclude")->enabled)
|
||||
data.exclude = 1;
|
||||
|
||||
if (info->d) {
|
||||
if ((sock = dconnect(clamdopts)) < 0) {
|
||||
info->errors++;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
logg(LOGG_INFO, " *** Scanning Programs in Computer Memory ***\n");
|
||||
|
||||
if (!EnablePrivilege(SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED))
|
||||
logg(LOGG_INFO, "---Please login as an Administrator to scan System processes loaded "
|
||||
"in computer memory---\n");
|
||||
|
||||
if (k32_ok)
|
||||
walkmodules_th(scanmem_cb, (void *)&data, info);
|
||||
else
|
||||
walkmodules_psapi(scanmem_cb, (void *)&data, info);
|
||||
free_cache(&data.files);
|
||||
|
||||
logg(LOGG_INFO, "\n *** Scanned %lu processes - %lu modules ***\n", data.processes,
|
||||
data.modules);
|
||||
logg(LOGG_INFO, " *** Computer Memory Scan Completed ***\n\n");
|
||||
return data.res;
|
||||
}
|
||||
68
clamav/common/scanmem.h
Normal file
68
clamav/common/scanmem.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
* Copyright (C) 2005-2010 Gianluigi Tiesi <sherpya@netfarm.it>
|
||||
*
|
||||
* Authors: Gianluigi Tiesi
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __SCANMEM_H
|
||||
#define __SCANMEM_H
|
||||
|
||||
#ifndef TH32CS_SNAPMODULE32
|
||||
#define TH32CS_SNAPMODULE32 0x00000010
|
||||
#endif
|
||||
|
||||
#define TIMEOUT_MODULE 30000
|
||||
|
||||
int scanmem(struct mem_info *info);
|
||||
|
||||
/* cache helpers */
|
||||
typedef struct _filelist_t {
|
||||
char filename[MAX_PATH];
|
||||
int res;
|
||||
struct _filelist_t *next;
|
||||
} filelist_t;
|
||||
|
||||
/* Callback */
|
||||
typedef struct _cb_data_t {
|
||||
const char *filename;
|
||||
size_t size, count;
|
||||
int oldvalue;
|
||||
int fd;
|
||||
} cb_data_t;
|
||||
|
||||
typedef struct _scanmem_data_t {
|
||||
filelist_t *files;
|
||||
int printclean, kill, unload, exclude;
|
||||
int res;
|
||||
uint32_t processes, modules;
|
||||
|
||||
} scanmem_data;
|
||||
|
||||
struct mem_info {
|
||||
unsigned int d; /*1 = clamdscan, 0 = clamscan */
|
||||
unsigned int files; /* number of scanned files */
|
||||
unsigned int ifiles; /* number of infected files */
|
||||
unsigned long int blocks; /* number of *scanned* 16kb blocks */
|
||||
unsigned int errors;
|
||||
|
||||
struct cl_engine *engine;
|
||||
const struct optstruct *opts;
|
||||
struct cl_scan_options *options;
|
||||
};
|
||||
|
||||
#endif
|
||||
228
clamav/common/service.c
Normal file
228
clamav/common/service.c
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
* Copyright (C) 2008-2010 Gianluigi Tiesi <sherpya@netfarm.it>
|
||||
*
|
||||
* Authors: Gianluigi Tiesi
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <platform.h>
|
||||
#include <winsvc.h>
|
||||
|
||||
#include "service.h"
|
||||
#include "output.h"
|
||||
|
||||
static SERVICE_STATUS svc;
|
||||
static SERVICE_STATUS_HANDLE svc_handle;
|
||||
static SERVICE_TABLE_ENTRYA DT[] = {{"Service", ServiceMain}, {NULL, NULL}};
|
||||
|
||||
static HANDLE evStart;
|
||||
static HANDLE DispatcherThread;
|
||||
static int checkpoint_every = 5000;
|
||||
|
||||
int svc_uninstall(const char *name, int verbose)
|
||||
{
|
||||
SC_HANDLE sm, svc;
|
||||
int ret = 1;
|
||||
|
||||
if (!(sm = OpenSCManagerA(NULL, NULL, DELETE))) {
|
||||
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
|
||||
fprintf(stderr, "Windows Services are not supported on this Platform\n");
|
||||
else
|
||||
fprintf(stderr, "Unable to Open SCManager (%d)\n", GetLastError());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((svc = OpenServiceA(sm, name, DELETE))) {
|
||||
if (DeleteService(svc)) {
|
||||
if (verbose) printf("Service %s successfully removed\n", name);
|
||||
} else {
|
||||
fprintf(stderr, "Unable to Open Service %s (%d)\n", name, GetLastError());
|
||||
ret = 0;
|
||||
}
|
||||
} else {
|
||||
if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
|
||||
if (verbose) printf("Service %s does not exist\n", name);
|
||||
} else {
|
||||
fprintf(stderr, "Unable to Open Service %s (%d)\n", name, GetLastError());
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (svc) CloseServiceHandle(svc);
|
||||
CloseServiceHandle(sm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int svc_install(const char *name, const char *dname, const char *desc)
|
||||
{
|
||||
SC_HANDLE sm, svc;
|
||||
char modulepath[MAX_PATH];
|
||||
char binpath[MAX_PATH];
|
||||
SERVICE_DESCRIPTIONA sdesc = {(char *)desc};
|
||||
|
||||
if (!GetModuleFileName(NULL, modulepath, MAX_PATH - 1)) {
|
||||
fprintf(stderr, "Unable to get the executable name (%d)\n", GetLastError());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!svc_uninstall(name, 0)) return 0;
|
||||
|
||||
if (!(sm = OpenSCManagerA(NULL, NULL, SC_MANAGER_CREATE_SERVICE | DELETE))) {
|
||||
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
|
||||
fprintf(stderr, "Windows Services are not supported on this Platform\n");
|
||||
else
|
||||
fprintf(stderr, "Unable to Open SCManager (%d)\n", GetLastError());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strchr(modulepath, ' '))
|
||||
snprintf(binpath, MAX_PATH - 1, "\"%s\" --daemon --service-mode", modulepath);
|
||||
else
|
||||
snprintf(binpath, MAX_PATH - 1, "%s --daemon --service-mode", modulepath);
|
||||
|
||||
svc = CreateServiceA(sm, name, dname, SERVICE_CHANGE_CONFIG,
|
||||
SERVICE_WIN32_OWN_PROCESS,
|
||||
SERVICE_DEMAND_START,
|
||||
SERVICE_ERROR_NORMAL,
|
||||
binpath,
|
||||
NULL, /* Load group order */
|
||||
NULL, /* Tag Id */
|
||||
NULL, /* Dependencies */
|
||||
NULL, /* User -> Local System */
|
||||
"");
|
||||
|
||||
if (!svc) {
|
||||
fprintf(stderr, "Unable to Create Service %s (%d)\n", name, GetLastError());
|
||||
CloseServiceHandle(sm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ChangeServiceConfig2A() */
|
||||
if (!ChangeServiceConfig2A(svc, SERVICE_CONFIG_DESCRIPTION, &sdesc))
|
||||
fprintf(stderr, "Unable to set description for Service %s (%d)\n", name, GetLastError());
|
||||
|
||||
CloseServiceHandle(svc);
|
||||
CloseServiceHandle(sm);
|
||||
|
||||
printf("Service %s successfully created.\n", name);
|
||||
printf("Use 'net start %s' and 'net stop %s' to start/stop the service.\n", name, name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void svc_getcpvalue(const char *name)
|
||||
{
|
||||
HKEY hKey;
|
||||
DWORD dwType;
|
||||
DWORD value, vlen = sizeof(DWORD);
|
||||
char subkey[MAX_PATH];
|
||||
|
||||
snprintf(subkey, MAX_PATH - 1, "SYSTEM\\CurrentControlSet\\Services\\%s", name);
|
||||
|
||||
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS)
|
||||
return;
|
||||
|
||||
if ((RegQueryValueExA(hKey, "Checkpoint", NULL, &dwType, (LPBYTE)&value, &vlen) == ERROR_SUCCESS) &&
|
||||
(vlen == sizeof(DWORD) && (dwType == REG_DWORD)))
|
||||
checkpoint_every = value;
|
||||
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
|
||||
void svc_register(const char *name)
|
||||
{
|
||||
DWORD tid;
|
||||
DT->lpServiceName = (char *)name;
|
||||
svc_getcpvalue(name);
|
||||
|
||||
evStart = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
DispatcherThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)StartServiceCtrlDispatcherA, (LPVOID)DT, 0, &tid);
|
||||
}
|
||||
|
||||
void svc_ready(void)
|
||||
{
|
||||
WaitForSingleObject(evStart, INFINITE);
|
||||
|
||||
svc.dwCurrentState = SERVICE_RUNNING;
|
||||
svc.dwControlsAccepted |= SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
||||
svc.dwCheckPoint = 0;
|
||||
|
||||
if (!SetServiceStatus(svc_handle, &svc)) {
|
||||
logg(LOGG_INFO, "[service] SetServiceStatus() failed with %d\n", GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int svc_checkpoint(const char *type, const char *name, unsigned int custom, void *context)
|
||||
{
|
||||
if (svc.dwCurrentState == SERVICE_START_PENDING) {
|
||||
svc.dwCheckPoint++;
|
||||
if ((svc.dwCheckPoint % checkpoint_every) == 0)
|
||||
SetServiceStatus(svc_handle, &svc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WINAPI ServiceCtrlHandler(DWORD code)
|
||||
{
|
||||
switch (code) {
|
||||
case SERVICE_CONTROL_STOP:
|
||||
case SERVICE_CONTROL_SHUTDOWN:
|
||||
svc.dwCurrentState = SERVICE_STOPPED;
|
||||
svc.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
|
||||
SetServiceStatus(svc_handle, &svc);
|
||||
return;
|
||||
case SERVICE_CONTROL_INTERROGATE:
|
||||
break;
|
||||
}
|
||||
|
||||
SetServiceStatus(svc_handle, &svc);
|
||||
}
|
||||
|
||||
BOOL WINAPI cw_stop_ctrl_handler(DWORD CtrlType)
|
||||
{
|
||||
if (CtrlType == CTRL_C_EVENT) {
|
||||
SetConsoleCtrlHandler(cw_stop_ctrl_handler, FALSE);
|
||||
fprintf(stderr, "Control+C pressed, aborting...\n");
|
||||
exit(0);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv)
|
||||
{
|
||||
svc.dwServiceType = SERVICE_WIN32;
|
||||
svc.dwCurrentState = SERVICE_START_PENDING;
|
||||
svc.dwControlsAccepted = 0;
|
||||
svc.dwWin32ExitCode = NO_ERROR;
|
||||
svc.dwServiceSpecificExitCode = 0;
|
||||
svc.dwCheckPoint = 0;
|
||||
svc.dwWaitHint = 0;
|
||||
|
||||
if (!(svc_handle = RegisterServiceCtrlHandlerA(DT->lpServiceName, ServiceCtrlHandler))) {
|
||||
logg(LOGG_INFO, "[service] RegisterServiceCtrlHandler() failed with %d\n", GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!SetServiceStatus(svc_handle, &svc)) {
|
||||
logg(LOGG_INFO, "[service] SetServiceStatus() failed with %d\n", GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
SetEvent(evStart);
|
||||
WaitForSingleObject(DispatcherThread, INFINITE);
|
||||
cw_stop_ctrl_handler(CTRL_C_EVENT);
|
||||
}
|
||||
38
clamav/common/service.h
Normal file
38
clamav/common/service.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
* Copyright (C) 2008-2010 Gianluigi Tiesi <sherpya@netfarm.it>
|
||||
*
|
||||
* Authors: Gianluigi Tiesi
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __SERVICE_H
|
||||
#define __SERVICE_H
|
||||
|
||||
#include <platform.h>
|
||||
#include <winsvc.h>
|
||||
|
||||
int svc_uninstall(const char *name, int verbose);
|
||||
int svc_install(const char *name, const char *dname, const char *desc);
|
||||
static void svc_getcpvalue(const char *name);
|
||||
void svc_register(const char *name);
|
||||
void svc_ready(void);
|
||||
int svc_checkpoint(const char *type, const char *name, unsigned int custom, void *context);
|
||||
void WINAPI ServiceCtrlHandler(DWORD code);
|
||||
BOOL WINAPI cw_stop_ctrl_handler(DWORD CtrlType);
|
||||
void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv);
|
||||
|
||||
#endif
|
||||
120
clamav/common/tar.c
Normal file
120
clamav/common/tar.c
Normal 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
31
clamav/common/tar.h
Normal 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
|
||||
172
clamav/common/win/cert_util_win.c
Normal file
172
clamav/common/win/cert_util_win.c
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* OpenSSL certificate verification for Windows.
|
||||
*
|
||||
* Copyright (C) 2019-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* Authors: Micah Snyder
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <Windows.h>
|
||||
#include <wincrypt.h>
|
||||
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "output.h"
|
||||
|
||||
#include "cert_util.h"
|
||||
#include "cert_util_internal.h"
|
||||
|
||||
cl_error_t cert_store_load(X509 **trusted_certs, size_t trusted_cert_count)
|
||||
{
|
||||
uint32_t numCertificatesFound = 0;
|
||||
DWORD lastError;
|
||||
|
||||
HCERTSTORE hStore = NULL;
|
||||
PCCERT_CONTEXT pWinCertContext = NULL;
|
||||
X509 *x509 = NULL;
|
||||
|
||||
cl_error_t ret = CL_EOPEN;
|
||||
int pt_err;
|
||||
|
||||
cert_store_t *store = NULL;
|
||||
bool locked = false;
|
||||
|
||||
hStore = CertOpenSystemStoreA(NULL, "ROOT");
|
||||
if (NULL == hStore) {
|
||||
mprintf(LOGG_ERROR, "Failed to open system certificate store.\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
store = cert_store_get_int();
|
||||
if (!store) {
|
||||
mprintf(LOGG_ERROR, "Failed to retrieve cert store\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
pt_err = pthread_mutex_lock(&store->mutex);
|
||||
if (pt_err) {
|
||||
errno = pt_err;
|
||||
mprintf(LOGG_ERROR, "Mutex lock failed\n");
|
||||
}
|
||||
locked = true;
|
||||
|
||||
if (store->loaded) {
|
||||
mprintf(LOGG_INFO, "Cert store already loaded\n");
|
||||
ret = CL_SUCCESS;
|
||||
goto done;
|
||||
}
|
||||
|
||||
store->system_certs.count = 0;
|
||||
store->system_certs.certificates = NULL;
|
||||
|
||||
while (NULL != (pWinCertContext = CertEnumCertificatesInStore(hStore, pWinCertContext))) {
|
||||
int addCertResult = 0;
|
||||
const unsigned char *encoded_cert = pWinCertContext->pbCertEncoded;
|
||||
|
||||
x509 = NULL;
|
||||
x509 = d2i_X509(NULL, &encoded_cert, pWinCertContext->cbCertEncoded);
|
||||
if (NULL == x509) {
|
||||
mprintf(LOGG_ERROR, "Failed to convert system certificate to x509.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
store->system_certs.certificates = realloc(
|
||||
store->system_certs.certificates,
|
||||
(numCertificatesFound + 1) * sizeof(*store->system_certs.certificates));
|
||||
if (store->system_certs.certificates == NULL) {
|
||||
mprintf(LOGG_ERROR, "Failed to reserve memory for system cert list\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
store->system_certs.certificates[store->system_certs.count++] = x509;
|
||||
|
||||
if (mprintf_verbose) {
|
||||
char *issuer = NULL;
|
||||
size_t issuerLen = 0;
|
||||
issuerLen = CertGetNameStringA(pWinCertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, NULL, 0);
|
||||
|
||||
issuer = malloc(issuerLen);
|
||||
if (NULL == issuer) {
|
||||
mprintf(LOGG_ERROR, "Failed to allocate memory for certificate name.\n");
|
||||
ret = CURLE_OUT_OF_MEMORY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (0 == CertGetNameStringA(pWinCertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, issuer, issuerLen)) {
|
||||
mprintf(LOGG_ERROR, "Failed to get friendly display name for certificate.\n");
|
||||
} else {
|
||||
mprintf(LOGG_INFO, "Certificate loaded from Windows certificate store: %s\n", issuer);
|
||||
}
|
||||
|
||||
free(issuer);
|
||||
}
|
||||
|
||||
numCertificatesFound++;
|
||||
}
|
||||
|
||||
lastError = GetLastError();
|
||||
switch (lastError) {
|
||||
case E_INVALIDARG:
|
||||
mprintf(LOGG_ERROR, "The handle in the hCertStore parameter is not the same as that in the certificate context pointed to by pPrevCertContext.\n");
|
||||
break;
|
||||
case CRYPT_E_NOT_FOUND:
|
||||
case ERROR_NO_MORE_FILES:
|
||||
if (0 == numCertificatesFound) {
|
||||
mprintf(LOGG_ERROR, "No certificates were found.\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mprintf(LOGG_ERROR, "Unexpected error code from CertEnumCertificatesInStore()\n");
|
||||
}
|
||||
|
||||
if (trusted_certs && trusted_cert_count > 0) {
|
||||
if (cert_store_set_trusted_int(trusted_certs, trusted_cert_count) == 0) {
|
||||
mprintf(LOGG_DEBUG, "Trusted certificates loaded: %zu\n",
|
||||
store->trusted_certs.count);
|
||||
} else {
|
||||
mprintf(LOGG_WARNING, "Continuing without trusted certificates\n");
|
||||
/* proceed as if we succeeded using only certificates from the
|
||||
* system */
|
||||
}
|
||||
}
|
||||
|
||||
store->loaded = true;
|
||||
ret = CL_SUCCESS;
|
||||
|
||||
done:
|
||||
if (locked) {
|
||||
pt_err = pthread_mutex_unlock(&store->mutex);
|
||||
if (pt_err) {
|
||||
errno = pt_err;
|
||||
mprintf(LOGG_ERROR, "Mutex unlock failed\n");
|
||||
}
|
||||
locked = false;
|
||||
}
|
||||
|
||||
if (NULL != pWinCertContext) {
|
||||
CertFreeCertificateContext(pWinCertContext);
|
||||
}
|
||||
if (NULL != hStore) {
|
||||
CertCloseStore(hStore, 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user