libclamav/openioc.c
2abe7362
 /*
e1cbc270
  *  Copyright (C) 2014-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
2abe7362
  *
  *  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
b0e47a6d
 
2abe7362
 #include <stdio.h>
 #include <stdlib.h>
 #include <dirent.h>
 #include <errno.h>
 #include <string.h>
 
b0e47a6d
 #include "mpool.h"
 #include "readdb.h"
60d8d2c3
 #include "clamav.h"
53506979
 #include "others.h"
 #include "openioc.h"
 
 #ifdef HAVE_LIBXML2
2abe7362
 #include <libxml/xmlreader.h>
 
 struct openioc_hash {
288057e9
     unsigned char *hash;
     void *next;
2abe7362
 };
 
288057e9
 static const xmlChar *openioc_read(xmlTextReaderPtr reader)
2abe7362
 {
288057e9
     const xmlChar *name;
2abe7362
     if (xmlTextReaderRead(reader) != 1)
         return NULL;
     name = xmlTextReaderConstLocalName(reader);
     if (name != NULL) {
3cd30f98
         cli_dbgmsg("openioc_parse: xmlTextReaderRead read %s%s\n", name,
288057e9
                    xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT ? " end tag" : "");
2abe7362
     }
288057e9
     return name;
2abe7362
 }
 
7e302913
 static int openioc_is_context_hash(xmlTextReaderPtr reader)
 {
288057e9
     xmlChar *document = xmlTextReaderGetAttribute(reader, (const xmlChar *)"document");
     xmlChar *search   = xmlTextReaderGetAttribute(reader, (const xmlChar *)"search");
     int rc            = 0;
7e302913
 
     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;
 }
 
288057e9
 static int openioc_parse_content(xmlTextReaderPtr reader, struct openioc_hash **elems, int context_hash)
2abe7362
 {
288057e9
     const xmlChar *xmlval;
     struct openioc_hash *elem;
2abe7362
     int rc = CL_SUCCESS;
 
7e302913
     if (context_hash == 0) {
288057e9
         xmlChar *type = xmlTextReaderGetAttribute(reader, (const xmlChar *)"type");
7e302913
         if (type == NULL) {
3cd30f98
             cli_dbgmsg("openioc_parse: xmlTextReaderGetAttribute no type attribute "
7e302913
                        "for <Content> element\n");
2abe7362
             return rc;
288057e9
         } else {
7e302913
             if (xmlStrcasecmp(type, (const xmlChar *)"sha1") &&
                 xmlStrcasecmp(type, (const xmlChar *)"sha256") &&
                 xmlStrcasecmp(type, (const xmlChar *)"md5")) {
                 xmlFree(type);
                 return rc;
             }
2abe7362
         }
7e302913
         xmlFree(type);
2abe7362
     }
288057e9
 
2abe7362
     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) {
3cd30f98
                 cli_dbgmsg("openioc_parse: calloc fails for openioc_hash.\n");
2abe7362
                 return CL_EMEM;
             }
             elem->hash = xmlStrdup(xmlval);
             elem->next = *elems;
288057e9
             *elems     = elem;
2abe7362
         } else {
288057e9
             cli_dbgmsg("openioc_parse: xmlTextReaderConstValue() returns NULL for Content md5 value.\n");
2abe7362
         }
288057e9
     } else {
3cd30f98
         cli_dbgmsg("openioc_parse: No text for XML Content element.\n");
2abe7362
     }
     return rc;
 }
 
288057e9
 static int openioc_parse_indicatoritem(xmlTextReaderPtr reader, struct openioc_hash **elems)
2abe7362
 {
288057e9
     const xmlChar *name;
     int rc           = CL_SUCCESS;
7e302913
     int context_hash = 0;
2abe7362
 
     while (1) {
         name = openioc_read(reader);
         if (name == NULL)
             break;
288057e9
         if (xmlStrEqual(name, (const xmlChar *)"Context") &&
7e302913
             xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
             context_hash = openioc_is_context_hash(reader);
288057e9
         } else if (xmlStrEqual(name, (const xmlChar *)"Content") &&
                    xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
7e302913
             rc = openioc_parse_content(reader, elems, context_hash);
2abe7362
             if (rc != CL_SUCCESS) {
                 break;
             }
         } else if (xmlStrEqual(name, (const xmlChar *)"IndicatorItem") &&
                    xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT) {
             break;
         }
     }
     return rc;
 }
 
288057e9
 static int openioc_parse_indicator(xmlTextReaderPtr reader, struct openioc_hash **elems)
2abe7362
 {
288057e9
     const xmlChar *name;
2abe7362
     int rc = CL_SUCCESS;
 
     while (1) {
         name = openioc_read(reader);
         if (name == NULL)
             return rc;
288057e9
         if (xmlStrEqual(name, (const xmlChar *)"Indicator") &&
2abe7362
             xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
             rc = openioc_parse_indicator(reader, elems);
             if (rc != CL_SUCCESS) {
3cd30f98
                 cli_dbgmsg("openioc_parse: openioc_parse_indicator recursion error.\n");
2abe7362
                 break;
             }
288057e9
         } else if (xmlStrEqual(name, (const xmlChar *)"IndicatorItem") &&
                    xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
2abe7362
             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;
 }
 
288057e9
 int openioc_parse(const char *fname, int fd, struct cl_engine *engine, unsigned int options)
2abe7362
 {
     int rc;
     xmlTextReaderPtr reader = NULL;
288057e9
     const xmlChar *name;
     struct openioc_hash *elems = NULL, *elem = NULL;
     const char *iocp = NULL;
2abe7362
     uint16_t ioclen;
288057e9
     char *virusname;
3cd30f98
     int hash_count = 0;
288057e9
 
2abe7362
     if (fname == NULL)
         return CL_ENULLARG;
 
     if (fd < 0)
         return CL_EARG;
 
3cd30f98
     cli_dbgmsg("openioc_parse: XML parsing file %s\n", fname);
2abe7362
 
d3e752ad
     reader = xmlReaderForFd(fd, NULL, NULL, CLAMAV_MIN_XMLREADER_FLAGS);
2abe7362
     if (reader == NULL) {
3cd30f98
         cli_dbgmsg("openioc_parse: xmlReaderForFd error\n");
2abe7362
         return CL_EOPEN;
     }
     rc = xmlTextReaderRead(reader);
     while (rc == 1) {
         name = xmlTextReaderConstLocalName(reader);
3cd30f98
         cli_dbgmsg("openioc_parse: xmlTextReaderRead read %s\n", name);
288057e9
         if (xmlStrEqual(name, (const xmlChar *)"Indicator") &&
2abe7362
             xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
             rc = openioc_parse_indicator(reader, &elems);
             if (rc != CL_SUCCESS) {
691e9545
                 xmlTextReaderClose(reader);
                 xmlFreeTextReader(reader);
2abe7362
                 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++;
 
8efbf4a0
     ioclen = (uint16_t)strlen(fname);
2abe7362
 
     if (elems != NULL) {
         if (NULL == engine->hm_hdb) {
544fa973
             engine->hm_hdb = MPOOL_CALLOC(engine->mempool, 1, sizeof(struct cli_matcher));
288057e9
             if (NULL == engine->hm_hdb) {
691e9545
                 xmlTextReaderClose(reader);
                 xmlFreeTextReader(reader);
2abe7362
                 return CL_EMEM;
691e9545
             }
2abe7362
 #ifdef USE_MPOOL
             engine->hm_hdb->mempool = engine->mempool;
 #endif
         }
     }
 
     while (elems != NULL) {
288057e9
         const char *sp;
         char *hash, *vp;
2abe7362
         int i, hashlen;
 
288057e9
         elem  = elems;
2abe7362
         elems = elems->next;
288057e9
         hash  = (char *)(elem->hash);
2abe7362
         while (isspace(*hash))
             hash++;
         hashlen = strlen(hash);
         if (hashlen == 0) {
             xmlFree(elem->hash);
             free(elem);
             continue;
         }
288057e9
         vp = hash + hashlen - 1;
53506979
         while (isspace(*vp) && vp > hash) {
             *vp-- = '\0';
2abe7362
             hashlen--;
         }
288057e9
         virusname = calloc(1, ioclen + hashlen + 2);
762c9ae3
         if (NULL == virusname) {
544fa973
             cli_dbgmsg("openioc_parse: calloc for virname memory failed.\n");
691e9545
             xmlTextReaderClose(reader);
             xmlFreeTextReader(reader);
2abe7362
             return CL_EMEM;
         }
53506979
         sp = fname;
ad8ddf98
         vp = virusname;
288057e9
         for (i = 0; i < ioclen; i++, sp++, vp++) {
2abe7362
             switch (*sp) {
288057e9
                 case '\\':
                 case '/':
                 case '?':
                 case '%':
                 case '*':
                 case ':':
                 case '|':
                 case '"':
                 case '<':
                 case '>':
2abe7362
                     *vp = '_';
288057e9
                     break;
                 default:
                     if (isspace(*sp))
                         *vp = '_';
                     else
                         *vp = *sp;
2abe7362
             }
         }
         *vp++ = '.';
288057e9
         sp    = hash;
         for (i = 0; i < hashlen; i++, sp++) {
2abe7362
             if (isxdigit(*sp)) {
                 *vp++ = *sp;
             }
         }
ad8ddf98
 
288057e9
         vp        = virusname;
544fa973
         virusname = CLI_MPOOL_VIRNAME(engine->mempool, virusname, options & CL_DB_OFFICIAL);
ad8ddf98
         if (!(virusname)) {
544fa973
             cli_dbgmsg("openioc_parse: MPOOL_MALLOC for virname memory failed.\n");
ad8ddf98
             xmlTextReaderClose(reader);
             xmlFreeTextReader(reader);
             free(vp);
             return CL_EMEM;
         }
 
         free(vp);
 
2abe7362
         rc = hm_addhash_str(engine->hm_hdb, hash, 0, virusname);
7e302913
         if (rc != CL_SUCCESS)
3cd30f98
             cli_dbgmsg("openioc_parse: hm_addhash_str failed with %i hash len %i for %s.\n",
2abe7362
                        rc, hashlen, virusname);
3cd30f98
         else
             hash_count++;
 
2abe7362
         xmlFree(elem->hash);
         free(elem);
     }
7e302913
 
3cd30f98
     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",
288057e9
                    hash_count, hash_count == 1 ? "" : "s", fname);
3cd30f98
 
7f92f975
     xmlTextReaderClose(reader);
     xmlFreeTextReader(reader);
 
53506979
     return CL_SUCCESS;
2abe7362
 }
53506979
 #else
288057e9
 int openioc_parse(const char *fname, int fd, struct cl_engine *engine, unsigned int options)
53506979
 {
3cd30f98
     cli_dbgmsg("openioc_parse: libxml2 support is compiled out and is needed for OpenIOC support.\n");
53506979
     return CL_SUCCESS;
 }
 #endif