void rxtx_print_error(struct rxtx_data *rxtx, const char *prefix, const char *suffix) { enum error_type type; int val; get_last_error(&rxtx->last_error, &type, &val); if (val != 0) { switch (type) { case ETYPE_ERRNO: printf("%s%s%s", prefix, strerror(val), suffix); break; case ETYPE_CLI: printf("%s%s%s", prefix, cli_strerror(val, 0), suffix); break; case ETYPE_BLADERF: printf("%s%s%s", prefix, bladerf_strerror(val), suffix); break; default: printf("%sBUG: Unexpected status=%d%s", prefix, val, suffix); } } else { printf("%sNone%s", prefix, suffix); } }
int conn_reply_errno(const client_conn_t *conn, const char *path, const char *msg) { char err[BUFFSIZE + sizeof(". ERROR")]; cli_strerror(errno, err, BUFFSIZE-1); strcat(err, ". ERROR"); return conn_reply(conn, path, msg, err); }
/* TODO: field in ctx, id of last bytecode that called magicscandesc, reset * after hooks/other bytecodes are run. TODO: need a more generic solution * to avoid uselessly recursing on bytecode-unpacked files, but also a way to * override the limit if we need it in a special situation */ int32_t cli_bcapi_write(struct cli_bc_ctx *ctx, uint8_t*data, int32_t len) { char err[128]; int32_t res; cli_ctx *cctx = (cli_ctx*)ctx->ctx; if (len < 0) { cli_warnmsg("Bytecode API: called with negative length!\n"); API_MISUSE(); return -1; } if (!ctx->outfd) { ctx->tempfile = cli_gentemp(cctx ? cctx->engine->tmpdir : NULL); if (!ctx->tempfile) { cli_dbgmsg("Bytecode API: Unable to allocate memory for tempfile\n"); cli_event_error_oom(EV, 0); return -1; } ctx->outfd = open(ctx->tempfile, O_RDWR|O_CREAT|O_EXCL|O_TRUNC|O_BINARY, 0600); if (ctx->outfd == -1) { ctx->outfd = 0; cli_warnmsg("Bytecode API: Can't create file %s: %s\n", ctx->tempfile, cli_strerror(errno, err, sizeof(err))); cli_event_error_str(EV, "cli_bcapi_write: Can't create temporary file"); free(ctx->tempfile); return -1; } cli_dbgmsg("bytecode opened new tempfile: %s\n", ctx->tempfile); } cli_event_fastdata(ctx->bc_events, BCEV_WRITE, data, len); if (cli_checklimits("bytecode api", cctx, ctx->written + len, 0, 0)) return -1; res = cli_writen(ctx->outfd, data, len); if (res > 0) ctx->written += res; if (res == -1) { cli_warnmsg("Bytecode API: write failed: %s\n", cli_strerror(errno, err, sizeof(err))); cli_event_error_str(EV, "cli_bcapi_write: write failed"); } return res; }
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 cli_chm_open(int fd, const char *dirname, chm_metadata_t *metadata, cli_ctx *ctx) { struct stat statbuf; int retval; cli_dbgmsg("in cli_chm_open\n"); if ((retval = chm_init_metadata(metadata)) != CL_SUCCESS) { return retval; } if (fstat(fd, &statbuf) == 0) { if (statbuf.st_size < CHM_ITSF_MIN_LEN) { return CL_ESTAT; } metadata->m_length = statbuf.st_size; metadata->map = fmap(fd, 0, metadata->m_length); if (!metadata->map) { return CL_EMAP; } } else { char err[128]; cli_warnmsg("fstat() failed: %s\n", cli_strerror(errno, err, sizeof(err))); return CL_ESTAT; } if (!itsf_read_header(metadata)) { goto abort; } itsf_print_header(&metadata->itsf_hdr); if (!itsp_read_header(metadata, metadata->itsf_hdr.dir_offset)) { goto abort; } itsp_print_header(&metadata->itsp_hdr); metadata->chunk_offset = metadata->itsf_hdr.dir_offset+CHM_ITSP_LEN; /* TODO: need to check this first calculation, currently have no files of this type */ if (metadata->itsp_hdr.index_head > 0) { metadata->chunk_offset += metadata->itsp_hdr.index_head * metadata->itsp_hdr.block_len; } metadata->num_chunks = metadata->itsp_hdr.index_tail - metadata->itsp_hdr.index_head + 1; /* Versions before 3 didn't have a data_offset */ /* TODO: need to check this calculation, currently have no files of this type */ if (metadata->itsf_hdr.version < 3) { metadata->itsf_hdr.data_offset = metadata->itsf_hdr.dir_offset + CHM_ITSP_LEN + (metadata->itsp_hdr.block_len*metadata->itsp_hdr.num_blocks); } while (metadata->num_chunks) { if (read_chunk(metadata) != CL_SUCCESS) { cli_dbgmsg("read_chunk failed\n"); goto abort; } if (read_control_entries(metadata) == FALSE) { goto abort; } metadata->num_chunks--; metadata->chunk_offset += metadata->itsp_hdr.block_len; } if (!metadata->sys_content.length || !metadata->sys_control.length || !metadata->sys_reset.length) { cli_dbgmsg("sys file missing\n"); goto abort; } metadata->ufd = chm_decompress_stream(fd, metadata, dirname, ctx); if (metadata->ufd == -1) { goto abort; } metadata->chunk_entries = 0; metadata->chunk_data = NULL; metadata->chunk_offset = metadata->itsf_hdr.dir_offset+CHM_ITSP_LEN; metadata->num_chunks = metadata->itsp_hdr.index_tail - metadata->itsp_hdr.index_head + 1; return CL_SUCCESS; abort: funmap(metadata->map); return CL_EFORMAT; }
char *pdf_convert_utf(char *begin, size_t sz) { char *res=NULL; char *buf, *outbuf; #if HAVE_ICONV char *p1, *p2; size_t inlen, outlen, i; char *encodings[] = { "UTF-16", NULL }; iconv_t cd; #endif buf = cli_calloc(1, sz+1); if (!(buf)) return NULL; memcpy(buf, begin, sz); #if HAVE_ICONV p1 = buf; p2 = outbuf = cli_calloc(1, sz+1); if (!(outbuf)) { free(buf); return NULL; } for (i=0; encodings[i] != NULL; i++) { p1 = buf; p2 = outbuf; inlen = outlen = sz; cd = iconv_open("UTF-8", encodings[i]); if (cd == (iconv_t)(-1)) { char errbuf[128]; cli_strerror(errno, errbuf, sizeof(errbuf)); cli_errmsg("pdf_convert_utf: could not initialize iconv for encoding %s: %s\n", encodings[i], errbuf); continue; } iconv(cd, (char **)(&p1), &inlen, &p2, &outlen); if (outlen == sz) { /* Decoding unsuccessful right from the start */ iconv_close(cd); continue; } outbuf[sz - outlen] = '\0'; res = strdup(outbuf); iconv_close(cd); break; } #else outbuf = cli_utf16_to_utf8(buf, sz, UTF16_BOM); if (!outbuf) { free(buf); return NULL; } res = strdup(outbuf); #endif free(buf); free(outbuf); return res; }
static int fmap_readpage(fmap_t *m, unsigned int first_page, unsigned int count, unsigned int lock_count) { size_t readsz = 0, eintr_off; char *pptr = NULL, errtxt[256]; uint32_t s; unsigned int i, page = first_page, force_read = 0; fmap_lock; for(i=0; i<count; i++) { /* prefault */ /* Not worth checking if the page is already paged, just ping each */ /* Also not worth reusing the loop below */ volatile char faultme; faultme = ((char *)m)[(first_page+i) * m->pgsz + m->hdrsz]; } fmap_unlock; for(i=0; i<=count; i++, page++) { int lock; if(lock_count) { lock_count--; lock = 1; } else lock = 0; if(i == count) { /* we count one page too much to flush pending reads */ if(!pptr) return 0; /* if we have any */ force_read = 1; } else if((s=fmap_bitmap[page]) & FM_MASK_PAGED) { /* page already paged */ if(lock) { /* we want locking */ if(s & FM_MASK_LOCKED) { /* page already locked */ s &= FM_MASK_COUNT; if(s == FM_MASK_COUNT) { /* lock count already at max: fial! */ cli_errmsg("fmap_readpage: lock count exceeded\n"); return 1; } /* acceptable lock count: inc lock count */ fmap_bitmap[page]++; } else /* page not currently locked: set lock count = 1 */ fmap_bitmap[page] = 1 | FM_MASK_LOCKED | FM_MASK_PAGED; } else { /* we don't want locking */ if(!(s & FM_MASK_LOCKED)) { /* page is not locked: we reset aging to max */ fmap_bitmap[page] = FM_MASK_PAGED | FM_MASK_COUNT; } } if(!pptr) continue; force_read = 1; } if(force_read) { /* we have some pending reads to perform */ if (m->handle_is_fd) { unsigned int j; int _fd = (int)(ssize_t)m->handle; for(j=first_page; j<page; j++) { if(fmap_bitmap[j] & FM_MASK_SEEN) { /* page we've seen before: check mtime */ STATBUF st; if(FSTAT(_fd, &st)) { cli_strerror(errno, errtxt, sizeof(errtxt)); cli_warnmsg("fmap_readpage: fstat failed: %s\n", errtxt); return 1; } if(m->mtime != st.st_mtime) { cli_warnmsg("fmap_readpage: file changed as we read it\n"); return 1; } break; } } } eintr_off = 0; while(readsz) { ssize_t got; off_t target_offset = eintr_off + m->offset + (first_page * m->pgsz); got=m->pread_cb(m->handle, pptr, readsz, target_offset); if(got < 0 && errno == EINTR) continue; if(got > 0) { pptr += got; eintr_off += got; readsz -= got; continue; } if(got < 0) { cli_strerror(errno, errtxt, sizeof(errtxt)); cli_errmsg("fmap_readpage: pread error: %s\n", errtxt); } else { cli_warnmsg("fmap_readpage: pread fail: asked for %lu bytes @ offset %lu, got %lu\n", (long unsigned int)readsz, (long unsigned int)target_offset, (long unsigned int)got); } return 1; } pptr = NULL; force_read = 0; readsz = 0; continue; } /* page is not already paged */ if(!pptr) { /* set a new start for pending reads if we don't have one */ pptr = (char *)m + page * m->pgsz + m->hdrsz; first_page = page; } if((page == m->pages - 1) && (m->real_len % m->pgsz)) readsz += m->real_len % m->pgsz; else readsz += m->pgsz; if(lock) /* lock requested: set paged, lock page and set lock count to 1 */ fmap_bitmap[page] = FM_MASK_PAGED | FM_MASK_LOCKED | 1; else /* no locking: set paged and set aging to max */ fmap_bitmap[page] = FM_MASK_PAGED | FM_MASK_COUNT; m->paged++; } return 0; }
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; }
/* TODO: handle ReadTimeout */ int fds_poll_recv (struct fd_data *data, int timeout, int check_signals, void *event) { unsigned fdsok = data->nfds; size_t i; int retval; time_t now, closest_timeout; UNUSEDPARAM(event); /* we must have at least one fd, the control fd! */ fds_cleanup (data); #ifndef _WIN32 if (!data->nfds) return 0; #endif for (i = 0; i < data->nfds; i++) { data->buf[i].got_newdata = 0; } time (&now); if (timeout > 0) closest_timeout = now + timeout; else closest_timeout = 0; for (i = 0; i < data->nfds; i++) { time_t timeout_at = data->buf[i].timeout_at; if (timeout_at && timeout_at < now) { /* timed out */ data->buf[i].got_newdata = -2; /* we must return immediately from poll/select, we have a timeout! */ closest_timeout = now; } else { if (!closest_timeout) closest_timeout = timeout_at; else if (timeout_at && timeout_at < closest_timeout) closest_timeout = timeout_at; } } if (closest_timeout) timeout = closest_timeout - now; else timeout = -1; if (timeout > 0) logg ("$fds_poll_recv: timeout after %d seconds\n", timeout); #ifdef HAVE_POLL /* Use poll() if available, preferred because: * - can poll any number of FDs * - can notify of both data available / socket disconnected events * - when it says POLLIN it is guaranteed that a following recv() won't * block (select may say that data is available to read, but a following * recv() may still block according to the manpage */ if (realloc_polldata (data) == -1) return -1; if (timeout > 0) { /* seconds to ms */ timeout *= 1000; } for (i = 0; i < data->nfds; i++) { data->poll_data[i].fd = data->buf[i].fd; data->poll_data[i].events = POLLIN; data->poll_data[i].revents = 0; } do { int n = data->nfds; fds_unlock (data); #ifdef _WIN32 retval = poll_with_event (data->poll_data, n, timeout, event); #else retval = poll (data->poll_data, n, timeout); #endif fds_lock (data); if (retval > 0) { fdsok = 0; /* nfds may change during poll, but not * poll_data_nfds */ for (i = 0; i < data->poll_data_nfds; i++) { short revents; if (data->buf[i].fd < 0) continue; if (data->buf[i].fd != data->poll_data[i].fd) { /* should never happen */ logg ("!poll_recv_fds FD mismatch\n"); continue; } revents = data->poll_data[i].revents; if (revents & (POLLIN | POLLHUP)) { logg ("$Received POLLIN|POLLHUP on fd %d\n", data->poll_data[i].fd); } #ifndef _WIN32 if (revents & POLLHUP) { /* avoid SHUT_WR problem on Mac OS X */ int ret = send (data->poll_data[i].fd, &n, 0, 0); if (!ret || (ret == -1 && errno == EINTR)) revents &= ~POLLHUP; } #endif if (revents & POLLIN) { int ret = read_fd_data (&data->buf[i]); /* Data available to be read */ if (ret == -1) revents |= POLLERR; else if (!ret) revents = POLLHUP; } if (revents & (POLLHUP | POLLERR | POLLNVAL)) { if (revents & (POLLHUP | POLLNVAL)) { /* remote disconnected */ logg ("*Client disconnected (FD %d)\n", data->poll_data[i].fd); } else { /* error on file descriptor */ logg ("^Error condition on fd %d\n", data->poll_data[i].fd); } data->buf[i].got_newdata = -1; } else { fdsok++; } } } } while (retval == -1 && !check_signals && errno == EINTR); #else { fd_set rfds; struct timeval tv; int maxfd = -1; for (i = 0; i < data->nfds; i++) { int fd = data->buf[i].fd; if (fd >= FD_SETSIZE) { logg ("!File descriptor is too high for FD_SET\n"); return -1; } maxfd = MAX (maxfd, fd); } do { FD_ZERO (&rfds); for (i = 0; i < data->nfds; i++) { int fd = data->buf[i].fd; if (fd >= 0) FD_SET (fd, &rfds); } tv.tv_sec = timeout; tv.tv_usec = 0; fds_unlock (data); retval = select (maxfd + 1, &rfds, NULL, NULL, timeout >= 0 ? &tv : NULL); fds_lock (data); if (retval > 0) { fdsok = data->nfds; for (i = 0; i < data->nfds; i++) { if (data->buf[i].fd < 0) { fdsok--; continue; } if (FD_ISSET (data->buf[i].fd, &rfds)) { int ret = read_fd_data (&data->buf[i]); if (ret == -1 || !ret) { if (ret == -1) logg ("!Error condition on fd %d\n", data->buf[i].fd); else { /* avoid SHUT_WR problem on Mac OS X */ int ret = send (data->buf[i].fd, &i, 0, 0); if (!ret || (ret == -1 && errno == EINTR)) continue; logg ("*Client disconnected\n"); } data->buf[i].got_newdata = -1; } } } } if (retval < 0 && errno == EBADF) { /* unlike poll(), select() won't tell us which FD is bad, so * we have to check them one by one. */ tv.tv_sec = 0; tv.tv_usec = 0; /* with tv == 0 it doesn't check for EBADF */ FD_ZERO (&rfds); for (i = 0; i < data->nfds; i++) { if (data->buf[i].fd == -1) continue; FD_SET (data->buf[i].fd, &rfds); do { retval = select (data->buf[i].fd + 1, &rfds, NULL, NULL, &tv); } while (retval == -1 && errno == EINTR); if (retval == -1) { data->buf[i].fd = -1; } else { FD_CLR (data->buf[i].fd, &rfds); } } retval = -1; errno = EINTR; continue; } } while (retval == -1 && !check_signals && errno == EINTR); } #endif if (retval == -1 && errno != EINTR) { char err[128]; #ifdef HAVE_POLL logg ("!poll_recv_fds: poll failed: %s\n", cli_strerror (errno, err, sizeof (err))); #else logg ("!poll_recv_fds: select failed: %s\n", cli_strerror (errno, err, sizeof (err))); #endif } return retval; }
int cli_scanxar(cli_ctx *ctx) { int rc = CL_SUCCESS; unsigned int cksum_fails = 0; unsigned int extract_errors = 0; #if HAVE_LIBXML2 int fd = -1; struct xar_header hdr; fmap_t *map = *ctx->fmap; long length, offset, size, at; int encoding; z_stream strm; char *toc, *tmpname; xmlTextReaderPtr reader = NULL; int a_hash, e_hash; unsigned char *a_cksum = NULL, *e_cksum = NULL; memset(&strm, 0x00, sizeof(z_stream)); /* retrieve xar header */ if (fmap_readn(*ctx->fmap, &hdr, 0, sizeof(hdr)) != sizeof(hdr)) { cli_dbgmsg("cli_scanxar: Invalid header, too short.\n"); return CL_EFORMAT; } hdr.magic = be32_to_host(hdr.magic); if (hdr.magic == XAR_HEADER_MAGIC) { cli_dbgmsg("cli_scanxar: Matched magic\n"); } else { cli_dbgmsg("cli_scanxar: Invalid magic\n"); return CL_EFORMAT; } hdr.size = be16_to_host(hdr.size); hdr.version = be16_to_host(hdr.version); hdr.toc_length_compressed = be64_to_host(hdr.toc_length_compressed); hdr.toc_length_decompressed = be64_to_host(hdr.toc_length_decompressed); hdr.chksum_alg = be32_to_host(hdr.chksum_alg); /* cli_dbgmsg("hdr.magic %x\n", hdr.magic); */ /* cli_dbgmsg("hdr.size %i\n", hdr.size); */ /* cli_dbgmsg("hdr.version %i\n", hdr.version); */ /* cli_dbgmsg("hdr.toc_length_compressed %lu\n", hdr.toc_length_compressed); */ /* cli_dbgmsg("hdr.toc_length_decompressed %lu\n", hdr.toc_length_decompressed); */ /* cli_dbgmsg("hdr.chksum_alg %i\n", hdr.chksum_alg); */ /* Uncompress TOC */ strm.next_in = (unsigned char *)fmap_need_off_once(*ctx->fmap, hdr.size, hdr.toc_length_compressed); if (strm.next_in == NULL) { cli_dbgmsg("cli_scanxar: fmap_need_off_once fails on TOC.\n"); return CL_EREAD; } strm.avail_in = hdr.toc_length_compressed; toc = cli_malloc(hdr.toc_length_decompressed+1); if (toc == NULL) { cli_dbgmsg("cli_scanxar: cli_malloc fails on TOC decompress buffer.\n"); return CL_EMEM; } toc[hdr.toc_length_decompressed] = '\0'; strm.avail_out = hdr.toc_length_decompressed; strm.next_out = (unsigned char *)toc; rc = inflateInit(&strm); if (rc != Z_OK) { cli_dbgmsg("cli_scanxar:inflateInit error %i \n", rc); rc = CL_EFORMAT; goto exit_toc; } rc = inflate(&strm, Z_SYNC_FLUSH); if (rc != Z_OK && rc != Z_STREAM_END) { cli_dbgmsg("cli_scanxar:inflate error %i \n", rc); rc = CL_EFORMAT; goto exit_toc; } rc = inflateEnd(&strm); if (rc != Z_OK) { cli_dbgmsg("cli_scanxar:inflateEnd error %i \n", rc); rc = CL_EFORMAT; goto exit_toc; } /* cli_dbgmsg("cli_scanxar: TOC xml:\n%s\n", toc); */ /* printf("cli_scanxar: TOC xml:\n%s\n", toc); */ /* cli_dbgmsg("cli_scanxar: TOC end:\n"); */ /* printf("cli_scanxar: TOC end:\n"); */ /* scan the xml */ cli_dbgmsg("cli_scanxar: scanning xar TOC xml in memory.\n"); rc = cli_mem_scandesc(toc, hdr.toc_length_decompressed, ctx); if (rc != CL_SUCCESS) { if (rc != CL_VIRUS || !SCAN_ALL) goto exit_toc; } /* make a file to leave if --leave-temps in effect */ if(ctx->engine->keeptmp) { if ((rc = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { cli_dbgmsg("cli_scanxar: Can't create temporary file for TOC.\n"); goto exit_toc; } if (cli_writen(fd, toc, hdr.toc_length_decompressed) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error writing TOC.\n"); rc = CL_EWRITE; xar_cleanup_temp_file(ctx, fd, tmpname); goto exit_toc; } rc = xar_cleanup_temp_file(ctx, fd, tmpname); if (rc != CL_SUCCESS) goto exit_toc; } reader = xmlReaderForMemory(toc, hdr.toc_length_decompressed, "noname.xml", NULL, 0); if (reader == NULL) { cli_dbgmsg("cli_scanxar: xmlReaderForMemory error for TOC\n"); goto exit_toc; } rc = xar_scan_subdocuments(reader, ctx); if (rc != CL_SUCCESS) { cli_dbgmsg("xar_scan_subdocuments returns %i.\n", rc); goto exit_reader; } /* Walk the TOC XML and extract files */ fd = -1; tmpname = NULL; while (CL_SUCCESS == (rc = xar_get_toc_data_values(reader, &length, &offset, &size, &encoding, &a_cksum, &a_hash, &e_cksum, &e_hash))) { int do_extract_cksum = 1; unsigned char * blockp; void *a_sc, *e_sc; void *a_mc, *e_mc; void *a_hash_ctx, *e_hash_ctx; char result[SHA1_HASH_SIZE]; char * expected; /* clean up temp file from previous loop iteration */ if (fd > -1 && tmpname) { rc = xar_cleanup_temp_file(ctx, fd, tmpname); if (rc != CL_SUCCESS) goto exit_reader; } at = offset + hdr.toc_length_compressed + hdr.size; if ((rc = cli_gentempfd(ctx->engine->tmpdir, &tmpname, &fd)) != CL_SUCCESS) { cli_dbgmsg("cli_scanxar: Can't generate temporary file.\n"); goto exit_reader; } cli_dbgmsg("cli_scanxar: decompress into temp file:\n%s, size %li,\n" "from xar heap offset %li length %li\n", tmpname, size, offset, length); a_hash_ctx = xar_hash_init(a_hash, &a_sc, &a_mc); e_hash_ctx = xar_hash_init(e_hash, &e_sc, &e_mc); switch (encoding) { case CL_TYPE_GZ: /* inflate gzip directly because file segments do not contain magic */ memset(&strm, 0, sizeof(strm)); if ((rc = inflateInit(&strm)) != Z_OK) { cli_dbgmsg("cli_scanxar: InflateInit failed: %d\n", rc); rc = CL_EFORMAT; extract_errors++; break; } while ((size_t)at < map->len && (unsigned long)at < offset+hdr.toc_length_compressed+hdr.size+length) { unsigned long avail_in; void * next_in; unsigned int bytes = MIN(map->len - at, map->pgsz); bytes = MIN(length, bytes); if(!(strm.next_in = next_in = (void*)fmap_need_off_once(map, at, bytes))) { cli_dbgmsg("cli_scanxar: Can't read %u bytes @ %lu.\n", bytes, (long unsigned)at); inflateEnd(&strm); rc = CL_EREAD; goto exit_tmpfile; } at += bytes; strm.avail_in = avail_in = bytes; do { int inf, outsize = 0; unsigned char buff[FILEBUFF]; strm.avail_out = sizeof(buff); strm.next_out = buff; inf = inflate(&strm, Z_SYNC_FLUSH); if (inf != Z_OK && inf != Z_STREAM_END && inf != Z_BUF_ERROR) { cli_dbgmsg("cli_scanxar: inflate error %i %s.\n", inf, strm.msg?strm.msg:""); rc = CL_EFORMAT; extract_errors++; break; } bytes = sizeof(buff) - strm.avail_out; xar_hash_update(e_hash_ctx, buff, bytes, e_hash); if (cli_writen(fd, buff, bytes) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error file %s.\n", tmpname); inflateEnd(&strm); rc = CL_EWRITE; goto exit_tmpfile; } outsize += sizeof(buff) - strm.avail_out; if (cli_checklimits("cli_scanxar", ctx, outsize, 0, 0) != CL_CLEAN) { break; } if (inf == Z_STREAM_END) { break; } } while (strm.avail_out == 0); if (rc != CL_SUCCESS) break; avail_in -= strm.avail_in; xar_hash_update(a_hash_ctx, next_in, avail_in, a_hash); } inflateEnd(&strm); break; case CL_TYPE_7Z: #define CLI_LZMA_OBUF_SIZE 1024*1024 #define CLI_LZMA_HDR_SIZE LZMA_PROPS_SIZE+8 #define CLI_LZMA_IBUF_SIZE CLI_LZMA_OBUF_SIZE>>2 /* estimated compression ratio 25% */ { struct CLI_LZMA lz; unsigned long in_remaining = length; unsigned long out_size = 0; unsigned char * buff = __lzma_wrap_alloc(NULL, CLI_LZMA_OBUF_SIZE); int lret; memset(&lz, 0, sizeof(lz)); if (buff == NULL) { cli_dbgmsg("cli_scanxar: memory request for lzma decompression buffer fails.\n"); rc = CL_EMEM; goto exit_tmpfile; } blockp = (void*)fmap_need_off_once(map, at, CLI_LZMA_HDR_SIZE); if (blockp == NULL) { char errbuff[128]; cli_strerror(errno, errbuff, sizeof(errbuff)); cli_dbgmsg("cli_scanxar: Can't read %li bytes @ %li, errno:%s.\n", length, at, errbuff); rc = CL_EREAD; __lzma_wrap_free(NULL, buff); goto exit_tmpfile; } lz.next_in = blockp; lz.avail_in = CLI_LZMA_HDR_SIZE; xar_hash_update(a_hash_ctx, blockp, CLI_LZMA_HDR_SIZE, a_hash); lret = cli_LzmaInit(&lz, 0); if (lret != LZMA_RESULT_OK) { cli_dbgmsg("cli_scanxar: cli_LzmaInit() fails: %i.\n", lret); rc = CL_EFORMAT; __lzma_wrap_free(NULL, buff); extract_errors++; break; } at += CLI_LZMA_HDR_SIZE; in_remaining -= CLI_LZMA_HDR_SIZE; while ((size_t)at < map->len && (unsigned long)at < offset+hdr.toc_length_compressed+hdr.size+length) { SizeT avail_in; SizeT avail_out; void * next_in; unsigned long in_consumed; lz.next_out = buff; lz.avail_out = CLI_LZMA_OBUF_SIZE; lz.avail_in = avail_in = MIN(CLI_LZMA_IBUF_SIZE, in_remaining); lz.next_in = next_in = (void*)fmap_need_off_once(map, at, lz.avail_in); if (lz.next_in == NULL) { char errbuff[128]; cli_strerror(errno, errbuff, sizeof(errbuff)); cli_dbgmsg("cli_scanxar: Can't read %li bytes @ %li, errno: %s.\n", length, at, errbuff); rc = CL_EREAD; __lzma_wrap_free(NULL, buff); cli_LzmaShutdown(&lz); goto exit_tmpfile; } lret = cli_LzmaDecode(&lz); if (lret != LZMA_RESULT_OK && lret != LZMA_STREAM_END) { cli_dbgmsg("cli_scanxar: cli_LzmaDecode() fails: %i.\n", lret); rc = CL_EFORMAT; extract_errors++; break; } in_consumed = avail_in - lz.avail_in; in_remaining -= in_consumed; at += in_consumed; avail_out = CLI_LZMA_OBUF_SIZE - lz.avail_out; if (avail_out == 0) cli_dbgmsg("cli_scanxar: cli_LzmaDecode() produces no output for " "avail_in %lu, avail_out %lu.\n", avail_in, avail_out); xar_hash_update(a_hash_ctx, next_in, in_consumed, a_hash); xar_hash_update(e_hash_ctx, buff, avail_out, e_hash); /* Write a decompressed block. */ /* cli_dbgmsg("Writing %li bytes to LZMA decompress temp file, " */ /* "consumed %li of %li available compressed bytes.\n", */ /* avail_out, in_consumed, avail_in); */ if (cli_writen(fd, buff, avail_out) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error writing lzma temp file for %li bytes.\n", avail_out); __lzma_wrap_free(NULL, buff); cli_LzmaShutdown(&lz); rc = CL_EWRITE; goto exit_tmpfile; } /* Check file size limitation. */ out_size += avail_out; if (cli_checklimits("cli_scanxar", ctx, out_size, 0, 0) != CL_CLEAN) { break; } if (lret == LZMA_STREAM_END) break; } cli_LzmaShutdown(&lz); __lzma_wrap_free(NULL, buff); } break; case CL_TYPE_ANY: default: case CL_TYPE_BZ: case CL_TYPE_XZ: /* for uncompressed, bzip2, xz, and unknown, just pull the file, cli_magic_scandesc does the rest */ do_extract_cksum = 0; { unsigned long write_len; if (ctx->engine->maxfilesize) write_len = MIN((size_t)(ctx->engine->maxfilesize), (size_t)length); else write_len = length; if (!(blockp = (void*)fmap_need_off_once(map, at, length))) { char errbuff[128]; cli_strerror(errno, errbuff, sizeof(errbuff)); cli_dbgmsg("cli_scanxar: Can't read %li bytes @ %li, errno:%s.\n", length, at, errbuff); rc = CL_EREAD; goto exit_tmpfile; } xar_hash_update(a_hash_ctx, blockp, length, a_hash); if (cli_writen(fd, blockp, write_len) < 0) { cli_dbgmsg("cli_scanxar: cli_writen error %li bytes @ %li.\n", length, at); rc = CL_EWRITE; goto exit_tmpfile; } /*break;*/ } } if (rc == CL_SUCCESS) { xar_hash_final(a_hash_ctx, result, a_hash); if (a_cksum != NULL) { expected = cli_hex2str((char *)a_cksum); if (xar_hash_check(a_hash, result, expected) != 0) { cli_dbgmsg("cli_scanxar: archived-checksum missing or mismatch.\n"); cksum_fails++; } else { cli_dbgmsg("cli_scanxar: archived-checksum matched.\n"); } free(expected); } if (e_cksum != NULL) { if (do_extract_cksum) { xar_hash_final(e_hash_ctx, result, e_hash); expected = cli_hex2str((char *)e_cksum); if (xar_hash_check(e_hash, result, expected) != 0) { cli_dbgmsg("cli_scanxar: extracted-checksum missing or mismatch.\n"); cksum_fails++; } else { cli_dbgmsg("cli_scanxar: extracted-checksum matched.\n"); } free(expected); } } rc = cli_magic_scandesc(fd, ctx); if (rc != CL_SUCCESS) { if (rc == CL_VIRUS) { cli_dbgmsg("cli_scanxar: Infected with %s\n", cli_get_last_virus(ctx)); if (!SCAN_ALL) goto exit_tmpfile; } else if (rc != CL_BREAK) { cli_dbgmsg("cli_scanxar: cli_magic_scandesc error %i\n", rc); goto exit_tmpfile; } } } if (a_cksum != NULL) { xmlFree(a_cksum); a_cksum = NULL; } if (e_cksum != NULL) { xmlFree(e_cksum); e_cksum = NULL; } } exit_tmpfile: xar_cleanup_temp_file(ctx, fd, tmpname); exit_reader: if (a_cksum != NULL) xmlFree(a_cksum); if (e_cksum != NULL) xmlFree(e_cksum); xmlTextReaderClose(reader); xmlFreeTextReader(reader); exit_toc: free(toc); if (rc == CL_BREAK) rc = CL_SUCCESS; #else cli_dbgmsg("cli_scanxar: can't scan xar files, need libxml2.\n"); #endif if (cksum_fails + extract_errors != 0) { cli_warnmsg("cli_scanxar: %u checksum errors and %u extraction errors, use --debug for more info.\n", cksum_fails, extract_errors); } return rc; }
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; }
void *onas_fan_th(void *arg) { struct thrarg *tharg = (struct thrarg *) arg; sigset_t sigset; struct sigaction act; int eventcnt = 1, extinfo; char err[128]; struct ClamAuthEvent event; /* ignore all signals except SIGUSR1 */ sigfillset(&sigset); sigdelset(&sigset, SIGUSR1); /* The behavior of a process is undefined after it ignores a * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */ sigdelset(&sigset, SIGFPE); sigdelset(&sigset, SIGILL); sigdelset(&sigset, SIGSEGV); #ifdef SIGBUS sigdelset(&sigset, SIGBUS); #endif pthread_sigmask(SIG_SETMASK, &sigset, NULL); memset(&act, 0, sizeof(struct sigaction)); act.sa_handler = cauth_exit; sigfillset(&(act.sa_mask)); sigaction(SIGUSR1, &act, NULL); sigaction(SIGSEGV, &act, NULL); extinfo = optget(tharg->opts, "ExtendedDetectionInfo")->enabled; cauth_fd = open("/dev/clamauth", O_RDONLY); if(cauth_fd == -1) { logg("!ScanOnAccess: Can't open /dev/clamauth\n"); if(errno == ENOENT) logg("!ScanOnAccess: Please make sure ClamAuth.kext is loaded\n"); else if(errno == EACCES) logg("!ScanOnAccess: This application requires root privileges\n"); else logg("!ScanOnAccess: /dev/clamauth: %s\n", cli_strerror(errno, err, sizeof(err))); return NULL; } while(1) { if(read(cauth_fd, &event, sizeof(event)) > 0) { if(eventcnt == 1) { if(event.action != SUPPORTED_PROTOCOL) { logg("!ScanOnAccess: Protocol version mismatch (tool: %d, driver: %d)\n", SUPPORTED_PROTOCOL, event.action); close(cauth_fd); return NULL; } if(strncmp(event.path, "ClamAuth", 8)) { logg("!ScanOnAccess: Invalid version event\n"); close(cauth_fd); return NULL; } logg("ScanOnAccess: Driver version: %s, protocol version: %d\n", &event.path[9], event.action); } else { cauth_scanfile(event.path, extinfo, tharg); } eventcnt++; } else { if(errno == ENODEV) { printf("^ScanOnAccess: ClamAuth module deactivated, terminating\n"); close(cauth_fd); return NULL; } } usleep(200); } }
void *onas_fan_th(void *arg) { struct thrarg *tharg = (struct thrarg *) arg; sigset_t sigset; struct sigaction act; const struct optstruct *pt; short int scan; int sizelimit = 0, extinfo; STATBUF sb; uint64_t fan_mask = FAN_EVENT_ON_CHILD | FAN_CLOSE; fd_set rfds; char buf[4096]; ssize_t bread; struct fanotify_event_metadata *fmd; char fname[1024]; int ret, len; char err[128]; pthread_attr_t ddd_attr; struct ddd_thrarg *ddd_tharg = NULL; ddd_pid = 0; /* ignore all signals except SIGUSR1 */ sigfillset(&sigset); sigdelset(&sigset, SIGUSR1); /* The behavior of a process is undefined after it ignores a * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */ sigdelset(&sigset, SIGFPE); sigdelset(&sigset, SIGILL); sigdelset(&sigset, SIGSEGV); #ifdef SIGBUS sigdelset(&sigset, SIGBUS); #endif pthread_sigmask(SIG_SETMASK, &sigset, NULL); memset(&act, 0, sizeof(struct sigaction)); act.sa_handler = onas_fan_exit; sigfillset(&(act.sa_mask)); sigaction(SIGUSR1, &act, NULL); sigaction(SIGSEGV, &act, NULL); /* Initialize fanotify */ onas_fan_fd = fanotify_init(FAN_CLASS_CONTENT | FAN_UNLIMITED_QUEUE | FAN_UNLIMITED_MARKS, O_LARGEFILE | O_RDONLY); if(onas_fan_fd < 0) { logg("!ScanOnAccess: fanotify_init failed: %s\n", cli_strerror(errno, err, sizeof(err))); if(errno == EPERM) logg("ScanOnAccess: clamd must be started by root\n"); return NULL; } if (!tharg) { logg("!Unable to start on-access scanner. Bad thread args.\n"); return NULL; } if (optget(tharg->opts, "OnAccessPrevention")->enabled && !optget(tharg->opts, "OnAccessMountPath")->enabled) { logg("ScanOnAccess: preventing access attempts on malicious files.\n"); fan_mask |= FAN_ACCESS_PERM | FAN_OPEN_PERM; } else { logg("ScanOnAccess: notifying only for access attempts.\n"); fan_mask |= FAN_ACCESS | FAN_OPEN; } if ((pt = optget(tharg->opts, "OnAccessMountPath"))->enabled) { while(pt) { if(fanotify_mark(onas_fan_fd, FAN_MARK_ADD | FAN_MARK_MOUNT, fan_mask, onas_fan_fd, pt->strarg) != 0) { logg("!ScanOnAccess: Can't include mountpoint '%s'\n", pt->strarg); return NULL; } else logg("ScanOnAccess: Protecting '%s' and rest of mount.\n", pt->strarg); pt = (struct optstruct *) pt->nextarg; } } else if (!optget(tharg->opts, "OnAccessDisableDDD")->enabled) { do { if(pthread_attr_init(&ddd_attr)) break; pthread_attr_setdetachstate(&ddd_attr, PTHREAD_CREATE_JOINABLE); if(!(ddd_tharg = (struct ddd_thrarg *) malloc(sizeof(struct ddd_thrarg)))) break; ddd_tharg->fan_fd = onas_fan_fd; ddd_tharg->fan_mask = fan_mask; ddd_tharg->opts = tharg->opts; ddd_tharg->engine = tharg->engine; ddd_tharg->options = tharg->options; if(!pthread_create(&ddd_pid, &ddd_attr, onas_ddd_th, ddd_tharg)) break; free(ddd_tharg); ddd_tharg=NULL; } while(0); if (!ddd_tharg) logg("!Unable to start dynamic directory determination.\n"); } else { if((pt = optget(tharg->opts, "OnAccessIncludePath"))->enabled) { while(pt) { if(fanotify_mark(onas_fan_fd, FAN_MARK_ADD, fan_mask, onas_fan_fd, pt->strarg) != 0) { logg("!ScanOnAccess: Can't include path '%s'\n", pt->strarg); return NULL; } else logg("ScanOnAccess: Protecting directory '%s'\n", pt->strarg); pt = (struct optstruct *) pt->nextarg; } } else { logg("!ScanOnAccess: Please specify at least one path with OnAccessIncludePath\n"); return NULL; } } /* Load other options. */ sizelimit = optget(tharg->opts, "OnAccessMaxFileSize")->numarg; if(sizelimit) logg("ScanOnAccess: Max file size limited to %d bytes\n", sizelimit); else logg("ScanOnAccess: File size limit disabled\n"); extinfo = optget(tharg->opts, "ExtendedDetectionInfo")->enabled; FD_ZERO(&rfds); FD_SET(onas_fan_fd, &rfds); do { if (reload) sleep(1); ret = select(onas_fan_fd + 1, &rfds, NULL, NULL, NULL); } while((ret == -1 && errno == EINTR) || reload); time_t start = time(NULL) - 30; while(((bread = read(onas_fan_fd, buf, sizeof(buf))) > 0) || errno == EOVERFLOW) { if (errno == EOVERFLOW) { if (time(NULL) - start >= 30) { logg("!ScanOnAccess: Internal error (failed to read data) ... %s\n", strerror(errno)); logg("!ScanOnAccess: File too large for fanotify ... recovering and continuing scans...\n"); start = time(NULL); } errno = 0; continue; } fmd = (struct fanotify_event_metadata *) buf; while(FAN_EVENT_OK(fmd, bread)) { scan = 1; if(fmd->fd >= 0) { sprintf(fname, "/proc/self/fd/%d", fmd->fd); len = readlink(fname, fname, sizeof(fname) - 1); if(len == -1) { close(fmd->fd); logg("!ScanOnAccess: Internal error (readlink() failed)\n"); return NULL; } fname[len] = 0; if(onas_fan_checkowner(fmd->pid, tharg->opts)) { scan = 0; logg("*ScanOnAccess: %s skipped (excluded UID)\n", fname); } if(sizelimit) { if(FSTAT(fmd->fd, &sb) != 0 || sb.st_size > sizelimit) { scan = 0; /* logg("*ScanOnAccess: %s skipped (size > %d)\n", fname, sizelimit); */ } } if(onas_fan_scanfile(onas_fan_fd, fname, fmd, scan, extinfo, tharg) == -1) { close(fmd->fd); return NULL; } if(close(fmd->fd) == -1) { printf("!ScanOnAccess: Internal error (close(%d) failed)\n", fmd->fd); close(fmd->fd); return NULL; } } fmd = FAN_EVENT_NEXT(fmd, bread); } do { if (reload) sleep(1); ret = select(onas_fan_fd + 1, &rfds, NULL, NULL, NULL); } while((ret == -1 && errno == EINTR) || reload); } if(bread < 0) logg("!ScanOnAccess: Internal error (failed to read data) ... %s\n", strerror(errno)); return NULL; }
/* Given mish data, reconstruct the partition details */ static int dmg_handle_mish(cli_ctx *ctx, unsigned int mishblocknum, char *dir, uint64_t xmlOffset, struct dmg_mish_with_stripes *mish_set) { struct dmg_block_data *blocklist = mish_set->stripes; uint64_t totalSectors = 0; uint32_t i; unsigned long projected_size; int ret = CL_CLEAN, ofd; uint8_t sorted = 1, writeable_data = 0; char outfile[NAME_MAX + 1]; /* First loop, fix endian-ness and check if already sorted */ for (i = 0; i < mish_set->mish->blockDataCount; i++) { blocklist[i].type = be32_to_host(blocklist[i].type); // blocklist[i].reserved = be32_to_host(blocklist[i].reserved); blocklist[i].startSector = be64_to_host(blocklist[i].startSector); blocklist[i].sectorCount = be64_to_host(blocklist[i].sectorCount); blocklist[i].dataOffset = be64_to_host(blocklist[i].dataOffset); blocklist[i].dataLength = be64_to_host(blocklist[i].dataLength); cli_dbgmsg("mish %u stripe " STDu32 " type " STDx32 " start " STDu64 " count " STDu64 " source " STDu64 " length " STDu64 "\n", mishblocknum, i, blocklist[i].type, blocklist[i].startSector, blocklist[i].sectorCount, blocklist[i].dataOffset, blocklist[i].dataLength); if ((blocklist[i].dataOffset > xmlOffset) || (blocklist[i].dataOffset + blocklist[i].dataLength > xmlOffset)) { cli_dbgmsg("dmg_handle_mish: invalid stripe offset and/or length\n"); return CL_EFORMAT; } if ((i > 0) && sorted && (blocklist[i].startSector < blocklist[i-1].startSector)) { cli_dbgmsg("dmg_handle_mish: stripes not in order, will have to sort\n"); sorted = 0; } if (dmg_track_sectors(&totalSectors, &writeable_data, i, blocklist[i].type, blocklist[i].sectorCount)) { /* reason was logged from dmg_track_sector_count */ return CL_EFORMAT; } } if (!sorted) { cli_qsort(blocklist, mish_set->mish->blockDataCount, sizeof(struct dmg_block_data), cmp_mish_stripes); } cli_dbgmsg("dmg_handle_mish: stripes in order!\n"); /* Size checks */ if ((writeable_data == 0) || (totalSectors == 0)) { cli_dbgmsg("dmg_handle_mish: no data to output\n"); return CL_CLEAN; } else if (totalSectors > (ULONG_MAX / DMG_SECTOR_SIZE)) { /* cli_checklimits only takes unsigned long for now */ cli_warnmsg("dmg_handle_mish: mish block %u too big to handle (for now)", mishblocknum); return CL_CLEAN; } projected_size = (unsigned long)(totalSectors * DMG_SECTOR_SIZE); ret = cli_checklimits("cli_scandmg", ctx, projected_size, 0, 0); if (ret != CL_CLEAN) { /* limits exceeded */ cli_dbgmsg("dmg_handle_mish: skipping block %u, limits exceeded\n", mishblocknum); return ret; } /* Prepare for file */ snprintf(outfile, sizeof(outfile)-1, "%s"PATHSEP"dmg%02u", dir, mishblocknum); outfile[sizeof(outfile)-1] = '\0'; ofd = open(outfile, O_RDWR|O_CREAT|O_EXCL|O_TRUNC|O_BINARY, 0600); if (ofd < 0) { char err[128]; cli_errmsg("cli_scandmg: Can't create temporary file %s: %s\n", outfile, cli_strerror(errno, err, sizeof(err))); return CL_ETMPFILE; } cli_dbgmsg("dmg_handle_mish: extracting block %u to %s\n", mishblocknum, outfile); /* Push data, stripe by stripe */ for(i=0; i < mish_set->mish->blockDataCount && ret == CL_CLEAN; i++) { switch (blocklist[i].type) { case DMG_STRIPE_EMPTY: case DMG_STRIPE_ZEROES: ret = dmg_stripe_zeroes(ctx, ofd, i, mish_set); break; case DMG_STRIPE_STORED: ret = dmg_stripe_store(ctx, ofd, i, mish_set); break; case DMG_STRIPE_ADC: ret = dmg_stripe_adc(ctx, ofd, i, mish_set); break; case DMG_STRIPE_DEFLATE: ret = dmg_stripe_inflate(ctx, ofd, i, mish_set); break; case DMG_STRIPE_BZ: ret = dmg_stripe_bzip(ctx, ofd, i, mish_set); break; case DMG_STRIPE_SKIP: case DMG_STRIPE_END: default: cli_dbgmsg("dmg_handle_mish: stripe " STDu32 ", skipped\n", i); break; } } /* If okay so far, scan rebuilt partition */ if (ret == CL_CLEAN) { ret = cli_partition_scandesc(ofd, ctx); } close(ofd); if (!ctx->engine->keeptmp) if (cli_unlink(outfile)) return CL_EUNLINK; return ret; }
int main( int narg, char **opts) { int rc = RC_NORMAL, opt_on = 1, sysrc, sock, errlen, listen_len, log_fd; struct sockaddr *listen = 0; char *chrc = 0, *err_msg = 0, *st = 0; char display_ip[ IP_DISPLAY_SIZE]; struct task_details *plan = 0; void *s_addr; /* --- */ plan = figure_out_what_to_do( &rc, narg, opts); if( narg < 2) plan->show_help = 1; /* --- */ if( rc == RC_NORMAL) { if( plan->debug >= DEBUG_LOW) fprintf( stderr, "\nPlan: server(%s) port(%d) ipv4(%d) ipv6(%d) user(%s) log(%s) mode(%d)\n", plan->target_host, plan->target_port, plan->use_ip & DO_IPV4, plan->use_ip & DO_IPV6, plan->runuser, plan->logfile, plan->logmode); } if( plan->show_help) { st = opts[ 0]; if( *st == '.' && *(st + 1) == '/') st += 2; printf( MSG_SHOW_SYNTAX, st); exit( 1); } /* --- */ if( rc == RC_NORMAL) { rc = get_destination_ip( plan); if( rc == RC_NORMAL && !plan->found_family) { rc = ERR_GETHOST_FAILED; errlen = strlen( ERRMSG_GETHOST_FAILED) + strlen( plan->target_host); plan->err_msg = (char *) malloc( errlen); if( !plan->err_msg) rc = ERR_MALLOC_FAILED; else snprintf( plan->err_msg, errlen, ERRMSG_GETHOST_FAILED, plan->target_host); } } if( rc == RC_NORMAL) { if( plan->found_family == AF_INET) { plan->dest4.sin_port = htons( plan->target_port); listen = (struct sockaddr *) &plan->dest4; listen_len = (sizeof plan->dest4); s_addr = &plan->dest4.sin_addr; } else { plan->dest6.sin6_port = htons( plan->target_port); listen = (struct sockaddr *) &plan->dest6; listen_len = (sizeof plan->dest6); s_addr = &plan->dest6.sin6_addr; } chrc = (char *) inet_ntop( plan->found_family, s_addr, display_ip, IP_DISPLAY_SIZE); if( !chrc) { rc = ERR_SYS_CALL; plan->err_msg = build_syscall_errmsg( "inet_ntop", errno); if( !plan->err_msg) rc = ERR_MALLOC_FAILED; } else if( plan->debug >= DEBUG_LOW) fprintf( stderr, "Server(%s) IP(%s)\n", plan->target_host, display_ip); } if( rc == RC_NORMAL) { if( plan->debug >= DEBUG_NOISY) fprintf( stderr, "ff:%d type:%d\n", plan->found_family, SOCK_DGRAM); sock = socket( plan->found_family, SOCK_DGRAM, 0); if( sock == -1) { rc = ERR_SYS_CALL; plan->err_msg = build_syscall_errmsg( "socket", errno); if( !plan->err_msg) rc = ERR_MALLOC_FAILED; } else if( plan->found_family == AF_INET6) { sysrc = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt_on, (sizeof opt_on)); #ifndef __APPLE__ if( !sysrc) sysrc = setsockopt( sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &opt_on, (sizeof opt_on)); if( !sysrc) sysrc = setsockopt( sock, IPPROTO_IPV6, IPV6_RECVHOPOPTS, &opt_on, (sizeof opt_on)); if( !sysrc) sysrc = setsockopt( sock, IPPROTO_IPV6, IPV6_RECVRTHDR, &opt_on, (sizeof opt_on)); if( !sysrc) sysrc = setsockopt( sock, IPPROTO_IPV6, IPV6_RECVDSTOPTS, &opt_on, (sizeof opt_on)); #endif if( sysrc) { rc = ERR_SYS_CALL; plan->err_msg = build_syscall_errmsg( "setsockopt", errno); if( !plan->err_msg) rc = ERR_MALLOC_FAILED; } } } if( rc == RC_NORMAL) { sysrc = bind( sock, listen, listen_len); if( sysrc == -1) { rc = ERR_SYS_CALL; plan->err_msg = build_syscall_errmsg( "bind", errno); if( !plan->err_msg) rc = ERR_MALLOC_FAILED; } } if( rc == RC_NORMAL) rc = switch_user_and_group( plan); if( rc == RC_NORMAL) rc = open_logfile( &log_fd, plan); if( rc == RC_NORMAL) rc = receive_udp_and_log( plan, sock, log_fd); /* --- */ if( rc != RC_NORMAL) { fprintf( stderr, "Error, rc=%d\n", rc); if( !plan) err_msg = cli_strerror( rc); else if( !plan->err_msg) err_msg = cli_strerror( rc); else err_msg = plan->err_msg; fprintf( stderr, "%s\n", err_msg); if( errno) fprintf( stderr, "System error message: %s\n", strerror( errno)); } exit( rc); }
sfsistat clamfi_eom(SMFICTX *ctx) { struct CLAMFI *cf; char *reply; int len, ret; unsigned int crcpt; if(!(cf = (struct CLAMFI *)smfi_getpriv(ctx))) return SMFIS_CONTINUE; /* whatever */ if(!cf->totsz) { /* got no headers and no body */ logg("*Not scanning an empty message\n"); ret = CleanAction(ctx); nullify(ctx, cf, CF_NONE); free(cf); return ret; } if(cf->local) { lseek(cf->alt, 0, SEEK_SET); if(nc_sendmsg(cf->main, cf->alt) == -1) { logg("!FD send failed\n"); nullify(ctx, cf, CF_ALT); free(cf); return FailAction; } } else { uint32_t sendmetoo = 0; cf->sendme = htonl(cf->bufsz); if((cf->bufsz && nc_send(cf->main, &cf->sendme, cf->bufsz + 4)) || nc_send(cf->main, &sendmetoo, 4)) { logg("!Failed to flush STREAM\n"); nullify(ctx, cf, CF_NONE); free(cf); return FailAction; } } reply = nc_recv(cf->main); if(cf->local) close(cf->alt); cf->alt = -1; if(!reply) { logg("!No reply from clamd\n"); nullify(ctx, cf, CF_NONE); free(cf); return FailAction; } len = strlen(reply); if(len>5 && !strcmp(reply + len - 5, ": OK\n")) { if(addxvirus) add_x_header(ctx, "Clean", cf->scanned_count, cf->status_count); if(loginfected & LOGCLN_FULL) { const char *id = smfi_getsymval(ctx, "{i}"); const char *from = smfi_getsymval(ctx, "{mail_addr}"); const char *msg_subj = makesanehdr(cf->msg_subj); const char *msg_date = makesanehdr(cf->msg_date); const char *msg_id = makesanehdr(cf->msg_id); if(multircpt && cf->nrecipients) { for(crcpt = 0; crcpt < cf->nrecipients; crcpt++) logg("~Clean message %s from <%s> to <%s> with subject '%s' message-id '%s' date '%s'\n", id, from, cf->recipients[crcpt], msg_subj, msg_id, msg_date); } else { const char *to = smfi_getsymval(ctx, "{rcpt_addr}"); logg("~Clean message %s from <%s> to <%s> with subject '%s' message-id '%s' date '%s'\n", id, from, to ? to : HDR_UNAVAIL, msg_subj, msg_id, msg_date); } } else if(loginfected & LOGCLN_BASIC) { const char *from = smfi_getsymval(ctx, "{mail_addr}"); if(multircpt && cf->nrecipients) { for(crcpt = 0; crcpt < cf->nrecipients; crcpt++) logg("~Clean message from <%s> to <%s>\n", from, cf->recipients[crcpt]); } else { const char *to = smfi_getsymval(ctx, "{rcpt_addr}"); logg("~Clean message from <%s> to <%s>\n", from, to ? to : HDR_UNAVAIL); } } ret = CleanAction(ctx); } else if (len>7 && !strcmp(reply + len - 7, " FOUND\n")) { cf->virusname = NULL; if((loginfected & (LOGINF_BASIC | LOGINF_FULL)) || addxvirus || rejectfmt || viraction) { char *vir; reply[len-7] = '\0'; vir = strrchr(reply, ' '); if(vir) { unsigned int have_multi = (multircpt != 0 && cf->nrecipients); unsigned int lst_rcpt = (have_multi * (cf->nrecipients - 1)) + 1; vir++; if(rejectfmt) cf->virusname = vir; if(addxvirus) { char msg[255]; snprintf(msg, sizeof(msg), "Infected (%s)", vir); msg[sizeof(msg)-1] = '\0'; add_x_header(ctx, msg, cf->scanned_count, cf->status_count); } for(crcpt = 0; crcpt < lst_rcpt; crcpt++) { if(loginfected || viraction) { const char *from = smfi_getsymval(ctx, "{mail_addr}"); const char *to = have_multi ? cf->recipients[crcpt] : smfi_getsymval(ctx, "{rcpt_addr}"); if(!from) from = HDR_UNAVAIL; if(!to) to = HDR_UNAVAIL; if((loginfected & LOGINF_FULL) || viraction) { const char *id = smfi_getsymval(ctx, "{i}"); const char *msg_subj = makesanehdr(cf->msg_subj); const char *msg_date = makesanehdr(cf->msg_date); const char *msg_id = makesanehdr(cf->msg_id); if(!id) id = HDR_UNAVAIL; if(loginfected & LOGINF_FULL) logg("~Message %s from <%s> to <%s> with subject '%s' message-id '%s' date '%s' infected by %s\n", id, from, to, msg_subj, msg_id, msg_date, vir); if(viraction) { char er[256]; char *e_id = strdup(id); char *e_from = strdup(from); char *e_to = strdup(to); char *e_msg_subj = strdup(msg_subj); char *e_msg_date = strdup(msg_date); char *e_msg_id = strdup(msg_id); pid_t pid; logg("*VirusEvent: about to execute '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s'\n", viraction, vir, e_id, e_from, e_to, e_msg_subj, e_msg_id, e_msg_date); pthread_mutex_lock(&virusaction_lock); pid = fork(); if(!pid) { char * args[9]; /* avoid element is not computable at load time warns */ args[0]= viraction; args[1] = vir; args[2] = e_id; args[3] = e_from; args[4] = e_to; args[5] = e_msg_subj; args[6] = e_msg_id; args[7] = e_msg_date; args[8] = NULL; exit(execvp(viraction, args)); } else if(pid > 0) { int wret; pthread_mutex_unlock(&virusaction_lock); while((wret = waitpid(pid, &ret, 0)) == -1 && errno == EINTR); if(wret<0) logg("!VirusEvent: waitpid() failed: %s\n", cli_strerror(errno, er, sizeof(er))); else { if(WIFEXITED(ret)) logg("*VirusEvent: child exited with code %d\n", WEXITSTATUS(ret)); else if(WIFSIGNALED(ret)) logg("*VirusEvent: child killed by signal %d\n", WTERMSIG(ret)); else logg("*VirusEvent: child lost\n"); } } else { logg("!VirusEvent: fork failed: %s\n", cli_strerror(errno, er, sizeof(er))); } free(e_id); free(e_from); free(e_to); free(e_msg_subj); free(e_msg_date); free(e_msg_id); } } if(loginfected & LOGINF_BASIC) logg("~Message from <%s> to <%s> infected by %s\n", from, to, vir); } } } } ret = InfectedAction(ctx); } else { logg("!Unknown reply from clamd\n"); ret = FailAction; } nullify(ctx, cf, CF_MAIN); free(cf); free(reply); return ret; }
void *fan_th(void *arg) { struct thrarg *tharg = (struct thrarg *) arg; sigset_t sigset; struct sigaction act; const struct optstruct *pt; short int scan; int sizelimit = 0, extinfo; STATBUF sb; uint64_t fan_mask = FAN_ACCESS | FAN_EVENT_ON_CHILD; int fan_fd; fd_set rfds; char buf[4096]; ssize_t bread; struct fanotify_event_metadata *fmd; char fname[1024]; int ret, len; char err[128]; /* ignore all signals except SIGUSR1 */ sigfillset(&sigset); sigdelset(&sigset, SIGUSR1); /* The behavior of a process is undefined after it ignores a * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */ sigdelset(&sigset, SIGFPE); sigdelset(&sigset, SIGILL); sigdelset(&sigset, SIGSEGV); #ifdef SIGBUS sigdelset(&sigset, SIGBUS); #endif pthread_sigmask(SIG_SETMASK, &sigset, NULL); memset(&act, 0, sizeof(struct sigaction)); act.sa_handler = fan_exit; sigfillset(&(act.sa_mask)); sigaction(SIGUSR1, &act, NULL); sigaction(SIGSEGV, &act, NULL); fan_fd = fanotify_init(0, O_RDONLY); if(fan_fd < 0) { logg("!ScanOnAccess: fanotify_init failed: %s\n", cli_strerror(errno, err, sizeof(err))); if(errno == EPERM) logg("ScanOnAccess: clamd must be started by root\n"); return NULL; } if((pt = optget(tharg->opts, "OnAccessIncludePath"))->enabled) { while(pt) { if(fanotify_mark(fan_fd, FAN_MARK_ADD, fan_mask, fan_fd, pt->strarg) != 0) { logg("!ScanOnAccess: Can't include path '%s'\n", pt->strarg); return NULL; } else logg("ScanOnAccess: Protecting directory '%s'\n", pt->strarg); pt = (struct optstruct *) pt->nextarg; } } else { logg("!ScanOnAccess: Please specify at least one path with OnAccessIncludePath\n"); return NULL; } if((pt = optget(tharg->opts, "OnAccessExcludePath"))->enabled) { while(pt) { if(fanotify_mark(fan_fd, FAN_MARK_REMOVE, fan_mask, fan_fd, pt->strarg) != 0) { logg("!ScanOnAccess: Can't exclude path %s\n", pt->strarg); return NULL; } else logg("ScanOnAccess: Excluded path %s\n", pt->strarg); pt = (struct optstruct *) pt->nextarg; } } sizelimit = optget(tharg->opts, "OnAccessMaxFileSize")->numarg; if(sizelimit) logg("ScanOnAccess: Max file size limited to %d bytes\n", sizelimit); else logg("ScanOnAccess: File size limit disabled\n"); extinfo = optget(tharg->opts, "ExtendedDetectionInfo")->enabled; FD_ZERO(&rfds); FD_SET(fan_fd, &rfds); do { ret = select(fan_fd + 1, &rfds, NULL, NULL, NULL); } while(ret == -1 && errno == EINTR); while((bread = read(fan_fd, buf, sizeof(buf))) > 0) { fmd = (struct fanotify_event_metadata *) buf; while(FAN_EVENT_OK(fmd, bread)) { scan = 1; if(fmd->fd >= 0) { sprintf(fname, "/proc/self/fd/%d", fmd->fd); len = readlink(fname, fname, sizeof(fname) - 1); if(len == -1) { close(fmd->fd); logg("!ScanOnAccess: Internal error (readlink() failed)\n"); return NULL; } fname[len] = 0; if(fan_checkowner(fmd->pid, tharg->opts)) { scan = 0; logg("*ScanOnAccess: %s skipped (excluded UID)\n", fname); } if(sizelimit) { if(FSTAT(fmd->fd, &sb) != 0 || sb.st_size > sizelimit) { scan = 0; /* logg("*ScanOnAccess: %s skipped (size > %d)\n", fname, sizelimit); */ } } if(fan_scanfile(fan_fd, fname, fmd, scan, extinfo, tharg) == -1) { close(fmd->fd); return NULL; } if(close(fmd->fd) == -1) { printf("!ScanOnAccess: Internal error (close(%d) failed)\n", fmd->fd); close(fmd->fd); return NULL; } } fmd = FAN_EVENT_NEXT(fmd, bread); } do { ret = select(fan_fd + 1, &rfds, NULL, NULL, NULL); } while(ret == -1 && errno == EINTR); } if(bread < 0) logg("!ScanOnAccess: Internal error (failed to read data)\n"); return NULL; }