static int dmg_extract_xml(cli_ctx *ctx, char *dir, struct dmg_koly_block *hdr) { char * xmlfile; const char *outdata; size_t namelen, nread; int ofd; /* Prep TOC XML for output */ outdata = fmap_need_off_once_len(*ctx->fmap, hdr->xmlOffset, hdr->xmlLength, &nread); if (!outdata || (nread != hdr->xmlLength)) { cli_errmsg("cli_scandmg: Failed getting XML from map, len " STDu64 "\n", hdr->xmlLength); return CL_EMAP; } namelen = strlen(dir) + 1 + 7 + 1; if (!(xmlfile = cli_malloc(namelen))) { return CL_EMEM; } snprintf(xmlfile, namelen, "%s"PATHSEP"toc.xml", dir); cli_dbgmsg("cli_scandmg: Extracting XML as %s\n", xmlfile); /* Write out TOC XML */ if ((ofd = open(xmlfile, O_CREAT|O_RDWR|O_EXCL|O_TRUNC|O_BINARY, S_IRWXU)) < 0) { char err[128]; cli_errmsg("cli_scandmg: Can't create temporary file %s: %s\n", xmlfile, cli_strerror(errno, err, sizeof(err))); free(xmlfile); return CL_ETMPFILE; } if (cli_writen(ofd, outdata, hdr->xmlLength) != hdr->xmlLength) { cli_errmsg("cli_scandmg: Not all bytes written!\n"); close(ofd); free(xmlfile); return CL_EWRITE; } close(ofd); free(xmlfile); return CL_SUCCESS; }
int fmap_dump_to_file(fmap_t *map, const char *tmpdir, char **outname, int *outfd) { char *tmpname; int tmpfd, ret; size_t pos = 0, len; cli_dbgmsg("fmap_dump_to_file: dumping fmap not backed by file...\n"); ret = cli_gentempfd(tmpdir, &tmpname, &tmpfd); if(ret != CL_SUCCESS) { cli_dbgmsg("fmap_dump_to_file: failed to generate temporary file.\n"); return ret; } do { const char *b; len = 0; b = fmap_need_off_once_len(map, pos, BUFSIZ, &len); pos += len; if(b && (len > 0)) { if ((size_t)cli_writen(tmpfd, b, len) != len) { cli_warnmsg("fmap_dump_to_file: write failed to %s!\n", tmpname); close(tmpfd); unlink(tmpname); free(tmpname); return CL_EWRITE; } } } while (len > 0); if(lseek(tmpfd, 0, SEEK_SET) == -1) { cli_dbgmsg("fmap_dump_to_file: lseek failed\n"); } *outname = tmpname; *outfd = tmpfd; return CL_SUCCESS; }
cl_error_t fmap_dump_to_file(fmap_t* map, const char* filepath, const char* tmpdir, char** outname, int* outfd, size_t start_offset, size_t end_offset) { cl_error_t ret = CL_EARG; char* filebase = NULL; char* prefix = NULL; char* tmpname = NULL; int tmpfd = -1; size_t pos = 0, len = 0, bytes_remaining = 0, write_size = 0; if ((start_offset > map->real_len) || (end_offset < start_offset)) { cli_dbgmsg("fmap_dump_to_file: Invalid offset arguments: start %zu, end %zu\n", start_offset, end_offset); return ret; } pos = start_offset; end_offset = MIN(end_offset, map->real_len); bytes_remaining = end_offset - start_offset; /* Create a filename prefix that includes the original filename, if available */ if (filepath != NULL) { if (CL_SUCCESS != cli_basename(filepath, strlen(filepath), &filebase)) { cli_dbgmsg("fmap_dump_to_file: Unable to determine basename from filepath.\n"); } else if ((start_offset != 0) && (end_offset != map->real_len)) { /* If we're only dumping a portion of the file, inlcude the offsets in the prefix,... * e.g. tmp filename will become something like: filebase.500-1200.<randhex> */ uint32_t prefix_len = strlen(filebase) + 1 + SIZE_T_CHARLEN + 1 + SIZE_T_CHARLEN + 1; prefix = malloc(prefix_len); if (NULL == prefix) { cli_errmsg("fmap_dump_to_file: Failed to allocate memory for tempfile prefix.\n"); if (NULL != filebase) free(filebase); return CL_EMEM; } snprintf(prefix, prefix_len, "%s.%zu-%zu", filebase, start_offset, end_offset); free(filebase); filebase = NULL; } else { /* Else if we're dumping the whole thing, use the filebase as the prefix */ prefix = filebase; filebase = NULL; } } cli_dbgmsg("fmap_dump_to_file: dumping fmap not backed by file...\n"); ret = cli_gentempfd_with_prefix(tmpdir, prefix, &tmpname, &tmpfd); if (ret != CL_SUCCESS) { cli_dbgmsg("fmap_dump_to_file: failed to generate temporary file.\n"); if (NULL != prefix) { free(prefix); prefix = NULL; } return ret; } if (NULL != prefix) { free(prefix); prefix = NULL; } do { const char* b; len = 0; write_size = MIN(BUFSIZ, bytes_remaining); b = fmap_need_off_once_len(map, pos, write_size, &len); pos += len; if (b && (len > 0)) { if ((size_t)cli_writen(tmpfd, b, len) != len) { cli_warnmsg("fmap_dump_to_file: write failed to %s!\n", tmpname); close(tmpfd); unlink(tmpname); free(tmpname); return CL_EWRITE; } } if (len <= bytes_remaining) { bytes_remaining -= len; } else { bytes_remaining = 0; } } while ((len > 0) && (bytes_remaining > 0)); if (lseek(tmpfd, 0, SEEK_SET) == -1) { cli_dbgmsg("fmap_dump_to_file: lseek failed\n"); } *outname = tmpname; *outfd = tmpfd; return CL_SUCCESS; }
int cli_scanrtf(cli_ctx *ctx) { char* tempname; const unsigned char* ptr; const unsigned char* ptr_end; int ret = CL_CLEAN; struct rtf_state state; struct stack stack; size_t bread; table_t* actiontable; uint8_t main_symbols[256]; size_t offset = 0; cli_dbgmsg("in cli_scanrtf()\n"); memset(main_symbols, 0, 256); main_symbols['{']=1; main_symbols['}']=1; main_symbols['\\']=1; stack.stack_cnt = 0; stack.stack_size = 16; stack.elements = 0; stack.warned = 0; stack.states = cli_malloc(stack.stack_size*sizeof(*stack.states)); if(!stack.states) { cli_errmsg("ScanRTF: Unable to allocate memory for stack states\n"); return CL_EMEM; } if(!(tempname = cli_gentemp(ctx->engine->tmpdir))) return CL_EMEM; if(mkdir(tempname, 0700)) { cli_dbgmsg("ScanRTF -> Can't create temporary directory %s\n", tempname); free(stack.states); free(tempname); return CL_ETMPDIR; } actiontable = tableCreate(); if((ret = load_actions(actiontable))) { cli_dbgmsg("RTF: Unable to load rtf action table\n"); free(stack.states); if(!ctx->engine->keeptmp) cli_rmdirs(tempname); free(tempname); tableDestroy(actiontable); return ret; } init_rtf_state(&state); for (offset = 0; (ptr = fmap_need_off_once_len(*ctx->fmap, offset, BUFF_SIZE, &bread)) && bread; offset += bread) { ptr_end = ptr + bread; while(ptr < ptr_end) { switch(state.parse_state) { case PARSE_MAIN: switch(*ptr++) { case '{': if(( ret = push_state(&stack,&state) )) { cli_dbgmsg("RTF:Push failure!\n"); SCAN_CLEANUP; return ret; } break; case '}': if(state.cb_data && state.cb_end) if(( ret = state.cb_end(&state, ctx) )) { SCAN_CLEANUP; return ret; } if(( ret = pop_state(&stack,&state) )) { cli_dbgmsg("RTF:pop failure!\n"); SCAN_CLEANUP; return ret; } break; case '\\': state.parse_state = PARSE_CONTROL_; break; default: ptr--; { size_t i; size_t left = ptr_end - ptr; size_t use = left; for(i = 1;i < left; i++) if(main_symbols[ptr[i]]) { use = i; break; } if(state.cb_begin) { if(!state.cb_data) if(( ret = state.cb_begin(&state, ctx,tempname) )) { SCAN_CLEANUP; return ret; } if(( ret = state.cb_process(&state, ptr, use) )) { if(state.cb_end) { state.cb_end(&state,ctx); } SCAN_CLEANUP; return ret; } } ptr += use; } } break; case PARSE_CONTROL_: if(isalpha(*ptr)) { state.parse_state = PARSE_CONTROL_WORD; state.controlword_cnt = 0; } else state.parse_state = PARSE_CONTROL_SYMBOL; break; case PARSE_CONTROL_SYMBOL: ptr++; /* Do nothing */ state.parse_state = PARSE_MAIN; break; case PARSE_CONTROL_WORD: if(state.controlword_cnt == 32) { cli_dbgmsg("Invalid control word: maximum size exceeded:%s\n",state.controlword); state.parse_state = PARSE_MAIN; } else if(isalpha(*ptr)) state.controlword[state.controlword_cnt++] = *ptr++; else { if(isspace(*ptr)) { state.controlword[state.controlword_cnt++] = *ptr++; state.parse_state = PARSE_INTERPRET_CONTROLWORD; } else if (isdigit(*ptr)) { state.parse_state = PARSE_CONTROL_WORD_PARAM; state.controlword_param = 0; state.controlword_param_sign = 1; } else if(*ptr == '-') { ptr++; state.parse_state = PARSE_CONTROL_WORD_PARAM; state.controlword_param = 0; state.controlword_param_sign = -1; } else { state.parse_state = PARSE_INTERPRET_CONTROLWORD; } } break; case PARSE_CONTROL_WORD_PARAM: if(isdigit(*ptr)) { state.controlword_param = state.controlword_param*10 + *ptr++ - '0'; } else if(isalpha(*ptr)) { ptr++; } else { if(state.controlword_param_sign < 0) state.controlword_param = -state.controlword_param; state.parse_state = PARSE_INTERPRET_CONTROLWORD; } break; case PARSE_INTERPRET_CONTROLWORD: { int action; state.controlword[state.controlword_cnt] = '\0'; action = tableFind(actiontable, state.controlword); if(action != -1) { if(state.cb_data && state.cb_end) {/* premature end of previous block */ state.cb_end(&state,ctx); state.cb_begin = NULL; state.cb_end = NULL; state.cb_data = NULL; } rtf_action(&state,action); } state.parse_state = PARSE_MAIN; break; } } } } SCAN_CLEANUP; return ret; }
int cli_untar(const char *dir, unsigned int posix, cli_ctx *ctx) { int size = 0, ret, fout=-1; int in_block = 0; int last_header_bad = 0; int limitnear = 0; unsigned int files = 0; char fullname[NAME_MAX + 1]; size_t pos = 0; size_t currsize = 0; char zero[BLOCKSIZE]; unsigned int num_viruses = 0; cli_dbgmsg("In untar(%s)\n", dir); memset(zero, 0, sizeof(zero)); for(;;) { const char *block; size_t nread; block = fmap_need_off_once_len(*ctx->fmap, pos, BLOCKSIZE, &nread); cli_dbgmsg("cli_untar: pos = %lu\n", (unsigned long)pos); if(!in_block && !nread) break; if (!nread) block = zero; if(!block) { if(fout>=0) close(fout); cli_errmsg("cli_untar: block read error\n"); return CL_EREAD; } pos += nread; if(!in_block) { char type; int directory, skipEntry = 0; int checksum = -1; char magic[7], name[101], osize[TARSIZELEN + 1]; currsize = 0; if(fout>=0) { lseek(fout, 0, SEEK_SET); ret = cli_magic_scandesc(fout, ctx); close(fout); if (!ctx->engine->keeptmp) if (cli_unlink(fullname)) return CL_EUNLINK; if (ret==CL_VIRUS) { if (!SCAN_ALL) return CL_VIRUS; else num_viruses++; } fout = -1; } if(block[0] == '\0') /* We're done */ break; if((ret=cli_checklimits("cli_untar", ctx, 0, 0, 0))!=CL_CLEAN) return ret; checksum = getchecksum(block); cli_dbgmsg("cli_untar: Candidate checksum = %d, [%o in octal]\n", checksum, checksum); if(testchecksum(block, checksum) != 0) { // If checksum is bad, dump and look for next header block cli_dbgmsg("cli_untar: Invalid checksum in tar header. Skip to next...\n"); if (last_header_bad == 0) { last_header_bad++; cli_dbgmsg("cli_untar: Invalid checksum found inside archive!\n"); } continue; } else { last_header_bad = 0; cli_dbgmsg("cli_untar: Checksum %d is valid.\n", checksum); } /* Notice assumption that BLOCKSIZE > 262 */ if(posix) { strncpy(magic, block+257, 5); magic[5] = '\0'; if(strcmp(magic, "ustar") != 0) { cli_dbgmsg("cli_untar: Incorrect magic string '%s' in tar header\n", magic); return CL_EFORMAT; } } type = block[TARFILETYPEOFFSET]; switch(type) { default: cli_dbgmsg("cli_untar: unknown type flag %c\n", type); case '0': /* plain file */ case '\0': /* plain file */ case '7': /* contiguous file */ case 'M': /* continuation of a file from another volume; might as well scan it. */ files++; directory = 0; break; case '1': /* Link to already archived file */ case '5': /* directory */ case '2': /* sym link */ case '3': /* char device */ case '4': /* block device */ case '6': /* fifo special */ case 'V': /* Volume header */ directory = 1; break; case 'K': case 'L': /* GNU extension - ././@LongLink * Discard the blocks with the extended filename, * the last header will contain parts of it anyway */ case 'N': /* Old GNU format way of storing long filenames. */ case 'A': /* Solaris ACL */ case 'E': /* Solaris Extended attribute s*/ case 'I': /* Inode only */ case 'g': /* Global extended header */ case 'x': /* Extended attributes */ case 'X': /* Extended attributes (POSIX) */ directory = 0; skipEntry = 1; break; } if(directory) { in_block = 0; continue; } strncpy(osize, block+TARSIZEOFFSET, TARSIZELEN); osize[TARSIZELEN] = '\0'; size = octal(osize); if(size < 0) { cli_dbgmsg("cli_untar: Invalid size in tar header\n"); skipEntry++; } else { cli_dbgmsg("cli_untar: size = %d\n", size); ret = cli_checklimits("cli_untar", ctx, size, 0, 0); switch(ret) { case CL_EMAXFILES: // Scan no more files skipEntry++; limitnear = 0; break; case CL_EMAXSIZE: // Either single file limit or total byte limit would be exceeded cli_dbgmsg("cli_untar: would exceed limit, will try up to max"); limitnear = 1; break; default: // Ok based on reported content size limitnear = 0; break; } } if(skipEntry) { const int nskip = (size % BLOCKSIZE || !size) ? size + BLOCKSIZE - (size % BLOCKSIZE) : size; if(nskip < 0) { cli_dbgmsg("cli_untar: got negative skip size, giving up\n"); return CL_CLEAN; } cli_dbgmsg("cli_untar: skipping entry\n"); pos += nskip; continue; } strncpy(name, block, 100); name[100] = '\0'; if(cli_matchmeta(ctx, name, size, size, 0, files, 0, NULL) == CL_VIRUS) { if (!SCAN_ALL) return CL_VIRUS; else num_viruses++; } snprintf(fullname, sizeof(fullname)-1, "%s"PATHSEP"tar%02u", dir, files); fullname[sizeof(fullname)-1] = '\0'; fout = open(fullname, O_RDWR|O_CREAT|O_EXCL|O_TRUNC|O_BINARY, 0600); if(fout < 0) { char err[128]; cli_errmsg("cli_untar: Can't create temporary file %s: %s\n", fullname, cli_strerror(errno, err, sizeof(err))); return CL_ETMPFILE; } cli_dbgmsg("cli_untar: extracting to %s\n", fullname); in_block = 1; } else { /* write or continue writing file contents */ int nbytes, nwritten; int skipwrite = 0; char err[128]; nbytes = size>512? 512:size; if (nread && nread < (size_t)nbytes) nbytes = nread; if (limitnear > 0) { currsize += nbytes; cli_dbgmsg("cli_untar: Approaching limit...\n"); if (cli_checklimits("cli_untar", ctx, (unsigned long)currsize, 0, 0) != CL_SUCCESS) { // Limit would be exceeded by this file, suppress writing beyond limit // Need to keep reading to get to end of file chunk skipwrite++; } } if (skipwrite == 0) { nwritten = (int)cli_writen(fout, block, (size_t)nbytes); if(nwritten != nbytes) { cli_errmsg("cli_untar: only wrote %d bytes to file %s (out of disc space?): %s\n", nwritten, fullname, cli_strerror(errno, err, sizeof(err))); close(fout); return CL_EWRITE; } } size -= nbytes; if ((size != 0) && (nread == 0)) { // Truncated tar file, so end file content like tar behavior cli_dbgmsg("cli_untar: No bytes read! Forcing end of file content.\n"); size = 0; } } if (size == 0) in_block = 0; } if(fout>=0) { lseek(fout, 0, SEEK_SET); ret = cli_magic_scandesc(fout, ctx); close(fout); if (!ctx->engine->keeptmp) if (cli_unlink(fullname)) return CL_EUNLINK; if (ret==CL_VIRUS) return CL_VIRUS; } if (num_viruses) return CL_VIRUS; return CL_CLEAN; }
int cli_scandmg(cli_ctx *ctx) { struct dmg_koly_block hdr; int ret, namelen, ofd; size_t maplen, nread; off_t pos = 0; char *dirname, *tmpfile; const char *outdata; unsigned int file = 0; struct dmg_mish_with_stripes *mish_list = NULL, *mish_list_tail = NULL; enum dmgReadState state = DMG_FIND_BASE_PLIST; int stateDepth[DMG_MAX_STATE]; #if HAVE_LIBXML2 xmlTextReaderPtr reader; #endif if (!ctx || !ctx->fmap) { cli_errmsg("cli_scandmg: Invalid context\n"); return CL_ENULLARG; } maplen = (*ctx->fmap)->real_len; pos = maplen - 512; if (pos <= 0) { cli_dbgmsg("cli_scandmg: Sizing problem for DMG archive.\n"); return CL_CLEAN; } /* Grab koly block */ if (fmap_readn(*ctx->fmap, &hdr, pos, sizeof(hdr)) != sizeof(hdr)) { cli_dbgmsg("cli_scandmg: Invalid DMG trailer block\n"); return CL_EFORMAT; } /* Check magic */ hdr.magic = be32_to_host(hdr.magic); if (hdr.magic == 0x6b6f6c79) { cli_dbgmsg("cli_scandmg: Found koly block @ %ld\n", (long) pos); } else { cli_dbgmsg("cli_scandmg: No koly magic, %8x\n", hdr.magic); return CL_EFORMAT; } hdr.dataForkOffset = be64_to_host(hdr.dataForkOffset); hdr.dataForkLength = be64_to_host(hdr.dataForkLength); cli_dbgmsg("cli_scandmg: data offset %lu len %d\n", (unsigned long)hdr.dataForkOffset, (int)hdr.dataForkLength); hdr.xmlOffset = be64_to_host(hdr.xmlOffset); hdr.xmlLength = be64_to_host(hdr.xmlLength); if (hdr.xmlLength > (uint64_t)INT_MAX) { cli_dbgmsg("cli_scandmg: The embedded XML is way larger than necessary, and probably corrupt or tampered with.\n"); return CL_EFORMAT; } if ((hdr.xmlOffset > (uint64_t)maplen) || (hdr.xmlLength > (uint64_t)maplen) || (hdr.xmlOffset + hdr.xmlLength) > (uint64_t)maplen) { cli_dbgmsg("cli_scandmg: XML out of range for this file\n"); return CL_EFORMAT; } cli_dbgmsg("cli_scandmg: XML offset %lu len %d\n", (unsigned long)hdr.xmlOffset, (int)hdr.xmlLength); if (hdr.xmlLength == 0) { cli_dbgmsg("cli_scandmg: Embedded XML length is zero.\n"); return CL_EFORMAT; } /* Create temp folder for contents */ if (!(dirname = cli_gentemp(ctx->engine->tmpdir))) { return CL_ETMPDIR; } if (mkdir(dirname, 0700)) { cli_errmsg("cli_scandmg: Cannot create temporary directory %s\n", dirname); free(dirname); return CL_ETMPDIR; } cli_dbgmsg("cli_scandmg: Extracting into %s\n", dirname); /* Dump XML to tempfile, if needed */ if (ctx->engine->keeptmp) { int xret; xret = dmg_extract_xml(ctx, dirname, &hdr); if (xret != CL_SUCCESS) { /* Printed err detail inside dmg_extract_xml */ free(dirname); return xret; } } /* scan XML with cli_map_scandesc */ ret = cli_map_scandesc(*ctx->fmap, (off_t)hdr.xmlOffset, (size_t)hdr.xmlLength, ctx); if (ret != CL_CLEAN) { cli_dbgmsg("cli_scandmg: retcode from scanning TOC xml: %s\n", cl_strerror(ret)); if (!ctx->engine->keeptmp) cli_rmdirs(dirname); free(dirname); return ret; } /* page data from map */ outdata = fmap_need_off_once_len(*ctx->fmap, hdr.xmlOffset, hdr.xmlLength, &nread); if (!outdata || (nread != hdr.xmlLength)) { cli_errmsg("cli_scandmg: Failed getting XML from map, len %d\n", (int)hdr.xmlLength); if (!ctx->engine->keeptmp) cli_rmdirs(dirname); free(dirname); return CL_EMAP; } /* time to walk the tree */ /* plist -> dict -> (key:resource_fork) dict -> (key:blkx) array -> dict */ /* each of those bottom level dict should have 4 parts */ /* [ Attributes, Data, ID, Name ], where Data is Base64 mish block */ /* This is the block where we require libxml2 */ #if HAVE_LIBXML2 /* XML_PARSE_NOENT | XML_PARSE_NONET | XML_PARSE_COMPACT */ #define DMG_XML_PARSE_OPTS (1 << 1 | 1 << 11 | 1 << 16) reader = xmlReaderForMemory(outdata, (int)hdr.xmlLength, "toc.xml", NULL, DMG_XML_PARSE_OPTS); if (!reader) { cli_dbgmsg("cli_scandmg: Failed parsing XML!\n"); if (!ctx->engine->keeptmp) cli_rmdirs(dirname); free(dirname); return CL_EFORMAT; } stateDepth[DMG_FIND_BASE_PLIST] = -1; // May need to check for (xmlTextReaderIsEmptyElement(reader) == 0) /* Break loop if have return code or reader can't read any more */ while ((ret == CL_CLEAN) && (xmlTextReaderRead(reader) == 1)) { xmlReaderTypes nodeType; nodeType = xmlTextReaderNodeType(reader); if (nodeType == XML_READER_TYPE_ELEMENT) { // New element, do name check xmlChar *nodeName; int depth; depth = xmlTextReaderDepth(reader); if (depth < 0) { break; } if ((depth > 50) && SCAN_ALGO) { // Possible heuristic, should limit runaway cli_dbgmsg("cli_scandmg: Excessive nesting in DMG TOC.\n"); break; } nodeName = xmlTextReaderLocalName(reader); if (!nodeName) continue; dmg_parsemsg("read: name %s depth %d\n", nodeName, depth); if ((state == DMG_FIND_DATA_MISH) && (depth == stateDepth[state-1])) { xmlChar * textValue; struct dmg_mish_with_stripes *mish_set; /* Reset state early, for continue cases */ stateDepth[DMG_FIND_KEY_DATA] = -1; state--; if (xmlStrcmp(nodeName, "data") != 0) { cli_dbgmsg("cli_scandmg: Not blkx data element\n"); xmlFree(nodeName); continue; } dmg_parsemsg("read: Found blkx data element\n"); /* Pull out data content from text */ if (xmlTextReaderIsEmptyElement(reader)) { cli_dbgmsg("cli_scandmg: blkx data element is empty\n"); xmlFree(nodeName); continue; } if (xmlTextReaderRead(reader) != 1) { xmlFree(nodeName); break; } if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_TEXT) { cli_dbgmsg("cli_scandmg: Next node not text\n"); xmlFree(nodeName); continue; } textValue = xmlTextReaderValue(reader); if (textValue == NULL) { xmlFree(nodeName); continue; } /* Have encoded mish block */ mish_set = cli_malloc(sizeof(struct dmg_mish_with_stripes)); if (mish_set == NULL) { ret = CL_EMEM; xmlFree(textValue); xmlFree(nodeName); break; } ret = dmg_decode_mish(ctx, &file, textValue, mish_set); xmlFree(textValue); if (ret == CL_EFORMAT) { /* Didn't decode, or not a mish block */ ret = CL_CLEAN; xmlFree(nodeName); continue; } else if (ret != CL_CLEAN) { xmlFree(nodeName); continue; } /* Add mish block to list */ if (mish_list_tail != NULL) { mish_list_tail->next = mish_set; mish_list_tail = mish_set; } else { mish_list = mish_set; mish_list_tail = mish_set; } mish_list_tail->next = NULL; } if ((state == DMG_FIND_KEY_DATA) && (depth > stateDepth[state-1]) && (xmlStrcmp(nodeName, "key") == 0)) { xmlChar * textValue; dmg_parsemsg("read: Found key - checking for Data\n"); if (xmlTextReaderRead(reader) != 1) { xmlFree(nodeName); break; } if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_TEXT) { cli_dbgmsg("cli_scandmg: Key node no text\n"); xmlFree(nodeName); continue; } textValue = xmlTextReaderValue(reader); if (textValue == NULL) { cli_dbgmsg("cli_scandmg: no value from xmlTextReaderValue\n"); xmlFree(nodeName); continue; } if (xmlStrcmp(textValue, "Data") == 0) { dmg_parsemsg("read: Matched data\n"); stateDepth[DMG_FIND_KEY_DATA] = depth; state++; } else { dmg_parsemsg("read: text value is %s\n", textValue); } xmlFree(textValue); } if ((state == DMG_FIND_BLKX_CONTAINER) && (depth == stateDepth[state-1])) { if (xmlStrcmp(nodeName, "array") == 0) { dmg_parsemsg("read: Found array blkx\n"); stateDepth[DMG_FIND_BLKX_CONTAINER] = depth; state++; } else if (xmlStrcmp(nodeName, "dict") == 0) { dmg_parsemsg("read: Found dict blkx\n"); stateDepth[DMG_FIND_BLKX_CONTAINER] = depth; state++; } else { cli_dbgmsg("cli_scandmg: Bad blkx, not container\n"); stateDepth[DMG_FIND_KEY_BLKX] = -1; state--; } } if ((state == DMG_FIND_KEY_BLKX) && (depth == stateDepth[state-1] + 1) && (xmlStrcmp(nodeName, "key") == 0)) { xmlChar * textValue; dmg_parsemsg("read: Found key - checking for blkx\n"); if (xmlTextReaderRead(reader) != 1) { xmlFree(nodeName); break; } if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_TEXT) { cli_dbgmsg("cli_scandmg: Key node no text\n"); xmlFree(nodeName); continue; } textValue = xmlTextReaderValue(reader); if (textValue == NULL) { cli_dbgmsg("cli_scandmg: no value from xmlTextReaderValue\n"); xmlFree(nodeName); continue; } if (xmlStrcmp(textValue, "blkx") == 0) { cli_dbgmsg("cli_scandmg: Matched blkx\n"); stateDepth[DMG_FIND_KEY_BLKX] = depth; state++; } else { cli_dbgmsg("cli_scandmg: wanted blkx, text value is %s\n", textValue); } xmlFree(textValue); } if ((state == DMG_FIND_DICT_RESOURCE_FORK) && (depth == stateDepth[state-1])) { if (xmlStrcmp(nodeName, "dict") == 0) { dmg_parsemsg("read: Found resource-fork dict\n"); stateDepth[DMG_FIND_DICT_RESOURCE_FORK] = depth; state++; } else { dmg_parsemsg("read: Not resource-fork dict\n"); stateDepth[DMG_FIND_KEY_RESOURCE_FORK] = -1; state--; } } if ((state == DMG_FIND_KEY_RESOURCE_FORK) && (depth == stateDepth[state-1] + 1) && (xmlStrcmp(nodeName, "key") == 0)) { dmg_parsemsg("read: Found resource-fork key\n"); stateDepth[DMG_FIND_KEY_RESOURCE_FORK] = depth; state++; } if ((state == DMG_FIND_BASE_DICT) && (depth == stateDepth[state-1] + 1) && (xmlStrcmp(nodeName, "dict") == 0)) { dmg_parsemsg("read: Found dict start\n"); stateDepth[DMG_FIND_BASE_DICT] = depth; state++; } if ((state == DMG_FIND_BASE_PLIST) && (xmlStrcmp(nodeName, "plist") == 0)) { dmg_parsemsg("read: Found plist start\n"); stateDepth[DMG_FIND_BASE_PLIST] = depth; state++; } xmlFree(nodeName); } else if ((nodeType == XML_READER_TYPE_END_ELEMENT) && (state > DMG_FIND_BASE_PLIST)) { int significantEnd = 0; int depth = xmlTextReaderDepth(reader); if (depth < 0) { break; } else if (depth < stateDepth[state-1]) { significantEnd = 1; } else if ((depth == stateDepth[state-1]) && (state-1 == DMG_FIND_BLKX_CONTAINER)) { /* Special case, ending blkx container */ significantEnd = 1; } if (significantEnd) { dmg_parsemsg("read: significant end tag, state %d\n", state); stateDepth[state-1] = -1; state--; if ((state-1 == DMG_FIND_KEY_RESOURCE_FORK) || (state-1 == DMG_FIND_KEY_BLKX)) { /* Keys end their own tag (validly) and the next state depends on the following tag */ // cli_dbgmsg("read: significant end tag ending prior key state\n"); stateDepth[state-1] = -1; state--; } } else { dmg_parsemsg("read: not significant end tag, state %d depth %d prior depth %d\n", state, depth, stateDepth[state-1]); } } } xmlFreeTextReader(reader); xmlCleanupParser(); #else cli_dbgmsg("cli_scandmg: libxml2 support is compiled out. It is required for full DMG support.\n"); #endif /* Loop over mish array */ file = 0; while ((ret == CL_CLEAN) && (mish_list != NULL)) { /* Handle & scan mish block */ ret = dmg_handle_mish(ctx, file++, dirname, hdr.xmlOffset, mish_list); free(mish_list->mish); mish_list_tail = mish_list; mish_list = mish_list->next; free(mish_list_tail); } /* Cleanup */ /* If error occurred, need to free mish items and mish blocks */ while (mish_list != NULL) { free(mish_list->mish); mish_list_tail = mish_list; mish_list = mish_list->next; free(mish_list_tail); } if (!ctx->engine->keeptmp) cli_rmdirs(dirname); free(dirname); return ret; }