END_TEST START_TEST (test_cl_scandesc_callback_allscan) { const char *virname = NULL; const char **virpp = &virname; char file[256]; unsigned long size; unsigned long int scanned = 0; int ret; int fd = get_test_file(_i, file, sizeof(file), &size); cli_dbgmsg("scanning (scandesc_cb_allscan) %s\n", file); /* TODO: test callbacks */ ret = cl_scandesc_callback(fd, virpp, &scanned, g_engine, CL_SCAN_ALLMATCHES+CL_SCAN_STDOPT, NULL); cli_dbgmsg("scan end (scandesc_cb_allscan) %s\n", file); if (!FALSE_NEGATIVE) { fail_unless_fmt(ret == CL_VIRUS, "cl_scanfile_allscan failed for %s: %s", file, cl_strerror(ret)); virpp = (const char **)*virpp; /* allscan api hack */ fail_unless_fmt(*virpp && !strcmp(*virpp, "ClamAV-Test-File.UNOFFICIAL"), "virusname: %s", *virpp); free((void *)virpp); } close(fd); }
static int onas_fan_scanfile(int fan_fd, const char *fname, struct fanotify_event_metadata *fmd, int scan, int extinfo, struct thrarg *tharg) { struct fanotify_response res; struct cb_context context; const char *virname; int ret = 0; res.fd = fmd->fd; res.response = FAN_ALLOW; context.filename = fname; context.virsize = 0; context.scandata = NULL; if(scan && cl_scandesc_callback(fmd->fd, &virname, NULL, tharg->engine, tharg->options, &context) == CL_VIRUS) { if(extinfo && context.virsize) logg("ScanOnAccess: %s: %s(%s:%llu) FOUND\n", fname, virname, context.virhash, context.virsize); else logg("ScanOnAccess: %s: %s FOUND\n", fname, virname); virusaction(fname, virname, tharg->opts); res.response = FAN_DENY; } if(fmd->mask & FAN_ALL_PERM_EVENTS) { ret = write(fan_fd, &res, sizeof(res)); if(ret == -1) logg("!ScanOnAccess: Internal error (can't write to fanotify)\n"); } return ret; }
static int cauth_scanfile(const char *fname, int extinfo, struct thrarg *tharg) { struct cb_context context; const char *virname; int ret = 0, fd; context.filename = fname; context.virsize = 0; fd = open(fname, O_RDONLY); if(fd == -1) return -1; if(cl_scandesc_callback(fd, &virname, NULL, tharg->engine, tharg->options, &context) == CL_VIRUS) { if(context.virsize) detstats_add(virname, fname, context.virsize, context.virhash); if(extinfo && context.virsize) logg("ClamAuth: %s: %s(%s:%llu) FOUND\n", fname, virname, context.virhash, context.virsize); else logg("ClamAuth: %s: %s FOUND\n", fname, virname); virusaction(fname, virname, tharg->opts); } close(fd); return ret; }
END_TEST START_TEST (test_cl_scandesc_callback) { const char *virname = NULL; char file[256]; unsigned long size; unsigned long int scanned = 0; int ret; int fd = get_test_file(_i, file, sizeof(file), &size); cli_dbgmsg("scanning (scandesc_cb) %s\n", file); /* TODO: test callbacks */ ret = cl_scandesc_callback(fd, &virname, &scanned, g_engine, CL_SCAN_STDOPT, NULL); cli_dbgmsg("scan end (scandesc_cb) %s\n", file); if (!FALSE_NEGATIVE) { fail_unless_fmt(ret == CL_VIRUS, "cl_scanfile failed for %s: %s", file, cl_strerror(ret)); fail_unless_fmt(virname && !strcmp(virname, "ClamAV-Test-File.UNOFFICIAL"), "virusname: %s", virname); } close(fd); }
int CLAMAPI Scan_ScanObjectByHandle(CClamAVScanner *pScanner, HANDLE object, int *pScanStatus, PCLAM_SCAN_INFO_LIST *pInfoList) { instance *inst; HANDLE duphdl, self; char *virname = NULL; int fd, res; unsigned int i; struct scan_ctx sctx; DWORD perf; logg("*in Scan_ScanObjectByHandle(pScanner = %p, HANDLE = %p, pScanStatus = %p, pInfoList = %p)\n", pScanner, object, pScanStatus, pInfoList); if(!pScanner) FAIL(CL_ENULLARG, "NULL pScanner"); if(!pScanStatus) FAIL(CL_ENULLARG, "NULL pScanStatus on instance %p", pScanner); self = GetCurrentProcess(); if(!DuplicateHandle(self, object, self, &duphdl, GENERIC_READ, FALSE, 0)) FAIL(CL_EDUP, "Duplicate handle failed for instance %p", pScanner); if((fd = _open_osfhandle((intptr_t)duphdl, _O_RDONLY)) == -1) { CloseHandle(duphdl); FAIL(CL_EOPEN, "Open handle failed for instance %p", pScanner); } if(lock_instances()) { close(fd); FAIL(CL_ELOCK, "failed to lock instances for instance %p", pScanner); } inst = (instance *)pScanner; for(i=0; i<ninsts_total; i++) { if(instances[i].inst == inst) break; } if(i == ninsts_total) { unlock_instances(); close(fd); FAIL(CL_EARG, "invalid instance %p", inst); } instances[i].refcnt++; ResetEvent(reload_event); unlock_instances(); sctx.entryfd = fd; sctx.inst = inst; sctx.cb_times = 0; sctx.copy_times = 0; logg("*Scan_ScanObjectByHandle (instance %p) invoking cl_scandesc with clamav context %p\n", inst, &sctx); perf = GetTickCount(); res = cl_scandesc_callback(fd, &virname, NULL, engine, inst->scanopts, &sctx); if(!inst->filetype) do { CLAM_SCAN_INFO si; CLAM_ACTION act; DWORD cbperf; wchar_t wvirname[MAX_VIRNAME_LEN] = L"Clam."; LONG lo = 0, hi = 0, hi2 = 0; si.cbSize = sizeof(si); si.flags = 0; si.scanPhase = SCAN_PHASE_FINAL; si.errorCode = CLAMAPI_SUCCESS; if(res == CL_VIRUS) { if(MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, virname, -1, &wvirname[5], MAX_VIRNAME_LEN - 5)) si.pThreatName = wvirname; else si.pThreatName = L"Clam.UNOFFICIAL"; } else si.pThreatName = NULL; logg("*in final_cb with clamav context %p, instance %p, fd %d, result %d, virusname %S)\n", &sctx, inst, fd, res, si.pThreatName); si.pThreatType = threat_type(virname); si.object = INVALID_HANDLE_VALUE; si.objectId = INVALID_HANDLE_VALUE; si.pInnerObjectPath = NULL; lo = SetFilePointer(duphdl, 0, &hi, FILE_CURRENT); SetFilePointer(duphdl, 0, &hi2, FILE_BEGIN); logg("*final_cb (clamav context %p, instance %p) invoking callback %p with context %p\n", &sctx, inst, inst->scancb, inst->scancb_ctx); cbperf = GetTickCount(); inst->scancb(&si, &act, inst->scancb_ctx); cbperf = GetTickCount() - cbperf; sctx.cb_times += cbperf; logg("*final_cb (clamav context %p, instance %p) callback completed with %u (result ignored) in %u ms\n", &sctx, inst, act, cbperf); SetFilePointer(duphdl, lo, &hi, FILE_BEGIN); } while(0); perf = GetTickCount() - perf; close(fd); logg("*Scan_ScanObjectByHandle (instance %p): cl_scandesc returned %d in %u ms (%d ms own, %d ms copy)\n", inst, res, perf, perf - sctx.cb_times - sctx.copy_times, sctx.copy_times); if(lock_instances()) FAIL(CL_ELOCK, "failed to lock instances for instance %p", pScanner); instances[i].refcnt--; if(!instances[i].refcnt) SetEvent(reload_event); unlock_instances(); if(res == CL_VIRUS) { logg("Scan_ScanObjectByHandle (instance %p): file is INFECTED with %s\n", inst, virname); if(pInfoList) { CLAM_SCAN_INFO_LIST *infolist = (CLAM_SCAN_INFO_LIST *)calloc(1, sizeof(CLAM_SCAN_INFO_LIST) + sizeof(CLAM_SCAN_INFO) + MAX_VIRNAME_LEN * 2); PCLAM_SCAN_INFO scaninfo; wchar_t *wvirname; if(!infolist) FAIL(CL_EMEM, "ScanByHandle (instance %p): OOM while allocating result list", inst); scaninfo = (PCLAM_SCAN_INFO)(infolist + 1); infolist->cbCount = 1; scaninfo->cbSize = sizeof(*scaninfo); scaninfo->scanPhase = SCAN_PHASE_FINAL; scaninfo->errorCode = CLAMAPI_SUCCESS; scaninfo->pThreatType = threat_type(virname); scaninfo->object = INVALID_HANDLE_VALUE; scaninfo->objectId = INVALID_HANDLE_VALUE; wvirname = (wchar_t *)(scaninfo + 1); scaninfo->pThreatName = wvirname; memcpy(wvirname, L"Clam.", 10); if(!MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, virname, -1, &wvirname[5], MAX_VIRNAME_LEN-5)) scaninfo->pThreatName = L"Clam.UNOFFICIAL"; *pInfoList = infolist; logg("*Scan_ScanObjectByHandle (instance %p): created result list %p\n", inst, infolist); } *pScanStatus = CLAM_INFECTED; } else if(res == CL_CLEAN) { logg("*Scan_ScanObjectByHandle (instance %p): file is CLEAN\n", inst); if(pInfoList) *pInfoList = NULL; *pScanStatus = CLAM_CLEAN; } else { FAIL(res, "Scan failed for instance %p: %s", inst, cl_strerror(res)); } WIN(); }
int scanstream(int odesc, unsigned long int *scanned, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts, char term) { int ret, sockfd, acceptd; int tmpd, bread, retval, firsttimeout, timeout, btread; unsigned int port = 0, portscan, min_port, max_port; unsigned long int quota = 0, maxsize = 0; short bound = 0; const char *virname; char buff[FILEBUFF]; char peer_addr[32]; struct cb_context context; struct sockaddr_in server; struct sockaddr_in peer; socklen_t addrlen; char *tmpname; min_port = optget(opts, "StreamMinPort")->numarg; max_port = optget(opts, "StreamMaxPort")->numarg; /* search for a free port to bind to */ port = cli_rndnum(max_port - min_port); bound = 0; for (portscan = 0; portscan < 1000; portscan++) { port = (port - 1) % (max_port - min_port + 1); memset((char *) &server, 0, sizeof(server)); server.sin_family = AF_INET; server.sin_port = htons(min_port + port); server.sin_addr.s_addr = htonl(INADDR_ANY); if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) continue; if(bind(sockfd, (struct sockaddr *) &server, (socklen_t)sizeof(struct sockaddr_in)) == -1) closesocket(sockfd); else { bound = 1; break; } } port += min_port; timeout = optget(opts, "ReadTimeout")->numarg; firsttimeout = optget(opts, "CommandReadTimeout")->numarg; if(!bound) { logg("!ScanStream: Can't find any free port.\n"); mdprintf(odesc, "Can't find any free port. ERROR%c", term); return -1; } else { if (listen(sockfd, 1) == -1) { logg("!ScanStream: listen() error on socket. Error returned is %s.\n", strerror(errno)); closesocket(sockfd); return -1; } if(mdprintf(odesc, "PORT %u%c", port, term) <= 0) { logg("!ScanStream: error transmitting port.\n"); closesocket(sockfd); return -1; } } retval = poll_fd(sockfd, firsttimeout, 0); if (!retval || retval == -1) { const char *reason = !retval ? "timeout" : "poll"; mdprintf(odesc, "Accept %s. ERROR%c", reason, term); logg("!ScanStream %u: accept %s.\n", port, reason); closesocket(sockfd); return -1; } addrlen = sizeof(peer); if((acceptd = accept(sockfd, (struct sockaddr *) &peer, (socklen_t *)&addrlen)) == -1) { closesocket(sockfd); mdprintf(odesc, "accept() ERROR%c", term); logg("!ScanStream %u: accept() failed.\n", port); return -1; } *peer_addr = '\0'; inet_ntop(peer.sin_family, &peer.sin_addr, peer_addr, sizeof(peer_addr)); logg("*Accepted connection from %s on port %u, fd %d\n", peer_addr, port, acceptd); if(cli_gentempfd(optget(opts, "TemporaryDirectory")->strarg, &tmpname, &tmpd)) { shutdown(sockfd, 2); closesocket(sockfd); closesocket(acceptd); mdprintf(odesc, "cli_gentempfd() failed. ERROR%c", term); logg("!ScanStream(%s@%u): Can't create temporary file.\n", peer_addr, port); return -1; } quota = maxsize = optget(opts, "StreamMaxLength")->numarg; while((retval = poll_fd(acceptd, timeout, 0)) == 1) { /* only read up to max */ btread = (maxsize && (quota < sizeof(buff))) ? quota : sizeof(buff); if (!btread) { logg("^ScanStream(%s@%u): Size limit reached (max: %lu)\n", peer_addr, port, maxsize); break; /* Scan what we have */ } bread = recv(acceptd, buff, btread, 0); if(bread <= 0) break; quota -= bread; if(writen(tmpd, buff, bread) != bread) { shutdown(sockfd, 2); closesocket(sockfd); closesocket(acceptd); mdprintf(odesc, "Temporary file -> write ERROR%c", term); logg("!ScanStream(%s@%u): Can't write to temporary file.\n", peer_addr, port); close(tmpd); if(!optget(opts, "LeaveTemporaryFiles")->enabled) unlink(tmpname); free(tmpname); return -1; } } switch(retval) { case 0: /* timeout */ mdprintf(odesc, "read timeout ERROR%c", term); logg("!ScanStream(%s@%u): read timeout.\n", peer_addr, port); break; case -1: mdprintf(odesc, "read poll ERROR%c", term); logg("!ScanStream(%s@%u): read poll failed.\n", peer_addr, port); break; } if(retval == 1) { lseek(tmpd, 0, SEEK_SET); thrmgr_setactivetask(peer_addr, NULL); context.filename = peer_addr; context.virsize = 0; context.scandata = NULL; ret = cl_scandesc_callback(tmpd, &virname, scanned, engine, options, &context); thrmgr_setactivetask(NULL, NULL); } else { ret = -1; } close(tmpd); if(!optget(opts, "LeaveTemporaryFiles")->enabled) unlink(tmpname); free(tmpname); closesocket(acceptd); closesocket(sockfd); if(ret == CL_VIRUS) { if(context.virsize && optget(opts, "ExtendedDetectionInfo")->enabled) { mdprintf(odesc, "stream: %s(%s:%llu) FOUND%c", virname, context.virhash, context.virsize, term); logg("stream(%s@%u): %s(%s:%llu) FOUND\n", peer_addr, port, virname, context.virhash, context.virsize); } else { mdprintf(odesc, "stream: %s FOUND%c", virname, term); logg("stream(%s@%u): %s FOUND\n", peer_addr, port, virname); } virusaction("stream", virname, opts); } else if(ret != CL_CLEAN) { if(retval == 1) { mdprintf(odesc, "stream: %s ERROR%c", cl_strerror(ret), term); logg("stream(%s@%u): %s ERROR\n", peer_addr, port, cl_strerror(ret)); } } else { mdprintf(odesc, "stream: OK%c", term); if(logok) logg("stream(%s@%u): OK\n", peer_addr, port); } return ret; }
int scanfd(const client_conn_t *conn, unsigned long int *scanned, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts, int odesc, int stream) { int ret, fd = conn->scanfd; const char *virname; STATBUF statbuf; struct cb_context context; char fdstr[32]; const char*reply_fdstr; UNUSEDPARAM(odesc); if (stream) { struct sockaddr_in sa; socklen_t salen = sizeof(sa); if(getpeername(conn->sd, (struct sockaddr *)&sa, &salen) || salen > sizeof(sa) || sa.sin_family != AF_INET) strncpy(fdstr, "instream(local)", sizeof(fdstr)); else snprintf(fdstr, sizeof(fdstr), "instream(%s@%u)", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port)); reply_fdstr = "stream"; } else { snprintf(fdstr, sizeof(fdstr), "fd[%d]", fd); reply_fdstr = fdstr; } if(FSTAT(fd, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) { logg("%s: Not a regular file. ERROR\n", fdstr); if (conn_reply(conn, reply_fdstr, "Not a regular file", "ERROR") == -1) return CL_ETIMEOUT; return -1; } thrmgr_setactivetask(fdstr, NULL); context.filename = fdstr; context.virsize = 0; context.scandata = NULL; ret = cl_scandesc_callback(fd, &virname, scanned, engine, options, &context); thrmgr_setactivetask(NULL, NULL); if (thrmgr_group_need_terminate(conn->group)) { logg("*Client disconnected while scanjob was active\n"); return ret == CL_ETIMEOUT ? ret : CL_BREAK; } if(ret == CL_VIRUS) { if (conn_reply_virus(conn, reply_fdstr, virname) == -1) ret = CL_ETIMEOUT; if(context.virsize && optget(opts, "ExtendedDetectionInfo")->enabled) logg("%s: %s(%s:%llu) FOUND\n", fdstr, virname, context.virhash, context.virsize); else logg("%s: %s FOUND\n", fdstr, virname); virusaction(reply_fdstr, virname, opts); } else if(ret != CL_CLEAN) { if (conn_reply(conn, reply_fdstr, cl_strerror(ret), "ERROR") == -1) ret = CL_ETIMEOUT; logg("%s: %s ERROR\n", fdstr, cl_strerror(ret)); } else { if (conn_reply_single(conn, reply_fdstr, "OK") == CL_ETIMEOUT) ret = CL_ETIMEOUT; if(logok) logg("%s: OK\n", fdstr); } return ret; }
static void scanfile(const char *filename, struct cl_engine *engine, const struct optstruct *opts, unsigned int options) { int ret = 0, fd, included; unsigned i; const struct optstruct *opt; const char *virname; STATBUF sb; struct metachain chain; struct clamscan_cb_data data; if((opt = optget(opts, "exclude"))->enabled) { while(opt) { if(match_regex(filename, opt->strarg) == 1) { if(!printinfected) logg("~%s: Excluded\n", filename); return; } opt = opt->nextarg; } } if((opt = optget(opts, "include"))->enabled) { included = 0; while(opt) { if(match_regex(filename, opt->strarg) == 1) { included = 1; break; } opt = opt->nextarg; } if(!included) { if(!printinfected) logg("~%s: Excluded\n", filename); return; } } /* argh, don't scan /proc files */ if(CLAMSTAT(filename, &sb) != -1) { #ifdef C_LINUX if(procdev && sb.st_dev == procdev) { if(!printinfected) logg("~%s: Excluded (/proc)\n", filename); return; } #endif if(!sb.st_size) { if(!printinfected) logg("~%s: Empty file\n", filename); return; } info.rblocks += sb.st_size / CL_COUNT_PRECISION; } #ifndef _WIN32 if(geteuid()) { if(checkaccess(filename, NULL, R_OK) != 1) { if(!printinfected) logg("~%s: Access denied\n", filename); info.errors++; return; } } #endif memset(&chain, 0, sizeof(chain)); if(optget(opts, "archive-verbose")->enabled) { chain.chains = malloc(sizeof(char **)); if (chain.chains) { chain.chains[0] = strdup(filename); if (!chain.chains[0]) { free(chain.chains); logg("Unable to allocate memory in scanfile()\n"); info.errors++; return; } chain.nchains = 1; } } logg("*Scanning %s\n", filename); if((fd = safe_open(filename, O_RDONLY|O_BINARY)) == -1) { logg("^Can't open file %s: %s\n", filename, strerror(errno)); info.errors++; return; } data.chain = &chain; data.filename = filename; if((ret = cl_scandesc_callback(fd, &virname, &info.blocks, engine, options, &data)) == CL_VIRUS) { if(optget(opts, "archive-verbose")->enabled) { if (chain.nchains > 1) { char str[128]; int toolong = print_chain(&chain, str, sizeof(str)); logg("~%s%s!(%llu)%s: %s FOUND\n", str, toolong ? "..." : "", (long long unsigned)(chain.lastvir-1), chain.chains[chain.nchains-1], virname); } else if (chain.lastvir) { logg("~%s!(%llu): %s FOUND\n", filename, (long long unsigned)(chain.lastvir-1), virname); } } info.files++; info.ifiles++; if(bell) fprintf(stderr, "\007"); } else if(ret == CL_CLEAN) { if(!printinfected && printclean) mprintf("~%s: OK\n", filename); info.files++; } else { if(!printinfected) logg("~%s: %s ERROR\n", filename, cl_strerror(ret)); info.errors++; } for (i=0;i<chain.nchains;i++) free(chain.chains[i]); free(chain.chains); close(fd); if(ret == CL_VIRUS && action) action(filename); }