/* * Copyright (C) 2014-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved. * * Authors: Steven Morgan * * 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 #include #include #include #include #include "mpool.h" #include "readdb.h" #include "clamav.h" #include "others.h" #include "openioc.h" #ifdef HAVE_LIBXML2 #include 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 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