1bdaf813 |
/*
* Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
*
* Author: Shawn Webb
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
|
f2571e34 |
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h> |
4a4b84da |
#if HAVE_UNISTD_H |
f2571e34 |
#include <unistd.h> |
4a4b84da |
#endif |
f2571e34 |
#include <sys/types.h> |
c8bf9b6c |
#if !defined(_WIN32) |
70ab826f |
#if defined(C_SOLARIS)
#include <sys/utsname.h>
#else |
424fb3b5 |
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif |
58f224b8 |
#if HAVE_SYSCTLBYNAME |
c8bf9b6c |
#include <sys/sysctl.h> |
70ab826f |
#endif |
58f224b8 |
#endif |
53242b97 |
#else
#include <Windows.h>
#include <tchar.h> |
c8bf9b6c |
#endif |
f2571e34 |
#ifdef CL_THREAD_SAFE
#include <pthread.h>
#endif
|
68e60b12 |
#include <errno.h>
|
f2571e34 |
#include "libclamav/others.h"
#include "libclamav/clamav.h" |
d4f90ad4 |
#include "libclamav/dconf.h" |
e182c02c |
#include "libclamav/stats_json.h" |
4473a0a9 |
#include "libclamav/stats.h" |
9e0f01d9 |
#include "libclamav/hostid.h" |
2d6361a9 |
#include "libclamav/www.h" |
f2571e34 |
|
b86e3908 |
#define DEBUG_STATS 0 |
3c29ca0b |
static cli_flagged_sample_t *find_sample(cli_intel_t *intel, const char *virname, const unsigned char *md5, size_t size, stats_section_t *sections); |
f2571e34 |
void free_sample(cli_flagged_sample_t *sample);
|
3c29ca0b |
#if DEBUG_STATS
char *get_hash(unsigned char *md5)
{
char *hash;
int i;
hash = calloc(1, 33);
if (!(hash))
return NULL;
|
288057e9 |
for (i = 0; i < 16; i++)
sprintf(hash + (i * 2), "%02x", md5[i]); |
3c29ca0b |
return hash;
}
char *get_sample_names(char **names)
{
char *ret;
size_t n, i, sz;
sz = 0; |
288057e9 |
for (n = 0; names[n] != NULL; n++) |
3c29ca0b |
sz += strlen(names[n]);
ret = calloc(1, sz + n + 1);
if (!(ret))
return NULL;
|
288057e9 |
for (i = 0; names[i] != NULL; i++)
sprintf(ret + strlen(ret), "%s%s", (i == 0) ? "" : " ", names[i]); |
3c29ca0b |
return ret;
}
void print_sample(cli_flagged_sample_t *sample)
{
char *hash, *names;
size_t i;
if (!(sample))
return;
hash = get_hash(sample->md5);
if (!(hash))
return;
cli_warnmsg("Sample[%s]:\n", hash);
cli_warnmsg(" * Size: %zu\n", sample->size);
cli_warnmsg(" * Hits: %u\n", sample->hits);
free(hash);
names = get_sample_names(sample->virus_name);
if ((names))
cli_warnmsg(" * Names: %s\n", names);
if (sample->sections && sample->sections->nsections) { |
288057e9 |
for (i = 0; i < sample->sections->nsections; i++) { |
3c29ca0b |
hash = get_hash(sample->sections->sections[i].md5);
if ((hash)) {
cli_warnmsg(" * Section[%zu] (%zu): %s\n", i, sample->sections->sections[i].len, hash);
free(hash);
}
}
}
if ((names))
free(names);
}
#endif
void clamav_stats_add_sample(const char *virname, const unsigned char *md5, size_t size, stats_section_t *sections, void *cbdata) |
f2571e34 |
{
cli_intel_t *intel;
cli_flagged_sample_t *sample;
size_t i;
char **p; |
288057e9 |
int err, submit = 0; |
f2571e34 |
if (!(cbdata))
return;
intel = (cli_intel_t *)cbdata; |
4473a0a9 |
if (!(intel->engine))
return; |
f2571e34 |
|
d4f90ad4 |
if (intel->engine->dconf->stats & DCONF_STATS_DISABLED)
return;
|
4473a0a9 |
/* 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);
} |
f2571e34 |
|
4473a0a9 |
if (submit) {
if ((intel->engine->cb_stats_submit)) {
intel->engine->cb_stats_submit(intel->engine, cbdata);
} else { |
f2571e34 |
if ((intel->engine->cb_stats_flush))
intel->engine->cb_stats_flush(intel->engine, intel);
return;
}
}
#ifdef CL_THREAD_SAFE |
68e60b12 |
err = pthread_mutex_lock(&(intel->mutex));
if (err) {
cli_warnmsg("clamav_stats_add_sample: locking mutex failed (err: %d): %s\n", err, strerror(err)); |
f2571e34 |
return;
}
#endif
|
3c29ca0b |
sample = find_sample(intel, virname, md5, size, sections); |
f2571e34 |
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;
|
288057e9 |
sample->next = intel->samples; |
f2571e34 |
intel->samples->prev = sample; |
288057e9 |
intel->samples = sample; |
f2571e34 |
}
if ((sample->virus_name)) { |
288057e9 |
for (i = 0; sample->virus_name[i] != NULL; i++) |
f2571e34 |
; |
3fc1fc69 |
p = realloc(sample->virus_name, sizeof(char **) * (i + 1));
if (!(p)) {
free(sample->virus_name);
free(sample);
if (sample == intel->samples)
intel->samples = NULL;
goto end;
}
sample->virus_name = p; |
f2571e34 |
} else { |
288057e9 |
i = 0; |
f2571e34 |
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->virus_name); |
0c58ddd2 |
free(sample); |
f2571e34 |
if (sample == intel->samples)
intel->samples = NULL;
goto end;
}
|
288057e9 |
p = realloc(sample->virus_name, sizeof(char **) * (i + 2)); |
f2571e34 |
if (!(p)) {
free(sample->virus_name);
free(sample);
if (sample == intel->samples)
intel->samples = NULL;
goto end;
}
|
288057e9 |
sample->virus_name = p;
sample->virus_name[i + 1] = NULL; |
f2571e34 |
memcpy(sample->md5, md5, sizeof(sample->md5)); |
b3c40dc6 |
sample->size = (uint32_t)size; |
f2571e34 |
intel->nsamples++; |
3c29ca0b |
if (sections && sections->nsections && !(sample->sections)) {
/* Copy the section data that has already been allocated. We don't care if calloc fails; just skip copying if it does. */
sample->sections = calloc(1, sizeof(stats_section_t)); |
e6bcbd5a |
if ((sample->sections)) {
sample->sections->sections = calloc(sections->nsections, sizeof(struct cli_section_hash));
if ((sample->sections->sections)) {
memcpy(sample->sections->sections, sections->sections, sections->nsections * sizeof(struct cli_section_hash));
sample->sections->nsections = sections->nsections;
} else {
free(sample->sections);
sample->sections = NULL;
}
} |
3c29ca0b |
} |
f2571e34 |
}
sample->hits++;
end:
#ifdef CL_THREAD_SAFE |
68e60b12 |
err = pthread_mutex_unlock(&(intel->mutex));
if (err) { |
7cd9337a |
cli_warnmsg("clamav_stats_add_sample: unlocking mutex failed (err: %d): %s\n", err, strerror(err)); |
f2571e34 |
}
#endif |
f092d0a1 |
return; |
f2571e34 |
}
void clamav_stats_flush(struct cl_engine *engine, void *cbdata)
{
cli_intel_t *intel;
cli_flagged_sample_t *sample, *next; |
68e60b12 |
int err;
if (!(cbdata) || !(engine))
return;
intel = (cli_intel_t *)cbdata; |
f2571e34 |
#ifdef CL_THREAD_SAFE |
68e60b12 |
err = pthread_mutex_lock(&(intel->mutex));
if (err) {
cli_warnmsg("clamav_stats_flush: locking mutex failed (err: %d): %s\n", err, strerror(err)); |
f2571e34 |
return;
}
#endif
|
288057e9 |
for (sample = intel->samples; sample != NULL; sample = next) { |
f2571e34 |
next = sample->next;
free_sample(sample);
}
|
288057e9 |
intel->samples = NULL; |
f2571e34 |
intel->nsamples = 0; |
e6bcbd5a |
if (intel->hostid) {
free(intel->hostid);
intel->hostid = NULL;
} |
f2571e34 |
#ifdef CL_THREAD_SAFE |
68e60b12 |
err = pthread_mutex_unlock(&(intel->mutex));
if (err)
cli_warnmsg("clamav_stats_flush: unlocking mutex failed (err: %d): %s\n", err, strerror(err)); |
f2571e34 |
#endif
}
void free_sample(cli_flagged_sample_t *sample)
{
size_t i;
if ((sample->virus_name)) { |
288057e9 |
for (i = 0; sample->virus_name[i] != NULL; i++) |
f2571e34 |
free(sample->virus_name[i]);
free(sample->virus_name);
}
|
186712e0 |
if ((sample->sections) && (sample->sections->nsections)) {
free(sample->sections->sections);
free(sample->sections);
}
|
f2571e34 |
free(sample);
}
void clamav_stats_submit(struct cl_engine *engine, void *cbdata)
{
char *json; |
2d6361a9 |
cli_intel_t *intel, myintel;
cli_flagged_sample_t *sample, *next; |
68e60b12 |
int err; |
f2571e34 |
intel = (cli_intel_t *)cbdata; |
2d6361a9 |
if (!(intel) || !(engine))
return; |
f2571e34 |
|
d4f90ad4 |
if (engine->dconf->stats & DCONF_STATS_DISABLED)
return;
|
b7485a22 |
if (!(engine->cb_stats_get_hostid)) {
/* Submitting stats is disabled due to HostID being turned off */
if ((engine->cb_stats_flush))
engine->cb_stats_flush(engine, cbdata);
return;
}
|
690a27b8 |
cli_dbgmsg("stats - start\n");
|
f2571e34 |
#ifdef CL_THREAD_SAFE |
68e60b12 |
err = pthread_mutex_lock(&(intel->mutex));
if (err) {
cli_warnmsg("clamav_stats_submit: locking mutex failed (err: %d): %s\n", err, strerror(err)); |
f2571e34 |
if ((intel->engine) && (intel->engine->cb_stats_flush))
intel->engine->cb_stats_flush(intel->engine, cbdata);
return;
}
#endif
|
2d6361a9 |
/* Empty out the cached intelligence data so that other threads don't sit waiting to add data to the cache */
memcpy(&myintel, intel, sizeof(cli_intel_t)); |
288057e9 |
intel->samples = NULL; |
2d6361a9 |
intel->nsamples = 0;
json = export_stats_to_json(engine, &myintel); |
f2571e34 |
#ifdef CL_THREAD_SAFE |
68e60b12 |
err = pthread_mutex_unlock(&(intel->mutex));
if (err) {
cli_warnmsg("clamav_stats_submit: unlocking mutex failed (err: %d): %s\n", err, strerror(err)); |
f2571e34 |
}
#endif
|
288057e9 |
for (sample = myintel.samples; sample != NULL; sample = next) { |
3c29ca0b |
#if DEBUG_STATS
print_sample(sample);
#endif |
2d6361a9 |
next = sample->next; |
f2571e34 |
|
2d6361a9 |
free_sample(sample);
} |
f2571e34 |
|
2d6361a9 |
if (json) { |
4e1236c8 |
submit_post(STATS_HOST, STATS_PORT, "PUT", "/clamav/1/submit/stats", json, myintel.timeout); |
2d6361a9 |
free(json);
} |
e6bcbd5a |
if (myintel.hostid && !(intel->hostid)) {
free(myintel.hostid);
myintel.hostid = NULL;
} |
690a27b8 |
cli_dbgmsg("stats - end\n"); |
f2571e34 |
}
|
3c29ca0b |
void clamav_stats_remove_sample(const char *virname, const unsigned char *md5, size_t size, void *cbdata) |
4473a0a9 |
{
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
|
3c29ca0b |
while ((sample = find_sample(intel, virname, md5, size, NULL))) {
if (sample->prev)
sample->prev->next = sample->next;
if (sample->next)
sample->next->prev = sample->prev;
if (sample == intel->samples)
intel->samples = sample->next; |
4473a0a9 |
|
3c29ca0b |
free_sample(sample);
intel->nsamples--;
} |
4473a0a9 |
#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
}
|
3c29ca0b |
void clamav_stats_decrement_count(const char *virname, const unsigned char *md5, size_t size, void *cbdata) |
4473a0a9 |
{
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
|
3c29ca0b |
sample = find_sample(intel, virname, md5, size, NULL); |
4473a0a9 |
if (!(sample)) |
e198df77 |
goto clamav_stats_decrement_end; |
4473a0a9 |
if (sample->hits == 1) {
if ((intel->engine->cb_stats_remove_sample)) |
3c29ca0b |
intel->engine->cb_stats_remove_sample(virname, md5, size, intel); |
4473a0a9 |
else |
3c29ca0b |
clamav_stats_remove_sample(virname, md5, size, intel); |
4473a0a9 |
|
e198df77 |
goto clamav_stats_decrement_end; |
4473a0a9 |
}
sample->hits--;
|
288057e9 |
clamav_stats_decrement_end: |
4473a0a9 |
#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 |
f092d0a1 |
return; |
4473a0a9 |
}
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;
|
c8bf9b6c |
sz = sizeof(cli_intel_t);
|
4473a0a9 |
#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)); |
c8bf9b6c |
return sz; |
4473a0a9 |
}
#endif
for (sample = intel->samples; sample != NULL; sample = sample->next) {
sz += sizeof(cli_flagged_sample_t);
if ((sample->virus_name)) { |
288057e9 |
for (i = 0; sample->virus_name[i] != NULL; i++) |
4473a0a9 |
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;
}
|
c8bf9b6c |
#if defined(_WIN32)
char *clamav_stats_get_hostid(void *cbdata)
{ |
cfeb7723 |
HW_PROFILE_INFO HwProfInfo; |
53242b97 |
|
cfeb7723 |
if (!GetCurrentHwProfile(&HwProfInfo))
return strdup(STATS_ANON_UUID); |
53242b97 |
return strdup(HwProfInfo.szHwProfileGuid); |
c8bf9b6c |
} |
70ab826f |
#elif defined(C_SOLARIS)
char *clamav_stats_get_hostid(void *cbdata)
{
struct utsname utsnm;
int ret;
ret = uname(&utsnm);
if (ret != -1)
return strdup(utsnm.nodename);
return strdup(STATS_ANON_UUID);
} |
c8bf9b6c |
#else
char *clamav_stats_get_hostid(void *cbdata)
{
char *sysctls[] = {
"kern.hostuuid", |
288057e9 |
NULL}; |
c8bf9b6c |
size_t bufsz, i;
char *buf;
|
cd94be7a |
UNUSEDPARAM(cbdata);
|
02c1afff |
#if HAVE_SYSCTLBYNAME |
cfeb7723 |
/*
* FreeBSD provides a handy-dandy sysctl for grabbing the system's HostID. In a jail that
* hasn't run the hostid rc.d script, the hostid defaults to all zeros.
*/ |
288057e9 |
for (i = 0; sysctls[i] != NULL; i++) { |
02c1afff |
if (sysctlbyname(sysctls[i], NULL, &bufsz, NULL, 0))
continue; |
c8bf9b6c |
|
02c1afff |
break; /* Got one */
}
if (sysctls[i] != NULL) { |
288057e9 |
buf = calloc(1, bufsz + 1); |
02c1afff |
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; |
c8bf9b6c |
} |
9e47301b |
return strdup(STATS_ANON_UUID); |
02c1afff |
#else |
9e0f01d9 |
buf = internal_get_host_id();
if (!(buf)) |
02c1afff |
return strdup(STATS_ANON_UUID); |
9e0f01d9 |
return buf; |
02c1afff |
#endif |
c8bf9b6c |
}
#endif
|
3c29ca0b |
static cli_flagged_sample_t *find_sample(cli_intel_t *intel, const char *virname, const unsigned char *md5, size_t size, stats_section_t *sections) |
f2571e34 |
{
cli_flagged_sample_t *sample;
size_t i;
for (sample = intel->samples; sample != NULL; sample = sample->next) { |
3c29ca0b |
int foundSections = 0; |
f2571e34 |
if (sample->size != size)
continue;
if (memcmp(sample->md5, md5, sizeof(sample->md5)))
continue;
if (!(virname))
return sample;
|
3c29ca0b |
if ((sections) && (sample->sections)) {
if (sections->nsections == sample->sections->nsections) { |
288057e9 |
for (i = 0; i < sections->nsections; i++) |
3c29ca0b |
if (sections->sections[i].len == sample->sections->sections[i].len)
if (memcmp(sections->sections[i].md5, sample->sections->sections[i].md5, sizeof(stats_section_t)))
break;
if (i == sections->nsections)
foundSections = 1;
}
} else {
foundSections = 1;
}
if (foundSections) |
288057e9 |
for (i = 0; sample->virus_name[i] != NULL; i++) |
3c29ca0b |
if (!strcmp(sample->virus_name[i], virname))
return sample; |
f2571e34 |
}
return NULL;
} |
7c92a662 |
void cl_engine_set_clcb_stats_submit(struct cl_engine *engine, clcb_stats_submit callback)
{
engine->cb_stats_submit = callback;
}
|
e6786fef |
void cl_engine_set_stats_set_cbdata(struct cl_engine *engine, void *cbdata) |
7c92a662 |
{
engine->stats_data = cbdata;
}
void cl_engine_set_clcb_stats_add_sample(struct cl_engine *engine, clcb_stats_add_sample callback)
{
engine->cb_stats_add_sample = callback;
}
void cl_engine_set_clcb_stats_remove_sample(struct cl_engine *engine, clcb_stats_remove_sample callback)
{
engine->cb_stats_remove_sample = callback;
}
void cl_engine_set_clcb_stats_decrement_count(struct cl_engine *engine, clcb_stats_decrement_count callback)
{
engine->cb_stats_decrement_count = callback;
}
void cl_engine_set_clcb_stats_flush(struct cl_engine *engine, clcb_stats_flush callback)
{
engine->cb_stats_flush = callback;
}
void cl_engine_set_clcb_stats_get_num(struct cl_engine *engine, clcb_stats_get_num callback)
{
engine->cb_stats_get_num = callback;
}
void cl_engine_set_clcb_stats_get_size(struct cl_engine *engine, clcb_stats_get_size callback)
{
engine->cb_stats_get_size = callback;
}
void cl_engine_set_clcb_stats_get_hostid(struct cl_engine *engine, clcb_stats_get_hostid callback)
{
engine->cb_stats_get_hostid = callback;
} |
38702161 |
void cl_engine_stats_enable(struct cl_engine *engine)
{
engine->cb_stats_add_sample = clamav_stats_add_sample; |
288057e9 |
engine->cb_stats_submit = clamav_stats_submit; |
38702161 |
} |