static int cab_read_block(int fd, struct cab_state *state, uint16_t resdata) { struct cab_block_hdr block_hdr; if(cli_readn(fd, &block_hdr, sizeof(block_hdr)) != sizeof(block_hdr)) { cli_dbgmsg("cab_read_block: Can't read block header\n"); return CL_EFORMAT; /* most likely a corrupted file */ } if(resdata && lseek(fd, (off_t) resdata, SEEK_CUR) == -1) { cli_dbgmsg("cab_read_block: lseek failed\n"); return CL_EFORMAT; /* most likely a corrupted file */ } state->blklen = EC16(block_hdr.cbData); state->outlen = EC16(block_hdr.cbUncomp); if(cli_readn(fd, state->block, state->blklen) != state->blklen) { cli_dbgmsg("cab_read_block: Can't read block data\n"); return CL_EFORMAT; /* most likely a corrupted file */ } state->pt = state->end = state->block; state->end += state->blklen; return CL_SUCCESS; }
static void vba56_test_middle(int fd) { char test_middle[MIDDLE_SIZE]; /* MacOffice middle */ static const uint8_t middle1_str[MIDDLE_SIZE] = { 0x00, 0x01, 0x0d, 0x45, 0x2e, 0xe1, 0xe0, 0x8f, 0x10, 0x1a, 0x85, 0x2e, 0x02, 0x60, 0x8c, 0x4d, 0x0b, 0xb4, 0x00, 0x00 }; /* MS Office middle */ static const uint8_t middle2_str[MIDDLE_SIZE] = { 0x00, 0x00, 0xe1, 0x2e, 0x45, 0x0d, 0x8f, 0xe0, 0x1a, 0x10, 0x85, 0x2e, 0x02, 0x60, 0x8c, 0x4d, 0x0b, 0xb4, 0x00, 0x00 }; if(cli_readn(fd, &test_middle, MIDDLE_SIZE) != MIDDLE_SIZE) return; if((memcmp(test_middle, middle1_str, MIDDLE_SIZE) != 0) && (memcmp(test_middle, middle2_str, MIDDLE_SIZE) != 0)) { cli_dbgmsg("middle not found\n"); if (lseek(fd, -MIDDLE_SIZE, SEEK_CUR) == -1) { cli_dbgmsg("vba_test_middle: call to lseek() failed\n"); return; } } else cli_dbgmsg("middle found\n"); }
static uint64_t chm_copy_file_data(int ifd, int ofd, uint64_t len) { unsigned char data[8192]; uint64_t count, rem; unsigned int todo; rem = len; while (rem > 0) { todo = MIN(8192, rem); count = cli_readn(ifd, data, todo); if (count != todo) { return len-rem; } if (cli_writen(ofd, data, count) != (int64_t)count) { return len-rem-count; } rem -= count; } return len; }
unsigned char * cli_vba_inflate(int fd, off_t offset, int *size) { unsigned int pos, shift, mask, distance, clean; uint8_t flag; uint16_t token; blob *b; unsigned char buffer[VBA_COMPRESSION_WINDOW]; if(fd < 0) return NULL; b = blobCreate(); if(b == NULL) return NULL; lseek(fd, offset+3, SEEK_SET); /* 1byte ?? , 2byte length ?? */ clean = TRUE; pos = 0; while (cli_readn(fd, &flag, 1) == 1) { for(mask = 1; mask < 0x100; mask<<=1) { unsigned int winpos = pos % VBA_COMPRESSION_WINDOW; if (flag & mask) { uint16_t len; unsigned int srcpos; if(!read_uint16(fd, &token, FALSE)) { blobDestroy(b); if(size) *size = 0; return NULL; } shift = 12 - (winpos > 0x10) - (winpos > 0x20) - (winpos > 0x40) - (winpos > 0x80) - (winpos > 0x100) - (winpos > 0x200) - (winpos > 0x400) - (winpos > 0x800); len = (uint16_t)((token & ((1 << shift) - 1)) + 3); distance = token >> shift; srcpos = pos - distance - 1; if((((srcpos + len) % VBA_COMPRESSION_WINDOW) < winpos) && ((winpos + len) < VBA_COMPRESSION_WINDOW) && (((srcpos % VBA_COMPRESSION_WINDOW) + len) < VBA_COMPRESSION_WINDOW) && (len <= VBA_COMPRESSION_WINDOW)) { srcpos %= VBA_COMPRESSION_WINDOW; memcpy(&buffer[winpos], &buffer[srcpos], len); pos += len; } else while(len-- > 0) { srcpos = (pos - distance - 1) % VBA_COMPRESSION_WINDOW; buffer[pos++ % VBA_COMPRESSION_WINDOW] = buffer[srcpos]; } } else { if((pos != 0) && (winpos == 0) && clean) { if (cli_readn(fd, &token, 2) != 2) { blobDestroy(b); if(size) *size = 0; return NULL; } (void)blobAddData(b, buffer, VBA_COMPRESSION_WINDOW); clean = FALSE; break; } if(cli_readn(fd, &buffer[winpos], 1) == 1) pos++; } clean = TRUE; }
vba_project_t * cli_vba_readdir(const char *dir, struct uniq *U, uint32_t which) { unsigned char *buf; const unsigned char vba56_signature[] = { 0xcc, 0x61 }; uint16_t record_count, buflen, ffff, byte_count; uint32_t offset; int i, j, fd, big_endian = FALSE; vba_project_t *vba_project; struct vba56_header v56h; off_t seekback; char fullname[1024], *hash; cli_dbgmsg("in cli_vba_readdir()\n"); if(dir == NULL) return NULL; /* * _VBA_PROJECT files are embedded within office documents (OLE2) */ if (!uniq_get(U, "_vba_project", 12, &hash)) return NULL; snprintf(fullname, sizeof(fullname), "%s"PATHSEP"%s_%u", dir, hash, which); fullname[sizeof(fullname)-1] = '\0'; fd = open(fullname, O_RDONLY|O_BINARY); if(fd == -1) return NULL; if(cli_readn(fd, &v56h, sizeof(struct vba56_header)) != sizeof(struct vba56_header)) { close(fd); return NULL; } if (memcmp(v56h.magic, vba56_signature, sizeof(v56h.magic)) != 0) { close(fd); return NULL; } i = vba_read_project_strings(fd, TRUE); if ((seekback = lseek(fd, 0, SEEK_CUR)) == -1) { cli_dbgmsg("vba_readdir: lseek() failed. Unable to guess VBA type\n"); close(fd); return NULL; } if (lseek(fd, sizeof(struct vba56_header), SEEK_SET) == -1) { cli_dbgmsg("vba_readdir: lseek() failed. Unable to guess VBA type\n"); close(fd); return NULL; } j = vba_read_project_strings(fd, FALSE); if(!i && !j) { close(fd); cli_dbgmsg("vba_readdir: Unable to guess VBA type\n"); return NULL; } if (i > j) { big_endian = TRUE; if (lseek(fd, seekback, SEEK_SET) == -1) { cli_dbgmsg("vba_readdir: call to lseek() while guessing big-endian has failed\n"); close(fd); return NULL; } cli_dbgmsg("vba_readdir: Guessing big-endian\n"); } else { cli_dbgmsg("vba_readdir: Guessing little-endian\n"); } /* junk some more stuff */ do if (cli_readn(fd, &ffff, 2) != 2) { close(fd); return NULL; } while(ffff != 0xFFFF); /* check for alignment error */ if(!seekandread(fd, -3, SEEK_CUR, &ffff, sizeof(uint16_t))) { close(fd); return NULL; } if (ffff != 0xFFFF) { if (lseek(fd, 1, SEEK_CUR) == -1) { cli_dbgmsg("call to lseek() while checking alignment error has failed\n"); close(fd); return NULL; } } if(!read_uint16(fd, &ffff, big_endian)) { close(fd); return NULL; } if(ffff != 0xFFFF) { if (lseek(fd, ffff, SEEK_CUR) == -1) { cli_dbgmsg("call to lseek() while checking alignment error has failed\n"); close(fd); return NULL; } } if(!read_uint16(fd, &ffff, big_endian)) { close(fd); return NULL; } if(ffff == 0xFFFF) ffff = 0; if (lseek(fd, ffff + 100, SEEK_CUR) == -1) { cli_dbgmsg("call to lseek() failed\n"); close(fd); return NULL; } if(!read_uint16(fd, &record_count, big_endian)) { close(fd); return NULL; } cli_dbgmsg("vba_readdir: VBA Record count %d\n", record_count); if (record_count == 0) { /* No macros, assume clean */ close(fd); return NULL; } if (record_count > MAX_VBA_COUNT) { /* Almost certainly an error */ cli_dbgmsg("vba_readdir: VBA Record count too big\n"); close(fd); return NULL; } vba_project = create_vba_project(record_count, dir, U); if(vba_project == NULL) { close(fd); return NULL; } buf = NULL; buflen = 0; for(i = 0; i < record_count; i++) { uint16_t length; char *ptr; vba_project->colls[i] = 0; if(!read_uint16(fd, &length, big_endian)) break; if (length == 0) { cli_dbgmsg("vba_readdir: zero name length\n"); break; } if(length > buflen) { unsigned char *newbuf = (unsigned char *)cli_realloc(buf, length); if(newbuf == NULL) break; buflen = length; buf = newbuf; } if (cli_readn(fd, buf, length) != length) { cli_dbgmsg("vba_readdir: read name failed\n"); break; } ptr = get_unicode_name((const char *)buf, length, big_endian); if(ptr == NULL) break; if (!(vba_project->colls[i]=uniq_get(U, ptr, strlen(ptr), &hash))) { cli_dbgmsg("vba_readdir: cannot find project %s (%s)\n", ptr, hash); free(ptr); break; } cli_dbgmsg("vba_readdir: project name: %s (%s)\n", ptr, hash); free(ptr); vba_project->name[i] = hash; if(!read_uint16(fd, &length, big_endian)) break; lseek(fd, length, SEEK_CUR); if(!read_uint16(fd, &ffff, big_endian)) break; if (ffff == 0xFFFF) { lseek(fd, 2, SEEK_CUR); if(!read_uint16(fd, &ffff, big_endian)) break; lseek(fd, ffff + 8, SEEK_CUR); } else lseek(fd, ffff + 10, SEEK_CUR); if(!read_uint16(fd, &byte_count, big_endian)) break; lseek(fd, (8 * byte_count) + 5, SEEK_CUR); if(!read_uint32(fd, &offset, big_endian)) break; cli_dbgmsg("vba_readdir: offset: %u\n", (unsigned int)offset); vba_project->offset[i] = offset; lseek(fd, 2, SEEK_CUR); } if(buf) free(buf); close(fd); if(i < record_count) { free(vba_project->name); free(vba_project->colls); free(vba_project->dir); free(vba_project->offset); free(vba_project); return NULL; } return vba_project; }
/* return count of valid strings found, 0 on error */ static int vba_read_project_strings(int fd, int big_endian) { unsigned char *buf = NULL; uint16_t buflen = 0; uint16_t length = 0; int ret = 0, getnewlength = 1; for(;;) { off_t offset; char *name; /* if no initial name length, exit */ if(getnewlength && !read_uint16(fd, &length, big_endian)) { ret = 0; break; } getnewlength = 0; /* if too short, break */ if (length < 6) { if (lseek(fd, -2, SEEK_CUR) == -1) { cli_dbgmsg("vba_read_project_strings: call to lseek() has failed\n"); ret = 0; } break; } /* ensure buffer is large enough */ if(length > buflen) { unsigned char *newbuf = (unsigned char *)cli_realloc(buf, length); if(newbuf == NULL) { ret = 0; break; } buflen = length; buf = newbuf; } /* save current offset */ offset = lseek(fd, 0, SEEK_CUR); if (offset == -1) { cli_dbgmsg("vba_read_project_strings: call to lseek() has failed\n"); ret = 0; break; } /* if read name failed, break */ if(cli_readn(fd, buf, length) != (int)length) { cli_dbgmsg("read name failed - rewinding\n"); if (lseek(fd, offset, SEEK_SET) == -1) { cli_dbgmsg("call to lseek() in read name failed\n"); ret = 0; } break; } name = get_unicode_name((const char *)buf, length, big_endian); cli_dbgmsg("length: %d, name: %s\n", length, (name) ? name : "[null]"); /* if invalid name, break */ if((name == NULL) || (memcmp("*\\", name, 2) != 0) || (strchr("ghcd", name[2]) == NULL)) { /* Not a valid string, rewind */ if (lseek(fd, -(length+2), SEEK_CUR) == -1) { cli_dbgmsg("call to lseek() after get_unicode_name has failed\n"); ret = 0; } free(name); break; } free(name); /* can't get length, break */ if(!read_uint16(fd, &length, big_endian)) { break; } ret++; /* continue on reasonable length value */ if ((length != 0) && (length != 65535)) { continue; } /* determine offset and run middle test */ offset = lseek(fd, 10, SEEK_CUR); if (offset == -1) { cli_dbgmsg("call to lseek() has failed\n"); ret = 0; break; } cli_dbgmsg("offset: %lu\n", (unsigned long)offset); vba56_test_middle(fd); getnewlength = 1; } free(buf); return ret; }
static int vba_read_project_strings(int fd, int big_endian) { unsigned char *buf = NULL; uint16_t buflen = 0; int ret = 0; for(;;) { off_t offset; uint16_t length; char *name; if(!read_uint16(fd, &length, big_endian)) break; if (length < 6) { lseek(fd, -2, SEEK_CUR); break; } if(length > buflen) { unsigned char *newbuf = (unsigned char *)cli_realloc(buf, length); if(newbuf == NULL) { if(buf) free(buf); return 0; } buflen = length; buf = newbuf; } offset = lseek(fd, 0, SEEK_CUR); if(cli_readn(fd, buf, length) != (int)length) { cli_dbgmsg("read name failed - rewinding\n"); lseek(fd, offset, SEEK_SET); break; } name = get_unicode_name((const char *)buf, length, big_endian); cli_dbgmsg("length: %d, name: %s\n", length, (name) ? name : "[null]"); if((name == NULL) || (memcmp("*\\", name, 2) != 0) || (strchr("ghcd", name[2]) == NULL)) { /* Not a string */ lseek(fd, -(length+2), SEEK_CUR); if(name) free(name); break; } free(name); if(!read_uint16(fd, &length, big_endian)) { if(buf) free(buf); break; } ret++; if ((length != 0) && (length != 65535)) { lseek(fd, -2, SEEK_CUR); continue; } offset = lseek(fd, 10, SEEK_CUR); cli_dbgmsg("offset: %lu\n", (unsigned long)offset); vba56_test_middle(fd); } if(buf) free(buf); return ret; }
static int cli_tgzload(int fd, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio, struct cli_dbinfo *dbinfo) { char osize[13], name[101]; char block[TAR_BLOCKSIZE]; int nread, fdd, ret; unsigned int type, size, pad, compr = 1; off_t off; struct cli_dbinfo *db; unsigned char hash[32]; cli_dbgmsg("in cli_tgzload()\n"); if(lseek(fd, 512, SEEK_SET) < 0) { return CL_ESEEK; } if(cli_readn(fd, block, 7) != 7) return CL_EFORMAT; /* truncated file? */ if(!strncmp(block, "COPYING", 7)) compr = 0; if(lseek(fd, 512, SEEK_SET) < 0) { return CL_ESEEK; } if((fdd = dup(fd)) == -1) { cli_errmsg("cli_tgzload: Can't duplicate descriptor %d\n", fd); return CL_EDUP; } if(compr) { if((dbio->gzs = gzdopen(fdd, "rb")) == NULL) { cli_errmsg("cli_tgzload: Can't gzdopen() descriptor %d, errno = %d\n", fdd, errno); if (fdd > -1) close(fdd); return CL_EOPEN; } dbio->fs = NULL; } else { if((dbio->fs = fdopen(fdd, "rb")) == NULL) { cli_errmsg("cli_tgzload: Can't fdopen() descriptor %d, errno = %d\n", fdd, errno); if (fdd > -1) close(fdd); return CL_EOPEN; } dbio->gzs = NULL; } dbio->bufsize = CLI_DEFAULT_DBIO_BUFSIZE; dbio->buf = cli_malloc(dbio->bufsize); if(!dbio->buf) { cli_errmsg("cli_tgzload: Can't allocate memory for dbio->buf\n"); cli_tgzload_cleanup(compr, dbio, fdd); return CL_EMALFDB; } dbio->bufpt = NULL; dbio->usebuf = 1; dbio->readpt = dbio->buf; while(1) { if(compr) nread = gzread(dbio->gzs, block, TAR_BLOCKSIZE); else nread = fread(block, 1, TAR_BLOCKSIZE, dbio->fs); if(!nread) break; if(nread != TAR_BLOCKSIZE) { cli_errmsg("cli_tgzload: Incomplete block read\n"); cli_tgzload_cleanup(compr, dbio, fdd); return CL_EMALFDB; } if(block[0] == '\0') /* We're done */ break; strncpy(name, block, 100); name[100] = '\0'; if(strchr(name, '/')) { cli_errmsg("cli_tgzload: Slash separators are not allowed in CVD\n"); cli_tgzload_cleanup(compr, dbio, fdd); return CL_EMALFDB; } type = block[156]; switch(type) { case '0': case '\0': break; case '5': cli_errmsg("cli_tgzload: Directories are not supported in CVD\n"); cli_tgzload_cleanup(compr, dbio, fdd); return CL_EMALFDB; default: cli_errmsg("cli_tgzload: Unknown type flag '%c'\n", type); cli_tgzload_cleanup(compr, dbio, fdd); return CL_EMALFDB; } strncpy(osize, block + 124, 12); osize[12] = '\0'; if((sscanf(osize, "%o", &size)) == 0) { cli_errmsg("cli_tgzload: Invalid size in header\n"); cli_tgzload_cleanup(compr, dbio, fdd); return CL_EMALFDB; } dbio->size = size; dbio->readsize = dbio->size < dbio->bufsize ? dbio->size : dbio->bufsize - 1; dbio->bufpt = NULL; dbio->readpt = dbio->buf; if (!(dbio->hashctx)) { dbio->hashctx = cl_hash_init("sha256"); if (!(dbio->hashctx)) { cli_tgzload_cleanup(compr, dbio, fdd); return CL_EMALFDB; } } dbio->bread = 0; /* cli_dbgmsg("cli_tgzload: Loading %s, size: %u\n", name, size); */ if(compr) off = (off_t) gzseek(dbio->gzs, 0, SEEK_CUR); else off = ftell(dbio->fs); if((!dbinfo && cli_strbcasestr(name, ".info")) || (dbinfo && (CLI_DBEXT(name) || cli_strbcasestr(name, ".ign") || cli_strbcasestr(name, ".ign2")))) { ret = cli_load(name, engine, signo, options, dbio); if(ret) { cli_errmsg("cli_tgzload: Can't load %s\n", name); cli_tgzload_cleanup(compr, dbio, fdd); return CL_EMALFDB; } if(!dbinfo) { cli_tgzload_cleanup(compr, dbio, fdd); return CL_SUCCESS; } else { db = dbinfo; while(db && strcmp(db->name, name)) db = db->next; if(!db) { cli_errmsg("cli_tgzload: File %s not found in .info\n", name); cli_tgzload_cleanup(compr, dbio, fdd); return CL_EMALFDB; } if(dbio->bread) { if(db->size != dbio->bread) { cli_errmsg("cli_tgzload: File %s not correctly loaded\n", name); cli_tgzload_cleanup(compr, dbio, fdd); return CL_EMALFDB; } cl_finish_hash(dbio->hashctx, hash); dbio->hashctx = cl_hash_init("sha256"); if (!(dbio->hashctx)) { cli_tgzload_cleanup(compr, dbio, fdd); return CL_EMALFDB; } if(memcmp(db->hash, hash, 32)) { cli_errmsg("cli_tgzload: Invalid checksum for file %s\n", name); cli_tgzload_cleanup(compr, dbio, fdd); return CL_EMALFDB; } } } } pad = size % TAR_BLOCKSIZE ? (TAR_BLOCKSIZE - (size % TAR_BLOCKSIZE)) : 0; if(compr) { if(off == gzseek(dbio->gzs, 0, SEEK_CUR)) gzseek(dbio->gzs, size + pad, SEEK_CUR); else if(pad) gzseek(dbio->gzs, pad, SEEK_CUR); } else { if(off == ftell(dbio->fs)) fseek(dbio->fs, size + pad, SEEK_CUR); else if(pad) fseek(dbio->fs, pad, SEEK_CUR); } } cli_tgzload_cleanup(compr, dbio, fdd); return CL_SUCCESS; }
int cli_untar(const char *dir, int desc, unsigned int posix, cli_ctx *ctx) { int size = 0, ret, fout=-1; int in_block = 0; unsigned int files = 0; char fullname[NAME_MAX + 1]; cli_dbgmsg("In untar(%s, %d)\n", dir, desc); for(;;) { char block[BLOCKSIZE]; const int nread = cli_readn(desc, block, (unsigned int)sizeof(block)); if(!in_block && nread == 0) break; if(nread < 0) { if(fout>=0) close(fout); cli_errmsg("cli_untar: block read error\n"); return CL_EREAD; } if(!in_block) { char type; int directory, skipEntry = 0; char magic[7], name[101], osize[13]; 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; fout = -1; } if(block[0] == '\0') /* We're done */ break; if((ret=cli_checklimits("cli_untar", ctx, 0, 0, 0))!=CL_CLEAN) return ret; /* 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[156]; 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+124, 12); osize[12] = '\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); if((ret=cli_checklimits("cli_untar", ctx, size, 0, 0))!=CL_CLEAN) skipEntry++; } if(skipEntry) { const int nskip = (size % BLOCKSIZE || !size) ? size + BLOCKSIZE - (size % BLOCKSIZE) : size; if(nskip < 0) { cli_dbgmsg("cli_untar: got nagative skip size, giving up\n"); return CL_CLEAN; } cli_dbgmsg("cli_untar: skipping entry\n"); lseek(desc, nskip, SEEK_CUR); continue; } strncpy(name, block, 100); name[100] = '\0'; if(cli_matchmeta(ctx, name, size, size, 0, files, 0, NULL) == CL_VIRUS) return CL_VIRUS; 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 */ const int nbytes = size>512? 512:size; const int nwritten = (int)write(fout, block, (size_t)nbytes); if(nwritten != nbytes) { cli_errmsg("cli_untar: only wrote %d bytes to file %s (out of disc space?)\n", nwritten, fullname); close(fout); return CL_EWRITE; } size -= nbytes; } 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; } return CL_CLEAN; }
static int nsis_headers(struct nsis_st *n, cli_ctx *ctx) { char buf[28]; struct stat st; uint32_t pos; int i; uint8_t comps[] = {0, 0, 0, 0}, trunc = 0; if (fstat(n->ifd, &st)==-1 || lseek(n->ifd, n->off, SEEK_SET)==-1 || cli_readn(n->ifd, buf, 28) != 28) return CL_EIO; n->hsz = (uint32_t)cli_readint32(buf+0x14); n->asz = (uint32_t)cli_readint32(buf+0x18); cli_dbgmsg("NSIS: Header info - Flags=%x, Header size=%x, Archive size=%x\n", cli_readint32(buf), n->hsz, n->asz); if (st.st_size - n->off < (off_t) n->asz) { cli_dbgmsg("NSIS: Possibly truncated file\n"); n->asz = st.st_size - n->off; trunc++; } else if (st.st_size - n->off != (off_t) n->asz) { cli_dbgmsg("NSIS: Overlays found\n"); } n->asz -= 0x1c; /* Guess if solid */ for (i=0, pos=0;pos < n->asz-4;i++) { int32_t nextsz; if (cli_readn(n->ifd, buf+4, 4)!=4) return CL_EIO; nextsz=cli_readint32(buf+4); if (!i) n->comp = nsis_detcomp(buf+4); if (nextsz&0x80000000) { nextsz&=~0x80000000; if (cli_readn(n->ifd, buf+4, 4)!=4) return CL_EIO; comps[nsis_detcomp(buf+4)]++; nextsz-=4; pos+=4; } if ((pos+=4+nextsz) > n->asz) { n->solid = 1; break; } if (lseek(n->ifd, nextsz, SEEK_CUR)==-1) return CL_EIO; } if (trunc && i>=2) n->solid=0; cli_dbgmsg("NSIS: solid compression%s detected\n", (n->solid)?"":" not"); /* Guess the compression method */ if (!n->solid) { cli_dbgmsg("NSIS: bzip2 %u - lzma %u - zlib %u\n", comps[1], comps[2], comps[3]); n->comp = (comps[1]<comps[2]) ? (comps[2]<comps[3] ? COMP_ZLIB : COMP_LZMA) : (comps[1]<comps[3] ? COMP_ZLIB : COMP_BZIP2); } if (lseek(n->ifd, n->off+0x1c, SEEK_SET)==-1) return CL_EIO; return nsis_unpack_next(n, ctx); }
static int nsis_unpack_next(struct nsis_st *n, cli_ctx *ctx) { unsigned char *ibuf; uint32_t size, loops; int ret; unsigned char obuf[BUFSIZ]; if (n->eof) { cli_dbgmsg("NSIS: extraction complete\n"); return CL_BREAK; } if (ctx->limits && ctx->limits->maxfiles && n->fno >= ctx->limits->maxfiles) { cli_dbgmsg("NSIS: Files limit reached (max: %u)\n", ctx->limits->maxfiles); return CL_EMAXFILES; } if (n->fno) snprintf(n->ofn, 1023, "%s/content.%.3u", n->dir, n->fno); else snprintf(n->ofn, 1023, "%s/headers", n->dir); n->fno++; if ((n->ofd=open(n->ofn, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0600))==-1) { cli_errmsg("NSIS: unable to create output file %s - aborting.", n->ofn); return CL_EIO; } if (!n->solid) { if (cli_readn(n->ifd, &size, 4)!=4) { cli_dbgmsg("NSIS: reached EOF - extraction complete\n"); close(n->ofd); return CL_BREAK; } if (n->asz==4) { cli_dbgmsg("NSIS: reached CRC - extraction complete\n"); close(n->ofd); return CL_BREAK; } loops = EC32(size); if (!(size = (loops&~0x80000000))) { cli_dbgmsg("NSIS: empty file found\n"); return CL_SUCCESS; } if (n->asz <4 || size > n->asz-4) { cli_dbgmsg("NSIS: next file is outside the archive\n"); close(n->ofd); return CL_BREAK; } n->asz -= size+4; if (ctx->limits && ctx->limits->maxfilesize && size > ctx->limits->maxfilesize) { cli_dbgmsg("NSIS: Skipping file due to size limit (%u, max: %lu)\n", size, ctx->limits->maxfilesize); close(n->ofd); if (lseek(n->ifd, size, SEEK_CUR)==-1) return CL_EIO; return CL_EMAXSIZE; } if (!(ibuf= (unsigned char *) cli_malloc(size))) { cli_dbgmsg("NSIS: out of memory"__AT__"\n"); close(n->ofd); return CL_EMEM; } if (cli_readn(n->ifd, ibuf, size) != (ssize_t) size) { cli_dbgmsg("NSIS: cannot read %u bytes"__AT__"\n", size); free(ibuf); close(n->ofd); return CL_EIO; } if (loops==size) { if (cli_writen(n->ofd, ibuf, size) != (ssize_t) size) { cli_dbgmsg("NSIS: cannot write output file"__AT__"\n"); free(ibuf); close(n->ofd); return CL_EIO; } } else { if ((ret=nsis_init(n))!=CL_SUCCESS) { cli_dbgmsg("NSIS: decompressor init failed"__AT__"\n"); free(ibuf); close(n->ofd); return ret; } n->nsis.avail_in = size; n->nsis.next_in = ibuf; n->nsis.next_out = obuf; n->nsis.avail_out = BUFSIZ; loops=0; while ((ret=nsis_decomp(n))==CL_SUCCESS) { if ((size = n->nsis.next_out - obuf)) { if (cli_writen(n->ofd, obuf, size) != (ssize_t) size) { cli_dbgmsg("NSIS: cannot write output file"__AT__"\n"); free(ibuf); close(n->ofd); return CL_EIO; } n->nsis.next_out = obuf; n->nsis.avail_out = BUFSIZ; loops=0; if (ctx->limits && ctx->limits->maxfilesize && size > ctx->limits->maxfilesize) { cli_dbgmsg("NSIS: Skipping file due to size limit (%u, max: %lu)\n", size, ctx->limits->maxfilesize); free(ibuf); close(n->ofd); nsis_shutdown(n); return CL_EMAXSIZE; } } else if (++loops > 10) { cli_dbgmsg("NSIS: xs looping, breaking out"__AT__"\n"); ret = CL_BREAK; break; } } if (ret != CL_BREAK) { cli_dbgmsg("NSIS: bad stream"__AT__"\n"); free(ibuf); close(n->ofd); return CL_EFORMAT; } if (cli_writen(n->ofd, obuf, n->nsis.next_out - obuf) != n->nsis.next_out - obuf) { cli_dbgmsg("NSIS: cannot write output file"__AT__"\n"); free(ibuf); close(n->ofd); return CL_EIO; } nsis_shutdown(n); } free(ibuf); return CL_SUCCESS; } else { if (!n->freeme) { if ((ret=nsis_init(n))!=CL_SUCCESS) { cli_dbgmsg("NSIS: decompressor init failed\n"); close(n->ofd); return ret; } if (!(n->freeme= (unsigned char *) cli_malloc(n->asz))) { cli_dbgmsg("NSIS: out of memory\n"); close(n->ofd); return CL_EMEM; } if (cli_readn(n->ifd, n->freeme, n->asz) != (ssize_t) n->asz) { cli_dbgmsg("NSIS: cannot read %u bytes"__AT__"\n", n->asz); close(n->ofd); return CL_EIO; } n->nsis.next_in = n->freeme; n->nsis.avail_in = n->asz; } if (n->nsis.avail_in<=4) { cli_dbgmsg("NSIS: extraction complete\n"); close(n->ofd); return CL_BREAK; } n->nsis.next_out = obuf; n->nsis.avail_out = 4; loops = 0; while ((ret=nsis_decomp(n))==CL_SUCCESS) { if (n->nsis.next_out - obuf == 4) break; if (++loops > 20) { cli_dbgmsg("NSIS: xs looping, breaking out"__AT__"\n"); ret = CL_BREAK; break; } } if (ret != CL_SUCCESS) { cli_dbgmsg("NSIS: bad stream"__AT__"\n"); close(n->ofd); return CL_EFORMAT; } size=cli_readint32(obuf); if (ctx->limits && ctx->limits->maxfilesize && size > ctx->limits->maxfilesize) { cli_dbgmsg("NSIS: Breaking out due to filesize limit (%u, max: %lu) in solid archive\n", size, ctx->limits->maxfilesize); close(n->ofd); return CL_EFORMAT; } n->nsis.next_out = obuf; n->nsis.avail_out = MIN(BUFSIZ,size); loops = 0; while (size && (ret=nsis_decomp(n))==CL_SUCCESS) { unsigned int wsz; if ((wsz = n->nsis.next_out - obuf)) { if (cli_writen(n->ofd, obuf, wsz) != (ssize_t) wsz) { close(n->ofd); return CL_EIO; } size-=wsz; n->nsis.next_out = obuf; n->nsis.avail_out = MIN(size,BUFSIZ); } else if ( ++loops > 20 ) { cli_dbgmsg("NSIS: xs looping, breaking out"__AT__"\n"); ret = CL_BREAK; break; } } if (ret == CL_BREAK) { if (cli_writen(n->ofd, obuf, n->nsis.next_out - obuf) != n->nsis.next_out - obuf) { close(n->ofd); return CL_EIO; } n->eof=1; } else if (ret != CL_SUCCESS) { cli_dbgmsg("NSIS: bad stream"__AT__"\n"); close(n->ofd); return CL_EFORMAT; } return CL_SUCCESS; } }
int cli_untar(const char *dir, int desc, unsigned int posix) { int size = 0; int in_block = 0; char fullname[NAME_MAX + 1]; FILE *outfile = NULL; cli_dbgmsg("In untar(%s, %d)\n", dir ? dir : "", desc); for(;;) { char block[BLOCKSIZE]; const int nread = cli_readn(desc, block, (unsigned int)sizeof(block)); if(!in_block && nread == 0) break; if(nread < 0) { if(outfile) fclose(outfile); cli_errmsg("cli_untar: block read error\n"); return CL_EIO; } if(!in_block) { char type; const char *suffix; size_t suffixLen = 0; int fd, directory; char magic[7], name[101], osize[13]; if(outfile) { if(fclose(outfile)) { cli_errmsg("cli_untar: cannot close file %s\n", fullname); return CL_EIO; } outfile = (FILE*)0; } if(block[0] == '\0') /* We're done */ break; /* Notice assumption that BLOCKSIZE > 262 */ if(posix) { strncpy(magic, block+257, 5); magic[5] = '\0'; if(strcmp(magic, "ustar") != 0) { cli_dbgmsg("Incorrect magic string '%s' in tar header\n", magic); return CL_EFORMAT; } } type = block[156]; /* * Extra types from [email protected] */ switch(type) { case '0': /* plain file */ case '\0': /* plain file */ case '7': /* contiguous file */ 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 */ directory = 1; break; case 'L': /* GNU extension - ././@LongLink */ cli_errmsg("cli_untar: only standard TAR files are currently supported\n", type); return CL_EFORMAT; default: /*cli_errmsg("cli_untar: unknown type flag %c\n", type); return CL_EFORMAT;*/ /* * It isn't really a tar file */ cli_dbgmsg("cli_untar: unknown type flag %c\n", type); /* * We don't know that it's clean at all, * it would be better to have a * CL_CONTINUE return value since it * may be a different format */ return CL_CLEAN; } if(directory) { in_block = 0; continue; } strncpy(name, block, 100); name[100] = '\0'; /* * see also fileblobSetFilename() * TODO: check if the suffix needs to be put back */ sanitiseName(name); suffix = strrchr(name, '.'); if(suffix == NULL) suffix = ""; else { suffixLen = strlen(suffix); if(suffixLen > 4) { /* Found a full stop which isn't a suffix */ suffix = ""; suffixLen = 0; } } snprintf(fullname, sizeof(fullname) - 1 - suffixLen, "%s/%.*sXXXXXX", dir, (int)(sizeof(fullname) - 9 - suffixLen - strlen(dir)), name); #if defined(C_LINUX) || defined(C_BSD) || defined(HAVE_MKSTEMP) || defined(C_SOLARIS) || defined(C_CYGWIN) || defined(C_KFREEBSD_GNU) fd = mkstemp(fullname); #else (void)mktemp(fullname); fd = open(fullname, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC|O_BINARY, 0600); #endif if(fd < 0) { cli_errmsg("Can't create temporary file %s: %s\n", fullname, strerror(errno)); cli_dbgmsg("%lu %d %d\n", suffixLen, sizeof(fullname), strlen(fullname)); return CL_ETMPFILE; } cli_dbgmsg("cli_untar: extracting %s\n", fullname); in_block = 1; if((outfile = fdopen(fd, "wb")) == NULL) { cli_errmsg("cli_untar: cannot create file %s\n", fullname); close(fd); return CL_ETMPFILE; } strncpy(osize, block+124, 12); osize[12] = '\0'; size = octal(osize); if(size < 0) { cli_errmsg("Invalid size in tar header\n"); if(outfile) fclose(outfile); return CL_EFORMAT; } cli_dbgmsg("cli_untar: size = %d\n", size); } else { /* write or continue writing file contents */ const int nbytes = size>512? 512:size; const int nwritten = fwrite(block, 1, (size_t)nbytes, outfile); if(nwritten != nbytes) { cli_errmsg("cli_untar: only wrote %d bytes to file %s (out of disk space?)\n", nwritten, fullname); if(outfile) fclose(outfile); return CL_EIO; } size -= nbytes; } if (size == 0) in_block = 0; } if(outfile) return fclose(outfile); return 0; }
int cab_open(int fd, off_t offset, struct cab_archive *cab) { unsigned int i, folders = 0; struct cab_file *file, *lfile = NULL; struct cab_folder *folder, *lfolder = NULL; struct cab_hdr hdr; struct cab_hdr_opt hdr_opt; struct cab_folder_hdr folder_hdr; struct cab_file_hdr file_hdr; struct stat sb; uint16_t fidx; char *pt; int ret; off_t resfold = 0, rsize; if(lseek(fd, offset, SEEK_SET) == -1) { cli_errmsg("cab_open: Can't lseek to %u (offset)\n", (unsigned int) offset); return CL_ESEEK; } if(cli_readn(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { cli_dbgmsg("cab_open: Can't read cabinet header\n"); return CL_EFORMAT; /* most likely a corrupted file */ } if(EC32(hdr.signature) != 0x4643534d) { cli_dbgmsg("cab_open: Incorrect CAB signature\n"); return CL_EFORMAT; } else { cli_dbgmsg("CAB: -------------- Cabinet file ----------------\n"); } if(fstat(fd, &sb) == -1) { cli_errmsg("cab_open: Can't fstat descriptor %d\n", fd); return CL_ESTAT; } rsize = sb.st_size; memset(cab, 0, sizeof(struct cab_archive)); cab->length = EC32(hdr.cbCabinet); cli_dbgmsg("CAB: Cabinet length: %u\n", cab->length); if((off_t) cab->length > rsize) { cli_dbgmsg("CAB: Truncating file size from %lu to %lu\n", (unsigned long int) cab->length, (unsigned long int) rsize); cab->length = (uint32_t) rsize; } cab->nfolders = EC16(hdr.cFolders); if(!cab->nfolders) { cli_dbgmsg("cab_open: No folders in cabinet (fake cab?)\n"); return CL_EFORMAT; } else { cli_dbgmsg("CAB: Folders: %u\n", cab->nfolders); if(cab->nfolders > CAB_FOLDER_LIMIT) { cab->nfolders = CAB_FOLDER_LIMIT; cli_dbgmsg("CAB: *** Number of folders limited to %u ***\n", cab->nfolders); } } cab->nfiles = EC16(hdr.cFiles); if(!cab->nfiles) { cli_dbgmsg("cab_open: No files in cabinet (fake cab?)\n"); return CL_EFORMAT; } else { cli_dbgmsg("CAB: Files: %u\n", cab->nfiles); if(cab->nfiles > CAB_FILE_LIMIT) { cab->nfiles = CAB_FILE_LIMIT; cli_dbgmsg("CAB: *** Number of files limited to %u ***\n", cab->nfiles); } } cli_dbgmsg("CAB: File format version: %u.%u\n", hdr.versionMajor, hdr.versionMinor); cab->flags = EC16(hdr.flags); if(cab->flags & 0x0004) { if(cli_readn(fd, &hdr_opt, sizeof(hdr_opt)) != sizeof(hdr_opt)) { cli_dbgmsg("cab_open: Can't read file header (fake cab?)\n"); return CL_EFORMAT; /* most likely a corrupted file */ } cab->reshdr = EC16(hdr_opt.cbCFHeader); resfold = hdr_opt.cbCFFolder; cab->resdata = hdr_opt.cbCFData; if(cab->reshdr) { if(lseek(fd, cab->reshdr, SEEK_CUR) == -1) { cli_dbgmsg("cab_open: Can't lseek to %u (fake cab?)\n", cab->reshdr); return CL_EFORMAT; /* most likely a corrupted file */ } } } if(cab->flags & 0x0001) { /* preceeding cabinet */ /* name */ pt = cab_readstr(fd, &ret); if(ret) return ret; if(cab_chkname(pt, 0)) cli_dbgmsg("CAB: Invalid name of preceeding cabinet\n"); else cli_dbgmsg("CAB: Preceeding cabinet name: %s\n", pt); free(pt); /* info */ pt = cab_readstr(fd, &ret); if(ret) return ret; if(cab_chkname(pt, 0)) cli_dbgmsg("CAB: Invalid info for preceeding cabinet\n"); else cli_dbgmsg("CAB: Preceeding cabinet info: %s\n", pt); free(pt); } if(cab->flags & 0x0002) { /* next cabinet */ /* name */ pt = cab_readstr(fd, &ret); if(ret) return ret; if(cab_chkname(pt, 0)) cli_dbgmsg("CAB: Invalid name of next cabinet\n"); else cli_dbgmsg("CAB: Next cabinet name: %s\n", pt); free(pt); /* info */ pt = cab_readstr(fd, &ret); if(ret) return ret; if(cab_chkname(pt, 0)) cli_dbgmsg("CAB: Invalid info for next cabinet\n"); else cli_dbgmsg("CAB: Next cabinet info: %s\n", pt); free(pt); } /* folders */ for(i = 0; i < cab->nfolders; i++) { if(cli_readn(fd, &folder_hdr, sizeof(folder_hdr)) != sizeof(folder_hdr)) { cli_dbgmsg("cab_open: Can't read header for folder %u\n", i); break; } if(resfold) { if(lseek(fd, resfold, SEEK_CUR) == -1) { cli_dbgmsg("cab_open: Can't lseek to %u (resfold)\n", (unsigned int) resfold); break; } } if(EC32(folder_hdr.coffCabStart) + offset > rsize) { cli_dbgmsg("CAB: Folder out of file\n"); continue; } if((EC16(folder_hdr.typeCompress) & 0x000f) > 3) { cli_dbgmsg("CAB: Unknown compression method\n"); continue; } folder = (struct cab_folder *) cli_calloc(1, sizeof(struct cab_folder)); if(!folder) { cli_errmsg("cab_open: Can't allocate memory for folder\n"); cab_free(cab); return CL_EMEM; } folder->cab = (struct cab_archive *) cab; folder->offset = (off_t) EC32(folder_hdr.coffCabStart) + offset; folder->nblocks = EC16(folder_hdr.cCFData); folder->cmethod = EC16(folder_hdr.typeCompress); cli_dbgmsg("CAB: Folder record %u\n", i); cli_dbgmsg("CAB: Folder offset: %u\n", (unsigned int) folder->offset); cli_dbgmsg("CAB: Folder compression method: %d\n", folder->cmethod); if(!lfolder) cab->folders = folder; else lfolder->next = folder; lfolder = folder; folders++; } cli_dbgmsg("CAB: Recorded folders: %u\n", folders); /* files */ if(cab->nfolders != folders && lseek(fd, EC16(hdr.coffFiles), SEEK_SET) == -1) { cli_dbgmsg("cab_open: Can't lseek to hdr.coffFiles\n"); cab_free(cab); return CL_EFORMAT; } for(i = 0; i < cab->nfiles; i++) { if(cli_readn(fd, &file_hdr, sizeof(file_hdr)) != sizeof(file_hdr)) { cli_dbgmsg("cab_open: Can't read file %u header\n", i); break; } file = (struct cab_file *) cli_calloc(1, sizeof(struct cab_file)); if(!file) { cli_errmsg("cab_open: Can't allocate memory for file\n"); cab_free(cab); return CL_EMEM; } file->cab = cab; file->fd = fd; file->offset = EC32(file_hdr.uoffFolderStart); file->length = EC32(file_hdr.cbFile); file->attribs = EC32(file_hdr.attribs); fidx = EC32(file_hdr.iFolder); file->error = CL_SUCCESS; file->name = cab_readstr(fd, &ret); if(ret) { free(file); continue; } cab_chkname(file->name, 1); cli_dbgmsg("CAB: File record %u\n", i); cli_dbgmsg("CAB: File name: %s\n", file->name); cli_dbgmsg("CAB: File offset: %u\n", (unsigned int) file->offset); cli_dbgmsg("CAB: File folder index: %u\n", fidx); cli_dbgmsg("CAB: File attribs: 0x%x\n", file->attribs); if(file->attribs & 0x01) cli_dbgmsg("CAB: * file is read-only\n"); if(file->attribs & 0x02) cli_dbgmsg("CAB: * file is hidden\n"); if(file->attribs & 0x04) cli_dbgmsg("CAB: * file is a system file\n"); if(file->attribs & 0x20) cli_dbgmsg("CAB: * file modified since last backup\n"); if(file->attribs & 0x40) cli_dbgmsg("CAB: * file to be run after extraction\n"); if(file->attribs & 0x80) cli_dbgmsg("CAB: * file name contains UTF\n"); /* folder index */ if(fidx < 0xfffd) { if(fidx > cab->nfolders) { cli_dbgmsg("cab_open: File %s is not associated with any folder\n", file->name); free(file->name); free(file); continue; } file->folder = cab->folders; while(file->folder && fidx--) file->folder = file->folder->next; if(!file->folder) { cli_dbgmsg("cab_open: Folder not found for file %s\n", file->name); free(file->name); free(file); continue; } } else { cli_dbgmsg("CAB: File is split *skipping*\n"); free(file->name); free(file); continue; } if(!lfile) cab->files = file; else lfile->next = file; lfile = file; } return CL_SUCCESS; }
static int __zip_find_disk_trailer(int fd, off_t filesize, struct zip_disk_trailer *trailer, off_t *start) { char *buf, *end, *tail; off_t offset = 0, bufsize; struct zip_root_dirent dirent; uint32_t u_rootseek, shift = 0; int i; if(!trailer) { cli_errmsg("Unzip: __zip_find_disk_trailer: trailer == NULL\n"); return CL_ENULLARG; } if(filesize < __sizeof(struct zip_disk_trailer)) { cli_errmsg("Unzip: __zip_find_disk_trailer: File too short\n"); return CL_EFORMAT; } if(!(buf = cli_malloc(ZIPBUFSIZ))) return CL_EMEM; offset = filesize; while(1) { if(offset <= 0) { cli_dbgmsg("Unzip: __zip_find_disk_trailer: Central directory not found\n"); free(buf); return CL_EFORMAT; } if(offset >= ZIPBUFSIZ) { if(offset == filesize) offset -= ZIPBUFSIZ; else offset -= ZIPBUFSIZ - sizeof(struct zip_disk_trailer); bufsize = ZIPBUFSIZ; } else { if(filesize < ZIPBUFSIZ) bufsize = offset; else bufsize = ZIPBUFSIZ; offset = 0; } if(lseek(fd, offset, SEEK_SET) < 0) { cli_errmsg("Unzip: __zip_find_disk_trailer: Can't lseek descriptor %d\n", fd); free(buf); return CL_EIO; } if(cli_readn(fd, buf, (size_t) bufsize) < (ssize_t) bufsize) { cli_errmsg("Unzip: __zip_find_disk_trailer: Can't read %u bytes\n", (unsigned int) bufsize); free(buf); return CL_EIO; } end = buf + bufsize; for(tail = end - 1; tail >= buf; tail--) { if((*tail == 'P') && (end - tail >= __sizeof(struct zip_disk_trailer) - 2) && cli_readint32(tail) == ZIP_DISK_TRAILER_MAGIC) { if(end - tail >= __sizeof(struct zip_disk_trailer)) { memcpy(trailer, tail, sizeof(struct zip_disk_trailer)); } else { memcpy(trailer, tail, sizeof(struct zip_disk_trailer) - 2); trailer->z_comment = 0; } __fixup_rootseek(offset + tail - buf, trailer); u_rootseek = EC32(trailer->z_rootseek); if(u_rootseek > (uint32_t) filesize) { cli_dbgmsg("Unzip: __zip_find_disk_trailer: u_rootseek > filesize, continue search\n"); continue; } for(i = 0; i < 2; i++) { if(u_rootseek + shift + sizeof(dirent) < (uint32_t) filesize) { if(lseek(fd, u_rootseek + shift, SEEK_SET) < 0) { cli_errmsg("Unzip: __zip_find_disk_trailer: Can't lseek descriptor %d\n", fd); free(buf); return CL_EIO; } if(cli_readn(fd, &dirent, sizeof(dirent)) < __sizeof(dirent)) { cli_errmsg("Unzip: __zip_find_disk_trailer: Can't read %u bytes\n", (unsigned int) bufsize); free(buf); return CL_EIO; } if(EC32(dirent.z_magic) == ZIP_ROOT_DIRENT_MAGIC) { cli_dbgmsg("Unzip: __zip_find_disk_trailer: found file header at %u, shift %u\n", u_rootseek + shift, shift); free(buf); *start = shift; return CL_SUCCESS; } shift = *start; } } } } }
int cli_scanrtf(int desc, cli_ctx *ctx) { char* tempname; const unsigned char* ptr; const unsigned char* ptr_end; unsigned char* buff; int ret = CL_CLEAN; struct rtf_state state; struct stack stack; ssize_t bread; table_t* actiontable; uint8_t main_symbols[256]; 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) return CL_EMEM; buff = cli_malloc(BUFF_SIZE); if(!buff) { free(stack.states); return CL_EMEM; } tempname = cli_gentemp(NULL); if(mkdir(tempname, 0700)) { cli_dbgmsg("ScanRTF -> Can't create temporary directory %s\n", tempname); free(stack.states); free(buff); 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); free(buff); if(!cli_leavetemps_flag) cli_rmdirs(tempname); free(tempname); tableDestroy(actiontable); return ret; } init_rtf_state(&state); while(( bread = cli_readn(desc, buff, BUFF_SIZE) ) > 0) { ptr = buff; ptr_end = buff + 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; }