#if HAVE_CONFIG_H #include "clamav-config.h" #endif #include #include #include #include #include #if !defined(_WIN32) #include #include #endif #ifdef CL_THREAD_SAFE #include #endif #include #include "libclamav/others.h" #include "libclamav/clamav.h" #include "libclamav/json.h" #include "libclamav/stats.h" static cli_flagged_sample_t *find_sample(cli_intel_t *intel, const char *virname, const unsigned char *md5, size_t size, cli_intel_sample_type_t type); void free_sample(cli_flagged_sample_t *sample); void clamav_stats_add_sample(const char *virname, const unsigned char *md5, size_t size, cli_intel_sample_type_t type, void *cbdata) { cli_intel_t *intel; cli_flagged_sample_t *sample; size_t i; char **p; int err, submit=0; if (!(cbdata)) return; intel = (cli_intel_t *)cbdata; if (!(intel->engine)) return; /* First check if we need to submit stats based on memory/number limits */ if ((intel->engine->cb_stats_get_size)) submit = (intel->engine->cb_stats_get_size(cbdata) >= intel->maxmem); else submit = (clamav_stats_get_size(cbdata) >= intel->maxmem); if (submit == 0) { if ((intel->engine->cb_stats_get_num)) submit = (intel->engine->cb_stats_get_num(cbdata) >= intel->maxsamples); else submit = (clamav_stats_get_num(cbdata) >= intel->maxsamples); } if (submit) { if ((intel->engine->cb_stats_submit)) { intel->engine->cb_stats_submit(intel->engine, cbdata); } else { if ((intel->engine->cb_stats_flush)) intel->engine->cb_stats_flush(intel->engine, intel); return; } } #ifdef CL_THREAD_SAFE err = pthread_mutex_lock(&(intel->mutex)); if (err) { cli_warnmsg("clamav_stats_add_sample: locking mutex failed (err: %d): %s\n", err, strerror(err)); return; } #endif sample = find_sample(intel, virname, md5, size, type); if (!(sample)) { if (!(intel->samples)) { sample = intel->samples = calloc(1, sizeof(cli_flagged_sample_t)); if (!(sample)) goto end; } else { sample = calloc(1, sizeof(cli_flagged_sample_t)); if (!(sample)) goto end; sample->next = intel->samples; intel->samples->prev = sample; intel->samples = sample; } if ((sample->virus_name)) { for (i=0; sample->virus_name[i] != NULL; i++) ; } else { i=0; sample->virus_name = calloc(1, sizeof(char **)); if (!(sample->virus_name)) { free(sample); if (sample == intel->samples) intel->samples = NULL; goto end; } } sample->virus_name[i] = strdup((virname != NULL) ? virname : "[unknown]"); if (!(sample->virus_name[i])) { free(sample); free(sample->virus_name); if (sample == intel->samples) intel->samples = NULL; goto end; } p = realloc(sample->virus_name, sizeof(char **) * (i == 0 ? 2 : i+1)); if (!(p)) { free(sample->virus_name); free(sample); if (sample == intel->samples) intel->samples = NULL; goto end; } sample->virus_name = p; sample->virus_name[i+1] = NULL; memcpy(sample->md5, md5, sizeof(sample->md5)); sample->type = type; sample->size = size; intel->nsamples++; } cli_warnmsg("Added %s to the stats cache\n", (virname != NULL) ? virname: "[unknown]"); sample->hits++; end: #ifdef CL_THREAD_SAFE err = pthread_mutex_unlock(&(intel->mutex)); if (err) { cli_warnmsg("clamav_stats_add_sample: unlcoking mutex failed (err: %d): %s\n", err, strerror(err)); } #endif } void clamav_stats_flush(struct cl_engine *engine, void *cbdata) { cli_intel_t *intel; cli_flagged_sample_t *sample, *next; int err; if (!(cbdata) || !(engine)) return; intel = (cli_intel_t *)cbdata; #ifdef CL_THREAD_SAFE err = pthread_mutex_lock(&(intel->mutex)); if (err) { cli_warnmsg("clamav_stats_flush: locking mutex failed (err: %d): %s\n", err, strerror(err)); return; } #endif for (sample=intel->samples; sample != NULL; sample = next) { next = sample->next; free_sample(sample); } intel->samples = NULL; intel->nsamples = 0; #ifdef CL_THREAD_SAFE err = pthread_mutex_unlock(&(intel->mutex)); if (err) cli_warnmsg("clamav_stats_flush: unlocking mutex failed (err: %d): %s\n", err, strerror(err)); #endif } void free_sample(cli_flagged_sample_t *sample) { size_t i; if ((sample->virus_name)) { for (i=0; sample->virus_name[i] != NULL; i++) free(sample->virus_name[i]); free(sample->virus_name); } free(sample); } void clamav_stats_submit(struct cl_engine *engine, void *cbdata) { char *json; cli_intel_t *intel; int err; intel = (cli_intel_t *)cbdata; #ifdef CL_THREAD_SAFE err = pthread_mutex_lock(&(intel->mutex)); if (err) { cli_warnmsg("clamav_stats_submit: locking mutex failed (err: %d): %s\n", err, strerror(err)); if ((intel->engine) && (intel->engine->cb_stats_flush)) intel->engine->cb_stats_flush(intel->engine, cbdata); return; } #endif json = export_stats_to_json(engine, (cli_intel_t *)cbdata); #ifdef CL_THREAD_SAFE err = pthread_mutex_unlock(&(intel->mutex)); if (err) { cli_warnmsg("clamav_stats_submit: unlocking mutex failed (err: %d): %s\n", err, strerror(err)); } #endif cli_warnmsg("--- JSON ---\n%s\n--- END JSON ---\n", json); if (json) free(json); if ((engine->cb_stats_flush)) engine->cb_stats_flush(engine, cbdata); } void clamav_stats_remove_sample(const char *virname, const unsigned char *md5, size_t size, cli_intel_sample_type_t type, void *cbdata) { cli_intel_t *intel; cli_flagged_sample_t *sample; int err; intel = (cli_intel_t *)cbdata; if (!(intel)) return; #ifdef CL_THREAD_SAFE err = pthread_mutex_lock(&(intel->mutex)); if (err) { cli_warnmsg("clamav_stats_remove_sample: locking mutex failed (err: %d): %s\n", err, strerror(err)); return; } #endif sample = find_sample(intel, virname, md5, size, type); if (!(sample)) return; if (sample->prev) sample->prev->next = sample->next; if (sample->next) sample->next->prev = sample; if (sample == intel->samples) intel->samples = sample->next; free_sample(sample); intel->nsamples--; #ifdef CL_THREAD_SAFE err = pthread_mutex_unlock(&(intel->mutex)); if (err) { cli_warnmsg("clamav_stats_remove_sample: unlocking mutex failed (err: %d): %s\n", err, strerror(err)); } #endif } void clamav_stats_decrement_count(const char *virname, const unsigned char *md5, size_t size, cli_intel_sample_type_t type, void *cbdata) { cli_intel_t *intel; cli_flagged_sample_t *sample; int err; intel = (cli_intel_t *)cbdata; if (!(intel)) return; #ifdef CL_THREAD_SAFE err = pthread_mutex_lock(&(intel->mutex)); if (err) { cli_warnmsg("clamav_stats_decrement_count: locking mutex failed (err: %d): %s\n", err, strerror(err)); return; } #endif sample = find_sample(intel, virname, md5, size, type); if (!(sample)) return; if (sample->hits == 1) { if ((intel->engine->cb_stats_remove_sample)) intel->engine->cb_stats_remove_sample(virname, md5, size, type, intel); else clamav_stats_remove_sample(virname, md5, size, type, intel); return; } sample->hits--; #ifdef CL_THREAD_SAFE err = pthread_mutex_unlock(&(intel->mutex)); if (err) { cli_warnmsg("clamav_stats_decrement_count: unlocking mutex failed (err: %d): %s\n", err, strerror(err)); } #endif } size_t clamav_stats_get_num(void *cbdata) { cli_intel_t *intel; intel = (cli_intel_t *)cbdata; if (!(intel)) return 0; return intel->nsamples; } size_t clamav_stats_get_size(void *cbdata) { cli_intel_t *intel; cli_flagged_sample_t *sample; size_t sz, i; int err; intel = (cli_intel_t *)cbdata; if (!(intel)) return 0; sz = sizeof(cli_intel_t); #ifdef CL_THREAD_SAFE err = pthread_mutex_lock(&(intel->mutex)); if (err) { cli_warnmsg("clamav_stats_get_size: locking mutex failed (err: %d): %s\n", err, strerror(err)); return sz; } #endif for (sample = intel->samples; sample != NULL; sample = sample->next) { sz += sizeof(cli_flagged_sample_t); if ((sample->virus_name)) { for (i=0; sample->virus_name[i] != NULL; i++) sz += strlen(sample->virus_name[i]); sz += sizeof(char **) * i; } } #ifdef CL_THREAD_SAFE err = pthread_mutex_unlock(&(intel->mutex)); if (err) { cli_warnmsg("clamav_stats_get_size: unlocking mutex failed (err: %d): %s\n", err, strerror(err)); } #endif return sz; } #if defined(_WIN32) char *clamav_stats_get_hostid(void *cbdata) { return strdup(STATS_ANON_UUID); } #else char *clamav_stats_get_hostid(void *cbdata) { char *sysctls[] = { "kern.hostuuid", NULL }; size_t bufsz, i; char *buf; #if HAVE_SYSCTLBYNAME /* FreeBSD-landia */ for (i=0; sysctls[i] != NULL; i++) { if (sysctlbyname(sysctls[i], NULL, &bufsz, NULL, 0)) continue; break; /* Got one */ } if (sysctls[i] != NULL) { buf = calloc(1, bufsz+1); if (sysctlbyname(sysctls[i], buf, &bufsz, NULL, 0)) return strdup(STATS_ANON_UUID); /* Not sure why this would happen, but we'll just default to the anon uuid on error */ return buf; } #else return strdup(STATS_ANON_UUID); #endif return strdup(STATS_ANON_UUID); } #endif static cli_flagged_sample_t *find_sample(cli_intel_t *intel, const char *virname, const unsigned char *md5, size_t size, cli_intel_sample_type_t type) { cli_flagged_sample_t *sample; size_t i; for (sample = intel->samples; sample != NULL; sample = sample->next) { if (sample->type != type) continue; if (sample->size != size) continue; if (memcmp(sample->md5, md5, sizeof(sample->md5))) continue; if (!(virname)) return sample; for (i=0; sample->virus_name[i] != NULL; i++) if (!strcmp(sample->virus_name[i], virname)) return sample; } return NULL; }