2
* Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License version 2 as
8
* published by the Free Software Foundation.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22
#include "clamav-config.h"
32
#include <sys/types.h>
34
#if defined(C_SOLARIS)
35
#include <sys/utsname.h>
38
#include <sys/param.h>
41
#include <sys/sysctl.h>
56
#include <openssl/ssl.h>
57
#include <openssl/err.h>
58
#include "libclamav/crypto.h"
60
#include "libclamav/others.h"
61
#include "libclamav/clamav.h"
62
#include "libclamav/dconf.h"
63
#include "libclamav/json.h"
64
#include "libclamav/stats.h"
65
#include "libclamav/hostid.h"
66
#include "libclamav/www.h"
70
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);
71
void free_sample(cli_flagged_sample_t *sample);
74
char *get_hash(unsigned char *md5)
84
sprintf(hash+(i*2), "%02x", md5[i]);
89
char *get_sample_names(char **names)
95
for (n=0; names[n] != NULL; n++)
96
sz += strlen(names[n]);
98
ret = calloc(1, sz + n + 1);
102
for (i=0; names[i] != NULL; i++)
103
sprintf(ret+strlen(ret), "%s%s", (i==0) ? "" : " ", names[i]);
108
void print_sample(cli_flagged_sample_t *sample)
116
hash = get_hash(sample->md5);
120
cli_warnmsg("Sample[%s]:\n", hash);
121
cli_warnmsg(" * Size: %zu\n", sample->size);
122
cli_warnmsg(" * Hits: %u\n", sample->hits);
126
names = get_sample_names(sample->virus_name);
128
cli_warnmsg(" * Names: %s\n", names);
130
if (sample->sections && sample->sections->nsections) {
131
for (i=0; i < sample->sections->nsections; i++) {
132
hash = get_hash(sample->sections->sections[i].md5);
134
cli_warnmsg(" * Section[%zu] (%zu): %s\n", i, sample->sections->sections[i].len, hash);
145
void clamav_stats_add_sample(const char *virname, const unsigned char *md5, size_t size, stats_section_t *sections, void *cbdata)
148
cli_flagged_sample_t *sample;
156
intel = (cli_intel_t *)cbdata;
157
if (!(intel->engine))
160
if (intel->engine->dconf->stats & DCONF_STATS_DISABLED)
163
/* First check if we need to submit stats based on memory/number limits */
164
if ((intel->engine->cb_stats_get_size))
165
submit = (intel->engine->cb_stats_get_size(cbdata) >= intel->maxmem);
167
submit = (clamav_stats_get_size(cbdata) >= intel->maxmem);
170
if ((intel->engine->cb_stats_get_num))
171
submit = (intel->engine->cb_stats_get_num(cbdata) >= intel->maxsamples);
173
submit = (clamav_stats_get_num(cbdata) >= intel->maxsamples);
177
if ((intel->engine->cb_stats_submit)) {
178
intel->engine->cb_stats_submit(intel->engine, cbdata);
180
if ((intel->engine->cb_stats_flush))
181
intel->engine->cb_stats_flush(intel->engine, intel);
187
#ifdef CL_THREAD_SAFE
188
err = pthread_mutex_lock(&(intel->mutex));
190
cli_warnmsg("clamav_stats_add_sample: locking mutex failed (err: %d): %s\n", err, strerror(err));
195
sample = find_sample(intel, virname, md5, size, sections);
197
if (!(intel->samples)) {
198
sample = intel->samples = calloc(1, sizeof(cli_flagged_sample_t));
202
sample = calloc(1, sizeof(cli_flagged_sample_t));
206
sample->next = intel->samples;
207
intel->samples->prev = sample;
208
intel->samples = sample;
211
if ((sample->virus_name)) {
212
for (i=0; sample->virus_name[i] != NULL; i++)
214
p = realloc(sample->virus_name, sizeof(char **) * (i + 1));
216
free(sample->virus_name);
218
if (sample == intel->samples)
219
intel->samples = NULL;
224
sample->virus_name = p;
227
sample->virus_name = calloc(1, sizeof(char **));
228
if (!(sample->virus_name)) {
230
if (sample == intel->samples)
231
intel->samples = NULL;
237
sample->virus_name[i] = strdup((virname != NULL) ? virname : "[unknown]");
238
if (!(sample->virus_name[i])) {
239
free(sample->virus_name);
241
if (sample == intel->samples)
242
intel->samples = NULL;
247
p = realloc(sample->virus_name, sizeof(char **) * (i+2));
249
free(sample->virus_name);
251
if (sample == intel->samples)
252
intel->samples = NULL;
257
sample->virus_name = p;
258
sample->virus_name[i+1] = NULL;
260
memcpy(sample->md5, md5, sizeof(sample->md5));
261
sample->size = (uint32_t)size;
264
if (sections && sections->nsections && !(sample->sections)) {
265
/* Copy the section data that has already been allocated. We don't care if calloc fails; just skip copying if it does. */
266
sample->sections = calloc(1, sizeof(stats_section_t));
267
if ((sample->sections)) {
268
sample->sections->sections = calloc(sections->nsections, sizeof(struct cli_section_hash));
269
if ((sample->sections->sections)) {
270
memcpy(sample->sections->sections, sections->sections, sections->nsections * sizeof(struct cli_section_hash));
271
sample->sections->nsections = sections->nsections;
273
free(sample->sections);
274
sample->sections = NULL;
283
#ifdef CL_THREAD_SAFE
284
err = pthread_mutex_unlock(&(intel->mutex));
286
cli_warnmsg("clamav_stats_add_sample: unlcoking mutex failed (err: %d): %s\n", err, strerror(err));
291
void clamav_stats_flush(struct cl_engine *engine, void *cbdata)
294
cli_flagged_sample_t *sample, *next;
297
if (!(cbdata) || !(engine))
300
intel = (cli_intel_t *)cbdata;
302
#ifdef CL_THREAD_SAFE
303
err = pthread_mutex_lock(&(intel->mutex));
305
cli_warnmsg("clamav_stats_flush: locking mutex failed (err: %d): %s\n", err, strerror(err));
310
for (sample=intel->samples; sample != NULL; sample = next) {
316
intel->samples = NULL;
320
intel->hostid = NULL;
323
#ifdef CL_THREAD_SAFE
324
err = pthread_mutex_unlock(&(intel->mutex));
326
cli_warnmsg("clamav_stats_flush: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
330
void free_sample(cli_flagged_sample_t *sample)
334
if ((sample->virus_name)) {
335
for (i=0; sample->virus_name[i] != NULL; i++)
336
free(sample->virus_name[i]);
338
free(sample->virus_name);
341
if ((sample->sections) && (sample->sections->nsections)) {
342
free(sample->sections->sections);
343
free(sample->sections);
349
void clamav_stats_submit(struct cl_engine *engine, void *cbdata)
352
cli_intel_t *intel, myintel;
353
cli_flagged_sample_t *sample, *next;
356
intel = (cli_intel_t *)cbdata;
357
if (!(intel) || !(engine))
360
if (engine->dconf->stats & DCONF_STATS_DISABLED)
363
if (!(engine->cb_stats_get_hostid)) {
364
/* Submitting stats is disabled due to HostID being turned off */
365
if ((engine->cb_stats_flush))
366
engine->cb_stats_flush(engine, cbdata);
371
cli_dbgmsg("stats - start\n");
373
#ifdef CL_THREAD_SAFE
374
err = pthread_mutex_lock(&(intel->mutex));
376
cli_warnmsg("clamav_stats_submit: locking mutex failed (err: %d): %s\n", err, strerror(err));
378
if ((intel->engine) && (intel->engine->cb_stats_flush))
379
intel->engine->cb_stats_flush(intel->engine, cbdata);
385
/* Empty out the cached intelligence data so that other threads don't sit waiting to add data to the cache */
386
memcpy(&myintel, intel, sizeof(cli_intel_t));
387
intel->samples = NULL;
390
json = export_stats_to_json(engine, &myintel);
392
#ifdef CL_THREAD_SAFE
393
err = pthread_mutex_unlock(&(intel->mutex));
395
cli_warnmsg("clamav_stats_submit: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
399
for (sample=myintel.samples; sample != NULL; sample = next) {
401
print_sample(sample);
409
submit_post(STATS_HOST, STATS_PORT, "PUT", "/clamav/1/submit/stats", json, myintel.timeout);
413
if (myintel.hostid && !(intel->hostid)) {
414
free(myintel.hostid);
415
myintel.hostid = NULL;
418
cli_dbgmsg("stats - end\n");
421
void clamav_stats_remove_sample(const char *virname, const unsigned char *md5, size_t size, void *cbdata)
424
cli_flagged_sample_t *sample;
427
intel = (cli_intel_t *)cbdata;
431
#ifdef CL_THREAD_SAFE
432
err = pthread_mutex_lock(&(intel->mutex));
434
cli_warnmsg("clamav_stats_remove_sample: locking mutex failed (err: %d): %s\n", err, strerror(err));
439
while ((sample = find_sample(intel, virname, md5, size, NULL))) {
441
sample->prev->next = sample->next;
443
sample->next->prev = sample->prev;
444
if (sample == intel->samples)
445
intel->samples = sample->next;
451
#ifdef CL_THREAD_SAFE
452
err = pthread_mutex_unlock(&(intel->mutex));
454
cli_warnmsg("clamav_stats_remove_sample: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
459
void clamav_stats_decrement_count(const char *virname, const unsigned char *md5, size_t size, void *cbdata)
462
cli_flagged_sample_t *sample;
465
intel = (cli_intel_t *)cbdata;
469
#ifdef CL_THREAD_SAFE
470
err = pthread_mutex_lock(&(intel->mutex));
472
cli_warnmsg("clamav_stats_decrement_count: locking mutex failed (err: %d): %s\n", err, strerror(err));
477
sample = find_sample(intel, virname, md5, size, NULL);
479
goto clamav_stats_decrement_end;
481
if (sample->hits == 1) {
482
if ((intel->engine->cb_stats_remove_sample))
483
intel->engine->cb_stats_remove_sample(virname, md5, size, intel);
485
clamav_stats_remove_sample(virname, md5, size, intel);
487
goto clamav_stats_decrement_end;
492
clamav_stats_decrement_end:
493
#ifdef CL_THREAD_SAFE
494
err = pthread_mutex_unlock(&(intel->mutex));
496
cli_warnmsg("clamav_stats_decrement_count: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
501
size_t clamav_stats_get_num(void *cbdata)
505
intel = (cli_intel_t *)cbdata;
510
return intel->nsamples;
513
size_t clamav_stats_get_size(void *cbdata)
516
cli_flagged_sample_t *sample;
520
intel = (cli_intel_t *)cbdata;
524
sz = sizeof(cli_intel_t);
526
#ifdef CL_THREAD_SAFE
527
err = pthread_mutex_lock(&(intel->mutex));
529
cli_warnmsg("clamav_stats_get_size: locking mutex failed (err: %d): %s\n", err, strerror(err));
534
for (sample = intel->samples; sample != NULL; sample = sample->next) {
535
sz += sizeof(cli_flagged_sample_t);
536
if ((sample->virus_name)) {
537
for (i=0; sample->virus_name[i] != NULL; i++)
538
sz += strlen(sample->virus_name[i]);
539
sz += sizeof(char **) * i;
543
#ifdef CL_THREAD_SAFE
544
err = pthread_mutex_unlock(&(intel->mutex));
546
cli_warnmsg("clamav_stats_get_size: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
554
char *clamav_stats_get_hostid(void *cbdata)
556
HW_PROFILE_INFO HwProfInfo;
558
if (!GetCurrentHwProfile(&HwProfInfo))
559
return strdup(STATS_ANON_UUID);
561
return strdup(HwProfInfo.szHwProfileGuid);
563
#elif defined(C_SOLARIS)
564
char *clamav_stats_get_hostid(void *cbdata)
566
struct utsname utsnm;
571
return strdup(utsnm.nodename);
573
return strdup(STATS_ANON_UUID);
576
char *clamav_stats_get_hostid(void *cbdata)
585
#if HAVE_SYSCTLBYNAME
587
* FreeBSD provides a handy-dandy sysctl for grabbing the system's HostID. In a jail that
588
* hasn't run the hostid rc.d script, the hostid defaults to all zeros.
590
for (i=0; sysctls[i] != NULL; i++) {
591
if (sysctlbyname(sysctls[i], NULL, &bufsz, NULL, 0))
597
if (sysctls[i] != NULL) {
598
buf = calloc(1, bufsz+1);
599
if (sysctlbyname(sysctls[i], buf, &bufsz, NULL, 0))
600
return strdup(STATS_ANON_UUID); /* Not sure why this would happen, but we'll just default to the anon uuid on error */
605
return strdup(STATS_ANON_UUID);
607
buf = internal_get_host_id();
609
return strdup(STATS_ANON_UUID);
615
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)
617
cli_flagged_sample_t *sample;
620
for (sample = intel->samples; sample != NULL; sample = sample->next) {
621
int foundSections = 0;
623
if (sample->size != size)
626
if (memcmp(sample->md5, md5, sizeof(sample->md5)))
632
if ((sections) && (sample->sections)) {
633
if (sections->nsections == sample->sections->nsections) {
634
for (i=0; i < sections->nsections; i++)
635
if (sections->sections[i].len == sample->sections->sections[i].len)
636
if (memcmp(sections->sections[i].md5, sample->sections->sections[i].md5, sizeof(stats_section_t)))
639
if (i == sections->nsections)
647
for (i=0; sample->virus_name[i] != NULL; i++)
648
if (!strcmp(sample->virus_name[i], virname))
655
void cl_engine_set_clcb_stats_submit(struct cl_engine *engine, clcb_stats_submit callback)
657
engine->cb_stats_submit = callback;
660
void cl_engine_set_stats_set_cbdata(struct cl_engine *engine, void *cbdata)
662
engine->stats_data = cbdata;
665
void cl_engine_set_clcb_stats_add_sample(struct cl_engine *engine, clcb_stats_add_sample callback)
667
engine->cb_stats_add_sample = callback;
670
void cl_engine_set_clcb_stats_remove_sample(struct cl_engine *engine, clcb_stats_remove_sample callback)
672
engine->cb_stats_remove_sample = callback;
675
void cl_engine_set_clcb_stats_decrement_count(struct cl_engine *engine, clcb_stats_decrement_count callback)
677
engine->cb_stats_decrement_count = callback;
680
void cl_engine_set_clcb_stats_flush(struct cl_engine *engine, clcb_stats_flush callback)
682
engine->cb_stats_flush = callback;
685
void cl_engine_set_clcb_stats_get_num(struct cl_engine *engine, clcb_stats_get_num callback)
687
engine->cb_stats_get_num = callback;
690
void cl_engine_set_clcb_stats_get_size(struct cl_engine *engine, clcb_stats_get_size callback)
692
engine->cb_stats_get_size = callback;
695
void cl_engine_set_clcb_stats_get_hostid(struct cl_engine *engine, clcb_stats_get_hostid callback)
697
engine->cb_stats_get_hostid = callback;
700
void cl_engine_stats_enable(struct cl_engine *engine)
702
engine->cb_stats_add_sample = clamav_stats_add_sample;
703
engine->cb_stats_submit = clamav_stats_submit;