END_TEST START_TEST (test_cl_scanfile_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); close(fd); cli_dbgmsg("scanning (scanfile_cb_allscan) %s\n", file); /* TODO: test callbacks */ ret = cl_scanfile_callback(file, virpp, &scanned, g_engine, CL_SCAN_ALLMATCHES+CL_SCAN_STDOPT, NULL); cli_dbgmsg("scan end (scanfile_cb_allscan) %s\n", file); if (!FALSE_NEGATIVE) { fail_unless_fmt(ret == CL_VIRUS, "cl_scanfile_cb_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); } }
END_TEST START_TEST (test_cl_scanfile_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); close(fd); cli_dbgmsg("scanning (scanfile_cb) %s\n", file); /* TODO: test callbacks */ ret = cl_scanfile_callback(file, &virname, &scanned, g_engine, CL_SCAN_STDOPT, NULL); cli_dbgmsg("scan end (scanfile_cb) %s\n", file); if (!FALSE_NEGATIVE) { fail_unless_fmt(ret == CL_VIRUS, "cl_scanfile_cb failed for %s: %s", file, cl_strerror(ret)); fail_unless_fmt(virname && !strcmp(virname, "ClamAV-Test-File.UNOFFICIAL"), "virusname: %s", virname); } }
int scan_callback(STATBUF *sb, char *filename, const char *msg, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data) { struct scan_cb_data *scandata = data->data; const char *virname = NULL; int ret; int type = scandata->type; struct cb_context context; /* detect disconnected socket, * this should NOT detect half-shutdown sockets (SHUT_WR) */ if (send(scandata->conn->sd, &ret, 0, 0) == -1 && errno != EINTR) { logg("$Client disconnected while command was active!\n"); thrmgr_group_terminate(scandata->conn->group); if (reason == visit_file) free(filename); return CL_BREAK; } if (thrmgr_group_need_terminate(scandata->conn->group)) { logg("^Client disconnected while scanjob was active\n"); if (reason == visit_file) free(filename); return CL_BREAK; } scandata->total++; switch (reason) { case error_mem: if (msg) logg("!Memory allocation failed during cli_ftw() on %s\n", msg); else logg("!Memory allocation failed during cli_ftw()\n"); scandata->errors++; return CL_EMEM; case error_stat: conn_reply_errno(scandata->conn, msg, "lstat() failed:"); logg("^lstat() failed on: %s\n", msg); scandata->errors++; return CL_SUCCESS; case warning_skipped_dir: logg("^Directory recursion limit reached, skipping %s\n", msg); return CL_SUCCESS; case warning_skipped_link: logg("$Skipping symlink: %s\n", msg); return CL_SUCCESS; case warning_skipped_special: if (msg == scandata->toplevel_path) conn_reply(scandata->conn, msg, "Not supported file type", "ERROR"); logg("*Not supported file type: %s\n", msg); return CL_SUCCESS; case visit_directory_toplev: return CL_SUCCESS; case visit_file: break; } /* check whether the file is excluded */ #ifdef C_LINUX if(procdev && sb && (sb->st_dev == procdev)) { free(filename); return CL_SUCCESS; } #endif if(sb && sb->st_size == 0) { /* empty file */ if (msg == scandata->toplevel_path) conn_reply_single(scandata->conn, filename, "Empty file"); free(filename); return CL_SUCCESS; } if (type == TYPE_MULTISCAN) { client_conn_t *client_conn = (client_conn_t *) calloc(1, sizeof(struct client_conn_tag)); if(client_conn) { client_conn->scanfd = -1; client_conn->sd = scandata->odesc; client_conn->filename = filename; client_conn->cmdtype = COMMAND_MULTISCANFILE; client_conn->term = scandata->conn->term; client_conn->options = scandata->options; client_conn->opts = scandata->opts; client_conn->group = scandata->group; if(cl_engine_addref(scandata->engine)) { logg("!cl_engine_addref() failed\n"); free(filename); free(client_conn); return CL_EMEM; } else { client_conn->engine = scandata->engine; pthread_mutex_lock(&reload_mutex); client_conn->engine_timestamp = reloaded_time; pthread_mutex_unlock(&reload_mutex); if(!thrmgr_group_dispatch(scandata->thr_pool, scandata->group, client_conn, 1)) { logg("!thread dispatch failed\n"); cl_engine_free(scandata->engine); free(filename); free(client_conn); return CL_EMEM; } } } else { logg("!Can't allocate memory for client_conn\n"); scandata->errors++; free(filename); return CL_EMEM; } return CL_SUCCESS; } if (access(filename, R_OK)) { if (conn_reply(scandata->conn, filename, "Access denied.", "ERROR") == -1) { free(filename); return CL_ETIMEOUT; } logg("*Access denied: %s\n", filename); scandata->errors++; free(filename); return CL_SUCCESS; } thrmgr_setactivetask(filename, NULL); context.filename = filename; context.virsize = 0; context.scandata = scandata; ret = cl_scanfile_callback(filename, &virname, &scandata->scanned, scandata->engine, scandata->options, &context); thrmgr_setactivetask(NULL, NULL); if (thrmgr_group_need_terminate(scandata->conn->group)) { free(filename); logg("*Client disconnected while scanjob was active\n"); return ret == CL_ETIMEOUT ? ret : CL_BREAK; } if ((ret == CL_VIRUS) && (virname == NULL)) { logg("*%s: reported CL_VIRUS but no virname returned!\n", filename); ret = CL_EMEM; } if (ret == CL_VIRUS) { scandata->infected++; if (scandata->options & CL_SCAN_ALLMATCHES) { if(optget(scandata->opts, "PreludeEnable")->enabled){ prelude_logging(filename, virname, context.virhash, context.virsize); } virusaction(filename, virname, scandata->opts); } else { if (conn_reply_virus(scandata->conn, filename, virname) == -1) { free(filename); return CL_ETIMEOUT; } if(optget(scandata->opts, "PreludeEnable")->enabled){ prelude_logging(filename, virname, context.virhash, context.virsize); } if(context.virsize && optget(scandata->opts, "ExtendedDetectionInfo")->enabled) logg("~%s: %s(%s:%llu) FOUND\n", filename, virname, context.virhash, context.virsize); else logg("~%s: %s FOUND\n", filename, virname); virusaction(filename, virname, scandata->opts); } } else if (ret != CL_CLEAN) { scandata->errors++; if (conn_reply(scandata->conn, filename, cl_strerror(ret), "ERROR") == -1) { free(filename); return CL_ETIMEOUT; } logg("~%s: %s ERROR\n", filename, cl_strerror(ret)); } else if (logok) { logg("~%s: OK\n", filename); } free(filename); if(ret == CL_EMEM) /* stop scanning */ return ret; if (type == TYPE_SCAN) { /* virus -> break */ return ret; } /* keep scanning always */ return CL_SUCCESS; }
static int scanstdin(const struct cl_engine *engine, const struct optstruct *opts, int options) { int ret; unsigned int fsize = 0; const char *virname, *tmpdir; char *file, buff[FILEBUFF]; size_t bread; FILE *fs; struct clamscan_cb_data data; if(optget(opts, "tempdir")->enabled) { tmpdir = optget(opts, "tempdir")->strarg; } else { /* check write access */ tmpdir = cli_gettmpdir(); } if(checkaccess(tmpdir, CLAMAVUSER, W_OK) != 1) { logg("!Can't write to temporary directory\n"); return 2; } if(!(file = cli_gentemp(tmpdir))) { logg("!Can't generate tempfile name\n"); return 2; } if(!(fs = fopen(file, "wb"))) { logg("!Can't open %s for writing\n", file); free(file); return 2; } while((bread = fread(buff, 1, FILEBUFF, stdin))) { fsize += bread; if(fwrite(buff, 1, bread, fs) < bread) { logg("!Can't write to %s\n", file); free(file); fclose(fs); return 2; } } fclose(fs); logg("*Checking %s\n", file); info.files++; info.rblocks += fsize / CL_COUNT_PRECISION; data.filename = "stdin"; data.chain = NULL; if((ret = cl_scanfile_callback(file, &virname, &info.blocks, engine, options, &data)) == CL_VIRUS) { info.ifiles++; if(bell) fprintf(stderr, "\007"); } else if(ret == CL_CLEAN) { if(!printinfected) mprintf("stdin: OK\n"); } else { if(!printinfected) logg("stdin: %s ERROR\n", cl_strerror(ret)); info.errors++; } unlink(file); free(file); return ret; }
static void *clamukolegacyth(void *arg) { struct thrarg *tharg = (struct thrarg *) arg; sigset_t sigset; const char *virname; struct sigaction act; unsigned long mask = 0; const struct optstruct *pt; short int scan; int sizelimit = 0, extinfo; struct stat sb; struct cb_context context; clamuko_scanning = 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 = clamuko_exit; sigfillset(&(act.sa_mask)); sigaction(SIGUSR1, &act, NULL); sigaction(SIGSEGV, &act, NULL); /* register */ if(dazukoRegister("ClamAV", "r+")) { logg("!Clamuko: Can't register with Dazuko\n"); return NULL; } else logg("Clamuko: Correctly registered with Dazuko.\n"); /* access mask */ if(optget(tharg->opts, "ClamukoScanOnOpen")->enabled) { logg("Clamuko: Scan-on-open mode activated.\n"); mask |= DAZUKO_ON_OPEN; } if(optget(tharg->opts, "ClamukoScanOnClose")->enabled) { logg("Clamuko: Scan-on-close mode activated.\n"); mask |= DAZUKO_ON_CLOSE; } if(optget(tharg->opts, "ClamukoScanOnExec")->enabled) { logg("Clamuko: Scan-on-exec mode activated.\n"); mask |= DAZUKO_ON_EXEC; } if(!mask) { logg("!Access mask is not configured properly.\n"); dazukoUnregister(); return NULL; } if(dazukoSetAccessMask(mask)) { logg("!Clamuko: Can't set access mask in Dazuko.\n"); dazukoUnregister(); return NULL; } if((pt = optget(tharg->opts, "ClamukoIncludePath"))->enabled) { while(pt) { if((dazukoAddIncludePath(pt->strarg))) { logg("!Clamuko: Dazuko -> Can't include path %s\n", pt->strarg); dazukoUnregister(); return NULL; } else logg("Clamuko: Included path %s\n", pt->strarg); pt = (struct optstruct *) pt->nextarg; } } else { logg("!Clamuko: please include at least one path.\n"); dazukoUnregister(); return NULL; } if((pt = optget(tharg->opts, "ClamukoExcludePath"))->enabled) { while(pt) { if((dazukoAddExcludePath(pt->strarg))) { logg("!Clamuko: Dazuko -> Can't exclude path %s\n", pt->strarg); dazukoUnregister(); return NULL; } else logg("Clamuko: Excluded path %s\n", pt->strarg); pt = (struct optstruct *) pt->nextarg; } } sizelimit = optget(tharg->opts, "ClamukoMaxFileSize")->numarg; if(sizelimit) logg("Clamuko: Max file size limited to %d bytes.\n", sizelimit); else logg("Clamuko: File size limit disabled.\n"); extinfo = optget(tharg->opts, "ExtendedDetectionInfo")->enabled; while(1) { if(dazukoGetAccess(&acc) == 0) { clamuko_scanning = 1; scan = 1; if(sizelimit) { stat(acc->filename, &sb); if(sb.st_size > sizelimit) { scan = 0; logg("*Clamuko: %s skipped (too big)\n", acc->filename); } } if(clamuko_checkowner(acc->pid, tharg->opts)) { scan = 0; logg("*Clamuko: %s skipped (excluded UID)\n", acc->filename); } context.filename = acc->filename; context.virsize = 0; if(scan && cl_scanfile_callback(acc->filename, &virname, NULL, tharg->engine, tharg->options, &context) == CL_VIRUS) { if(context.virsize) detstats_add(virname, acc->filename, context.virsize, context.virhash); if(extinfo && context.virsize) logg("Clamuko: %s: %s(%s:%llu) FOUND\n", acc->filename, virname, context.virhash, context.virsize); else logg("Clamuko: %s: %s FOUND\n", acc->filename, virname); virusaction(acc->filename, virname, tharg->opts); acc->deny = 1; } else acc->deny = 0; if(dazukoReturnAccess(&acc)) { logg("!Can't return access to Dazuko.\n"); logg("Clamuko stopped.\n"); dazukoUnregister(); clamuko_scanning = 0; return NULL; } clamuko_scanning = 0; } } /* can't be ;) */ return NULL; }