denyhosts/clamav/libclamav/openioc.c

344 lines
11 KiB
C

/*
* Copyright (C) 2014-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*
* Authors: Steven Morgan <smorgan@sourcefire.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include "mpool.h"
#include "readdb.h"
#include "clamav.h"
#include "others.h"
#include "openioc.h"
#ifdef HAVE_LIBXML2
#include <libxml/xmlreader.h>
struct openioc_hash {
unsigned char *hash;
void *next;
};
static const xmlChar *openioc_read(xmlTextReaderPtr reader)
{
const xmlChar *name;
if (xmlTextReaderRead(reader) != 1)
return NULL;
name = xmlTextReaderConstLocalName(reader);
if (name != NULL) {
cli_dbgmsg("openioc_parse: xmlTextReaderRead read %s%s\n", name,
xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT ? " end tag" : "");
}
return name;
}
static int openioc_is_context_hash(xmlTextReaderPtr reader)
{
xmlChar *document = xmlTextReaderGetAttribute(reader, (const xmlChar *)"document");
xmlChar *search = xmlTextReaderGetAttribute(reader, (const xmlChar *)"search");
int rc = 0;
if ((document != NULL && search != NULL) &&
!xmlStrcmp(document, (const xmlChar *)"FileItem") &&
(!xmlStrcmp(search, (const xmlChar *)"FileItem/Md5sum") ||
!xmlStrcmp(search, (const xmlChar *)"FileItem/Sha1sum") ||
!xmlStrcmp(search, (const xmlChar *)"FileItem/Sha256sum")))
rc = 1;
if (document != NULL)
xmlFree(document);
if (search != NULL)
xmlFree(search);
return rc;
}
static int openioc_parse_content(xmlTextReaderPtr reader, struct openioc_hash **elems, int context_hash)
{
const xmlChar *xmlval;
struct openioc_hash *elem;
int rc = CL_SUCCESS;
if (context_hash == 0) {
xmlChar *type = xmlTextReaderGetAttribute(reader, (const xmlChar *)"type");
if (type == NULL) {
cli_dbgmsg("openioc_parse: xmlTextReaderGetAttribute no type attribute "
"for <Content> element\n");
return rc;
} else {
if (xmlStrcasecmp(type, (const xmlChar *)"sha1") &&
xmlStrcasecmp(type, (const xmlChar *)"sha256") &&
xmlStrcasecmp(type, (const xmlChar *)"md5")) {
xmlFree(type);
return rc;
}
}
xmlFree(type);
}
if (xmlTextReaderRead(reader) == 1 && xmlTextReaderNodeType(reader) == XML_READER_TYPE_TEXT) {
xmlval = xmlTextReaderConstValue(reader);
if (xmlval) {
elem = cli_calloc(1, sizeof(struct openioc_hash));
if (NULL == elem) {
cli_dbgmsg("openioc_parse: calloc fails for openioc_hash.\n");
return CL_EMEM;
}
elem->hash = xmlStrdup(xmlval);
elem->next = *elems;
*elems = elem;
} else {
cli_dbgmsg("openioc_parse: xmlTextReaderConstValue() returns NULL for Content md5 value.\n");
}
} else {
cli_dbgmsg("openioc_parse: No text for XML Content element.\n");
}
return rc;
}
static int openioc_parse_indicatoritem(xmlTextReaderPtr reader, struct openioc_hash **elems)
{
const xmlChar *name;
int rc = CL_SUCCESS;
int context_hash = 0;
while (1) {
name = openioc_read(reader);
if (name == NULL)
break;
if (xmlStrEqual(name, (const xmlChar *)"Context") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
context_hash = openioc_is_context_hash(reader);
} else if (xmlStrEqual(name, (const xmlChar *)"Content") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
rc = openioc_parse_content(reader, elems, context_hash);
if (rc != CL_SUCCESS) {
break;
}
} else if (xmlStrEqual(name, (const xmlChar *)"IndicatorItem") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT) {
break;
}
}
return rc;
}
static int openioc_parse_indicator(xmlTextReaderPtr reader, struct openioc_hash **elems)
{
const xmlChar *name;
int rc = CL_SUCCESS;
while (1) {
name = openioc_read(reader);
if (name == NULL)
return rc;
if (xmlStrEqual(name, (const xmlChar *)"Indicator") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
rc = openioc_parse_indicator(reader, elems);
if (rc != CL_SUCCESS) {
cli_dbgmsg("openioc_parse: openioc_parse_indicator recursion error.\n");
break;
}
} else if (xmlStrEqual(name, (const xmlChar *)"IndicatorItem") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
rc = openioc_parse_indicatoritem(reader, elems);
if (rc != CL_SUCCESS) {
break;
}
} else if (xmlStrEqual(name, (const xmlChar *)"Indicator") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT) {
break;
}
}
return rc;
}
int openioc_parse(const char *fname, int fd, struct cl_engine *engine, unsigned int options)
{
int rc;
xmlTextReaderPtr reader = NULL;
const xmlChar *name;
struct openioc_hash *elems = NULL, *elem = NULL;
const char *iocp = NULL;
uint16_t ioclen;
char *virusname;
int hash_count = 0;
if (fname == NULL)
return CL_ENULLARG;
if (fd < 0)
return CL_EARG;
cli_dbgmsg("openioc_parse: XML parsing file %s\n", fname);
reader = xmlReaderForFd(fd, NULL, NULL, CLAMAV_MIN_XMLREADER_FLAGS);
if (reader == NULL) {
cli_dbgmsg("openioc_parse: xmlReaderForFd error\n");
return CL_EOPEN;
}
rc = xmlTextReaderRead(reader);
while (rc == 1) {
name = xmlTextReaderConstLocalName(reader);
cli_dbgmsg("openioc_parse: xmlTextReaderRead read %s\n", name);
if (xmlStrEqual(name, (const xmlChar *)"Indicator") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
rc = openioc_parse_indicator(reader, &elems);
if (rc != CL_SUCCESS) {
xmlTextReaderClose(reader);
xmlFreeTextReader(reader);
return rc;
}
}
if (xmlStrEqual(name, (const xmlChar *)"ioc") &&
xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT) {
break;
}
rc = xmlTextReaderRead(reader);
}
iocp = strrchr(fname, *PATHSEP);
if (NULL == iocp)
iocp = fname;
else
iocp++;
ioclen = (uint16_t)strlen(fname);
if (elems != NULL) {
if (NULL == engine->hm_hdb) {
engine->hm_hdb = MPOOL_CALLOC(engine->mempool, 1, sizeof(struct cli_matcher));
if (NULL == engine->hm_hdb) {
xmlTextReaderClose(reader);
xmlFreeTextReader(reader);
return CL_EMEM;
}
#ifdef USE_MPOOL
engine->hm_hdb->mempool = engine->mempool;
#endif
}
}
while (elems != NULL) {
const char *sp;
char *hash, *vp;
int i, hashlen;
elem = elems;
elems = elems->next;
hash = (char *)(elem->hash);
while (isspace(*hash))
hash++;
hashlen = strlen(hash);
if (hashlen == 0) {
xmlFree(elem->hash);
free(elem);
continue;
}
vp = hash + hashlen - 1;
while (isspace(*vp) && vp > hash) {
*vp-- = '\0';
hashlen--;
}
virusname = calloc(1, ioclen + hashlen + 2);
if (NULL == virusname) {
cli_dbgmsg("openioc_parse: calloc for virname memory failed.\n");
xmlTextReaderClose(reader);
xmlFreeTextReader(reader);
return CL_EMEM;
}
sp = fname;
vp = virusname;
for (i = 0; i < ioclen; i++, sp++, vp++) {
switch (*sp) {
case '\\':
case '/':
case '?':
case '%':
case '*':
case ':':
case '|':
case '"':
case '<':
case '>':
*vp = '_';
break;
default:
if (isspace(*sp))
*vp = '_';
else
*vp = *sp;
}
}
*vp++ = '.';
sp = hash;
for (i = 0; i < hashlen; i++, sp++) {
if (isxdigit(*sp)) {
*vp++ = *sp;
}
}
vp = virusname;
virusname = CLI_MPOOL_VIRNAME(engine->mempool, virusname, options & CL_DB_OFFICIAL);
if (!(virusname)) {
cli_dbgmsg("openioc_parse: MPOOL_MALLOC for virname memory failed.\n");
xmlTextReaderClose(reader);
xmlFreeTextReader(reader);
free(vp);
return CL_EMEM;
}
free(vp);
rc = hm_addhash_str(engine->hm_hdb, hash, 0, virusname);
if (rc != CL_SUCCESS)
cli_dbgmsg("openioc_parse: hm_addhash_str failed with %i hash len %i for %s.\n",
rc, hashlen, virusname);
else
hash_count++;
xmlFree(elem->hash);
free(elem);
}
if (hash_count == 0)
cli_warnmsg("openioc_parse: No hash signatures extracted from %s.\n", fname);
else
cli_dbgmsg("openioc_parse: %i hash signature%s extracted from %s.\n",
hash_count, hash_count == 1 ? "" : "s", fname);
xmlTextReaderClose(reader);
xmlFreeTextReader(reader);
return CL_SUCCESS;
}
#else
int openioc_parse(const char *fname, int fd, struct cl_engine *engine, unsigned int options)
{
cli_dbgmsg("openioc_parse: libxml2 support is compiled out and is needed for OpenIOC support.\n");
return CL_SUCCESS;
}
#endif