static int ooxml_content_cb(int fd, cli_ctx *ctx) { int ret = CL_SUCCESS, tmp, toval = 0, state; int core=0, extn=0, cust=0, dsig=0; int mcore=0, mextn=0, mcust=0; const xmlChar *name, *value, *CT, *PN; xmlTextReaderPtr reader = NULL; uint32_t loff; unsigned long sav_scansize = ctx->scansize; unsigned int sav_scannedfiles = ctx->scannedfiles; cli_dbgmsg("in ooxml_content_cb\n"); /* perform engine limit checks in temporary tracking session */ ret = ooxml_updatelimits(fd, ctx); if (ret != CL_CLEAN) return ret; /* apply a reader to the document */ reader = xmlReaderForFd(fd, "[Content_Types].xml", NULL, CLAMAV_MIN_XMLREADER_FLAGS); if (reader == NULL) { cli_dbgmsg("ooxml_content_cb: xmlReaderForFd error for ""[Content_Types].xml""\n"); cli_json_parse_error(ctx->wrkproperty, "OOXML_ERROR_XML_READER_FD"); ctx->scansize = sav_scansize; ctx->scannedfiles = sav_scannedfiles; return CL_SUCCESS; // libxml2 failed! } /* locate core-properties, extended-properties, and custom-properties (optional) */ while ((state = xmlTextReaderRead(reader)) == 1) { if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS) { ret = CL_ETIMEOUT; goto ooxml_content_exit; } name = xmlTextReaderConstLocalName(reader); if (name == NULL) continue; if (strcmp((const char *)name, "Override")) continue; if (xmlTextReaderHasAttributes(reader) != 1) continue; CT = PN = NULL; while (xmlTextReaderMoveToNextAttribute(reader) == 1) { name = xmlTextReaderConstLocalName(reader); value = xmlTextReaderConstValue(reader); if (name == NULL || value == NULL) continue; if (!xmlStrcmp(name, (const xmlChar *)"ContentType")) { CT = value; } else if (!xmlStrcmp(name, (const xmlChar *)"PartName")) { PN = value; } cli_dbgmsg("%s: %s\n", name, value); } if (!CT && !PN) continue; if (!xmlStrcmp(CT, (const xmlChar *)"application/vnd.openxmlformats-package.core-properties+xml")) { /* default: /docProps/core.xml*/ tmp = unzip_search_single(ctx, (const char *)(PN+1), xmlStrlen(PN)-1, &loff); if (tmp == CL_ETIMEOUT) { ret = tmp; } else if (tmp != CL_VIRUS) { cli_dbgmsg("cli_process_ooxml: failed to find core properties file \"%s\"!\n", PN); mcore++; } else { cli_dbgmsg("ooxml_content_cb: found core properties file \"%s\" @ %x\n", PN, loff); if (!core) { tmp = unzip_single_internal(ctx, loff, ooxml_core_cb); if (tmp == CL_ETIMEOUT || tmp == CL_EMEM) { ret = tmp; } } core++; } } else if (!xmlStrcmp(CT, (const xmlChar *)"application/vnd.openxmlformats-officedocument.extended-properties+xml")) { /* default: /docProps/app.xml */ tmp = unzip_search_single(ctx, (const char *)(PN+1), xmlStrlen(PN)-1, &loff); if (tmp == CL_ETIMEOUT) { ret = tmp; } else if (tmp != CL_VIRUS) { cli_dbgmsg("cli_process_ooxml: failed to find extended properties file \"%s\"!\n", PN); mextn++; } else { cli_dbgmsg("ooxml_content_cb: found extended properties file \"%s\" @ %x\n", PN, loff); if (!extn) { tmp = unzip_single_internal(ctx, loff, ooxml_extn_cb); if (tmp == CL_ETIMEOUT || tmp == CL_EMEM) { ret = tmp; } } extn++; } } else if (!xmlStrcmp(CT, (const xmlChar *)"application/vnd.openxmlformats-officedocument.custom-properties+xml")) { /* default: /docProps/custom.xml */ tmp = unzip_search_single(ctx, (const char *)(PN+1), xmlStrlen(PN)-1, &loff); if (tmp == CL_ETIMEOUT) { ret = tmp; } else if (tmp != CL_VIRUS) { cli_dbgmsg("cli_process_ooxml: failed to find custom properties file \"%s\"!\n", PN); mcust++; } else { cli_dbgmsg("ooxml_content_cb: found custom properties file \"%s\" @ %x\n", PN, loff); /* custom properties are not parsed */ cust++; } } else if (!xmlStrcmp(CT, (const xmlChar *)"application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml")) { dsig++; } if (ret != CL_SUCCESS) goto ooxml_content_exit; } ooxml_content_exit: if (core) { cli_jsonint(ctx->wrkproperty, "CorePropertiesFileCount", core); if (core > 1) cli_json_parse_error(ctx->wrkproperty, "OOXML_ERROR_MULTIPLE_CORE_PROPFILES"); } else if (!mcore) cli_dbgmsg("cli_process_ooxml: file does not contain core properties file\n"); if (mcore) { cli_jsonint(ctx->wrkproperty, "CorePropertiesMissingFileCount", mcore); cli_json_parse_error(ctx->wrkproperty, "OOXML_ERROR_MISSING_CORE_PROPFILES"); } if (extn) { cli_jsonint(ctx->wrkproperty, "ExtendedPropertiesFileCount", extn); if (extn > 1) cli_json_parse_error(ctx->wrkproperty, "OOXML_ERROR_MULTIPLE_EXTN_PROPFILES"); } else if (!mextn) cli_dbgmsg("cli_process_ooxml: file does not contain extended properties file\n"); if (mextn) { cli_jsonint(ctx->wrkproperty, "ExtendedPropertiesMissingFileCount", mextn); cli_json_parse_error(ctx->wrkproperty, "OOXML_ERROR_MISSING_EXTN_PROPFILES"); } if (cust) { cli_jsonint(ctx->wrkproperty, "CustomPropertiesFileCount", cust); if (cust > 1) cli_json_parse_error(ctx->wrkproperty, "OOXML_ERROR_MULTIPLE_CUSTOM_PROPFILES"); } else if (!mcust) cli_dbgmsg("cli_process_ooxml: file does not contain custom properties file\n"); if (mcust) { cli_jsonint(ctx->wrkproperty, "CustomPropertiesMissingFileCount", mcust); cli_json_parse_error(ctx->wrkproperty, "OOXML_ERROR_MISSING_CUST_PROPFILES"); } if (dsig) { cli_jsonint(ctx->wrkproperty, "DigitalSignaturesCount", dsig); } /* restore the engine tracking limits; resets session limit tracking */ ctx->scansize = sav_scansize; ctx->scannedfiles = sav_scannedfiles; xmlTextReaderClose(reader); xmlFreeTextReader(reader); return ret; }
static int ooxml_basic_json(int fd, cli_ctx *ctx, const char *key) { int ret = CL_SUCCESS; const xmlChar *stack[OOXML_JSON_RECLEVEL]; json_object *summary, *wrkptr; int type, rlvl = 0; int32_t val2; const xmlChar *name, *value; xmlTextReaderPtr reader = NULL; cli_dbgmsg("in ooxml_basic_json\n"); reader = xmlReaderForFd(fd, "properties.xml", NULL, 0); if (reader == NULL) { cli_dbgmsg("ooxml_basic_json: xmlReaderForFd error for %s\n", key); return CL_SUCCESS; // libxml2 failed! } summary = json_object_new_object(); if (NULL == summary) { cli_errmsg("ooxml_basic_json: no memory for json object.\n"); ret = CL_EFORMAT; goto ooxml_basic_exit; } while (xmlTextReaderRead(reader) == 1) { name = xmlTextReaderConstLocalName(reader); value = xmlTextReaderConstValue(reader); type = xmlTextReaderNodeType(reader); cli_dbgmsg("%s [%i]: %s\n", name, type, value); switch (type) { case XML_READER_TYPE_ELEMENT: stack[rlvl] = name; rlvl++; break; case XML_READER_TYPE_TEXT: { wrkptr = summary; if (rlvl > 2) { /* 0 is root xml object */ int i; for (i = 1; i < rlvl-1; ++i) { json_object *newptr = json_object_object_get(wrkptr, stack[i]); if (!newptr) { newptr = json_object_new_object(); if (NULL == newptr) { cli_errmsg("ooxml_basic_json: no memory for json object.\n"); ret = CL_EMEM; goto ooxml_basic_exit; } json_object_object_add(wrkptr, stack[i], newptr); } else { /* object already exists */ if (!json_object_is_type(newptr, json_type_object)) { cli_warnmsg("ooxml_content_cb: json object already exists as not an object\n"); ret = CL_EFORMAT; goto ooxml_basic_exit; } } wrkptr = newptr; cli_dbgmsg("stack %d: %s\n", i, stack[i]); } } if (ooxml_is_int(value, xmlStrlen(value), &val2)) { ret = cli_jsonint(wrkptr, stack[rlvl-1], val2); } else if (!xmlStrcmp(value, "true")) { ret = cli_jsonbool(wrkptr, stack[rlvl-1], 1); } else if (!xmlStrcmp(value, "false")) { ret = cli_jsonbool(wrkptr, stack[rlvl-1], 0); } else { ret = cli_jsonstr(wrkptr, stack[rlvl-1], value); } if (ret != CL_SUCCESS) goto ooxml_basic_exit; } break; case XML_READER_TYPE_END_ELEMENT: rlvl--; break; default: cli_dbgmsg("ooxml_content_cb: unhandled xml node %s [%i]: %s\n", name, type, value); ret = CL_EFORMAT; goto ooxml_basic_exit; } } json_object_object_add(ctx->wrkproperty, key, summary); if (rlvl != 0) { cli_warnmsg("ooxml_basic_json: office property file has unbalanced tags\n"); /* FAIL */ } ooxml_basic_exit: xmlTextReaderClose(reader); xmlFreeTextReader(reader); return ret; }
int cli_jsonint_array(json_object *obj, int32_t val) { return cli_jsonint(obj, NULL, val); }
static int ooxml_parse_element(xmlTextReaderPtr reader, json_object *wrkptr, int rlvl, int skip) { const char *element_tag = NULL, *end_tag = NULL; const xmlChar *node_name = NULL, *node_value = NULL; json_object *njptr; int node_type, ret = CL_SUCCESS; int32_t val2; cli_dbgmsg("in ooxml_parse_element @ layer %d\n", rlvl); /* check recursion level */ if (rlvl >= OOXML_JSON_RECLEVEL_MAX) { return CL_EMAXREC; } if (wrkptr == NULL) { skip = 1; } /* acquire element type */ node_type = xmlTextReaderNodeType(reader); if (node_type != XML_READER_TYPE_ELEMENT) { cli_dbgmsg("ooxml_parse_element: first node typed %d, not %d\n", node_type, XML_READER_TYPE_ELEMENT); return CL_EPARSE; /* first type is not an element */ } /* acquire element tag */ node_name = xmlTextReaderConstLocalName(reader); if (!node_name) { cli_dbgmsg("ooxml_parse_element: element tag node nameless\n"); return CL_EPARSE; /* no name, nameless */ } element_tag = ooxml_check_key(node_name, xmlStrlen(node_name)); if (!element_tag) { cli_dbgmsg("ooxml_parse_element: invalid element tag [%s]\n", node_name); skip = 1; /* skipping element */ //return CL_EFORMAT; /* REMOVE */ } /* handle attributes if you want */ /* loop across all element contents */ while (xmlTextReaderRead(reader) == 1) { node_type = xmlTextReaderNodeType(reader); switch (node_type) { case XML_READER_TYPE_ELEMENT: if (!skip) { njptr = json_object_object_get(wrkptr, element_tag); if (!njptr) { njptr = json_object_new_object(); if (NULL == njptr) { cli_errmsg("ooxml_basic_json: no memory for json object.\n"); return CL_EMEM; } cli_dbgmsg("ooxml_basic_json: added json object [%s]\n", element_tag); json_object_object_add(wrkptr, element_tag, njptr); } else { if (!json_object_is_type(njptr, json_type_object)) { cli_warnmsg("ooxml_content_cb: json object [%s] already exists as not an object\n", element_tag); return CL_EFORMAT; } } } else { njptr = NULL; } ret = ooxml_parse_element(reader, njptr, rlvl+1, skip); if (ret != CL_SUCCESS) { return ret; } break; case XML_READER_TYPE_END_ELEMENT: cli_dbgmsg("in ooxml_parse_element @ layer %d closed\n", rlvl); node_name = xmlTextReaderConstLocalName(reader); if (!node_name) { cli_dbgmsg("ooxml_parse_element: element end tag node nameless\n"); return CL_EPARSE; /* no name, nameless */ } if (!skip) { end_tag = ooxml_check_key(node_name, xmlStrlen(node_name)); if (!end_tag) { cli_dbgmsg("ooxml_parse_element: invalid element end tag [%s]\n", node_name); return CL_EFORMAT; /* unrecognized element tag */ } if (strncmp(element_tag, end_tag, strlen(element_tag))) { cli_dbgmsg("ooxml_parse_element: element tag does not match end tag\n"); return CL_EFORMAT; } } return CL_SUCCESS; case XML_READER_TYPE_TEXT: if (!skip) { node_value = xmlTextReaderConstValue(reader); njptr = json_object_object_get(wrkptr, element_tag); if (njptr) { cli_warnmsg("ooxml_parse_element: json object [%s] already exists\n", element_tag); } if (ooxml_is_int(node_value, xmlStrlen(node_value), &val2)) { ret = cli_jsonint(wrkptr, element_tag, val2); } else if (!xmlStrcmp(node_value, "true")) { ret = cli_jsonbool(wrkptr, element_tag, 1); } else if (!xmlStrcmp(node_value, "false")) { ret = cli_jsonbool(wrkptr, element_tag, 0); } else { ret = cli_jsonstr(wrkptr, element_tag, node_value); } if (ret != CL_SUCCESS) return ret; cli_dbgmsg("ooxml_basic_json: added json value [%s: %s]\n", element_tag, node_value); } else { node_name = xmlTextReaderConstLocalName(reader); node_value = xmlTextReaderConstValue(reader); cli_dbgmsg("ooxml_parse_element: not adding xml node %s [%d]: %s\n", node_name, node_type, node_value); } break; default: node_name = xmlTextReaderConstLocalName(reader); node_value = xmlTextReaderConstValue(reader); cli_dbgmsg("ooxml_parse_element: unhandled xml node %s [%d]: %s\n", node_name, node_type, node_value); return CL_EPARSE; } } return CL_SUCCESS; }