Skyld AV  0.6
On access virus scanning for Linux
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
VirusScan.cc
Go to the documentation of this file.
1 /*
2  * File: virusscan.c
3  *
4  * Copyright 2013 Heinrich Schuchardt <xypron.glpk@gmx.de>
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
23 #include <cstring>
24 #include <ctime>
25 #include <limits.h>
26 #include <signal.h>
27 #include <sstream>
28 #include <stdio.h>
29 #include <syslog.h>
30 #include "unistd.h"
31 #include "VirusScan.h"
32 #include "Messaging.h"
33 
38  int ret;
39 
40  env = e;
41  status = RUNNING;
42  engineRefCount = 0;
43  pthread_mutex_init(&mutexEngine, NULL);
44  pthread_mutex_init(&mutexUpdate, NULL);
45 
46  ret = cl_init(CL_INIT_DEFAULT);
47  if (ret != CL_SUCCESS) {
48  std::stringstream msg;
49  msg << "cl_init() error: " << cl_strerror(ret);
51  throw SCANERROR;
52  }
53 
54  // Create virus scan engine.
55  engine = createEngine();
56  // Initialize monitoring of pattern update.
57  dbstat_clear();
58 
59  if (createThread()) {
60  Messaging::message(Messaging::ERROR, "Cannot create thread.");
61  cl_engine_free(engine);
62  throw SCANERROR;
63  }
64 }
65 
71 struct cl_engine *VirusScan::createEngine() {
72  int ret;
73  unsigned int sigs;
74  cl_engine *e;
75 
76  Messaging::message(Messaging::DEBUG, "Loading virus database");
77  e = cl_engine_new();
78  if (e == NULL) {
80  "Can't create new virus scan engine.");
81  throw SCANERROR;
82  }
83  // sigs must be zero before calling cl_load.
84  sigs = 0;
85  ret = cl_load(cl_retdbdir(), e, &sigs, CL_DB_STDOPT);
86  if (ret != CL_SUCCESS) {
87  std::stringstream msg;
88  msg << "cl_retdbdir() error: " << cl_strerror(ret);
90  cl_engine_free(e);
91  throw SCANERROR;
92  } else {
93  std::stringstream msg;
94  msg << sigs << " signatures loaded";
96  }
97  if ((ret = cl_engine_compile(e)) != CL_SUCCESS) {
98  std::stringstream msg;
99  msg << "cl_engine_compile() error: " << cl_strerror(ret);
101  cl_engine_free(e);
102  throw SCANERROR;
103  }
104  do {
105  int err;
106  time_t db_time;
107  uint version;
108  std::stringstream msg;
109  char buffer[80];
110  struct tm *timeinfo;
111  version = (uint) cl_engine_get_num(e, CL_ENGINE_DB_VERSION, &err);
112  if (err != CL_SUCCESS) {
113  break;
114  }
115  db_time = (time_t) cl_engine_get_num(e, CL_ENGINE_DB_TIME, &err);
116  if (err != CL_SUCCESS) {
117  break;
118  }
119  timeinfo = gmtime(&db_time);
120  strftime(buffer, sizeof (buffer), "%F %T UTC", timeinfo);
121  msg << "ClamAV database version " << version << ", " << buffer;
123  } while (0);
124  return e;
125 }
126 
131 void VirusScan::destroyEngine(cl_engine * e) {
132  int ret;
133  ret = cl_engine_free(e);
134  if (ret != 0) {
135  std::stringstream msg;
136  msg << "cl_engine_free() error: " << cl_strerror(ret);
138  throw SCANERROR;
139  }
140 }
141 
147 struct cl_engine * VirusScan::getEngine() {
148  struct cl_engine *ret = NULL;
149 
150  // Wait for update to complete
151  pthread_mutex_lock(&mutexUpdate);
152  pthread_mutex_lock(&mutexEngine);
153 
154  ret = engine;
155  // Increase reference count.
156  engineRefCount++;
157 
158  pthread_mutex_unlock(&mutexEngine);
159  pthread_mutex_unlock(&mutexUpdate);
160  return ret;
161 }
162 
169  int ret;
170 
171  if (pthread_create(&updateThread, NULL, updater, this)) {
172  ret = 1;
173  } else {
174  ret = 0;
175  }
176  return ret;
177 }
178 
184  int ret = 0;
185  if (cl_statchkdir(&dbstat) == 1) {
186  ret = 1;
187  cl_statfree(&dbstat);
188  cl_statinidir(cl_retdbdir(), &dbstat);
189  }
190  return ret;
191 }
192 
197  memset(&dbstat, 0, sizeof (struct cl_stat));
198  cl_statinidir(cl_retdbdir(), &dbstat);
199 }
200 
205  cl_statfree(&dbstat);
206 }
207 
214 void VirusScan::log_virus_found(const int fd, const char *virname) {
215  int path_len;
216  char path[PATH_MAX + 1];
217  std::stringstream msg;
218 
219  snprintf(path, sizeof (path), "/proc/self/fd/%d", fd);
220  path_len = readlink(path, path, sizeof (path) - 1);
221  if (path_len < 0) {
222  path_len = 0;
223  }
224  path[path_len] = '\0';
225  msg << "Virus \"" << virname << "\" detected in file \"" << path << "\".";
227 }
228 
233  pthread_mutex_lock(&mutexEngine);
234  engineRefCount--;
235  pthread_mutex_unlock(&mutexEngine);
236 }
237 
243 int VirusScan::scan(const int fd) {
244  int success = SCANOK;
245  int ret;
246  const char *virname;
247 
248  ret = cl_scandesc(fd, &virname, NULL, getEngine(), CL_SCAN_STDOPT);
249  switch (ret) {
250  case CL_CLEAN:
251  success = SCANOK;
252  break;
253  case CL_VIRUS:
254  log_virus_found(fd, virname);
255  success = SCANVIRUS;
256  break;
257  default:
258  std::stringstream msg;
259  msg << "cl_scandesc() error: " << cl_strerror(ret);
261  success = SCANOK;
262  break;
263  }
264  releaseEngine();
265  return success;
266 }
267 
274 void * VirusScan::updater(void *virusScan) {
275  int count = 0;
276  cl_engine *e;
277  struct timespec interval = {
278  1,
279  0
280  };
281  struct timespec interval2 = {
282  0,
283  100000
284  };
285  VirusScan *vs;
286 
287  vs = static_cast<VirusScan *> (virusScan);
288 
289  for (;;) {
290  if (vs->status == STOPPING) {
291  break;
292  }
293  nanosleep(&interval, NULL);
294  count++;
295  // Every minute check for virus database updates
296  if (count >= 60) {
297  if (vs->dbstat_check()) {
299  "ClamAV database update detected.");
300  try {
301  // Create the new engine.
302  e = vs->createEngine();
303  // Stop scanning.
304  pthread_mutex_lock(&(vs->mutexUpdate));
305  // Wait for all running scans to be finished.
306  for (;;) {
307  pthread_mutex_lock(&(vs->mutexEngine));
308  if (vs->engineRefCount == 0) {
309  break;
310  }
311  pthread_mutex_unlock(&(vs->mutexEngine));
312  nanosleep(&interval2, NULL);
313  }
314  // Destroy the old engine
315  vs->destroyEngine(vs->engine);
316  vs->engine = e;
317  if (vs->env->isCleanCacheOnUpdate()) {
318  vs->env->getScanCache()->clear();
319  }
320  pthread_mutex_unlock(&(vs->mutexEngine));
322  "Using updated ClamAV database.");
323  } catch (Status& e) {
324  }
325  // Allow scanning.
326  pthread_mutex_unlock(&(vs->mutexUpdate));
327  }
328  }
329  }
330  vs->status = STOPPED;
331  return NULL;
332 }
333 
338  struct timespec interval = {
339  0,
340  1000000
341  };
342 
343  status = STOPPING;
344  do {
345  nanosleep(&interval, NULL);
346  } while (status == STOPPING);
347 
349  pthread_mutex_destroy(&mutexUpdate);
350  pthread_mutex_destroy(&mutexEngine);
351  dbstat_free();
352 }
~VirusScan()
Deletes the virus scanner.
Definition: VirusScan.cc:337
int engineRefCount
Reference count of virus scan enginge.
Definition: VirusScan.h:100
void dbstat_free()
Frees database status.
Definition: VirusScan.cc:204
pthread_t updateThread
Thrad for updating.
Definition: VirusScan.h:96
static void version()
Shows version information and exits.
Definition: notify.cc:57
Error, e.g. malfunction of the code, malware detected.
Definition: Messaging.h:56
void log_virus_found(const int fd, const char *virname)
Writes log entry.
Definition: VirusScan.cc:214
Status
Status of virus scanning.
Definition: VirusScan.h:44
struct cl_engine * getEngine()
Gets reference to virus scan engine.
Definition: VirusScan.cc:147
struct cl_engine * createEngine()
Creates a new virus scan engine.
Definition: VirusScan.cc:71
static void * updater(void *)
Thread to update engine.
Definition: VirusScan.cc:274
struct cl_stat dbstat
Struture indicating if database has changed.
Definition: VirusScan.h:76
Send messages.
Scans files for viruses.
pthread_mutex_t mutexEngine
Mutex for accessing the engine.
Definition: VirusScan.h:84
Environment * env
environment
Definition: VirusScan.h:72
void destroyEngine(cl_engine *)
Definition: VirusScan.cc:131
void clear()
Removes all entries from the cache.
Definition: ScanCache.cc:104
enum RunStatus status
Definition: VirusScan.h:92
Information, e.g. access scanning has started.
Definition: Messaging.h:48
static void message(const enum Level, const std::string &)
Sends message.
Definition: Messaging.cc:101
int scan(const int fd)
Scans file for virus.
Definition: VirusScan.cc:243
pthread_mutex_t mutexUpdate
Mutex for accessing the engine.
Definition: VirusScan.h:88
int isCleanCacheOnUpdate()
Determines if cache shall be cleaned when the virus scanner receives a new pattern file...
Definition: Environment.cc:46
void dbstat_clear()
Clears database status.
Definition: VirusScan.cc:196
A virus was found.
Definition: VirusScan.h:56
int dbstat_check()
Checks if database has changed. 0 = unchanged, 1 = changed.
Definition: VirusScan.cc:183
void releaseEngine()
Definition: VirusScan.cc:232
An error occured.
Definition: VirusScan.h:52
ScanCache * getScanCache()
Gets the scan cache.
Definition: Environment.cc:110
struct cl_engine * engine
Reference to virus scan engine.
Definition: VirusScan.h:80
VirusScan(Environment *)
Initializes virus scan engine.
Definition: VirusScan.cc:37
Debugging information only to be shown in the console.
Definition: Messaging.h:44
The environment holds variables that are shared by instances of multiple classes. ...
Definition: Environment.h:38
Scans files for viruses.
Definition: VirusScan.h:38
int createThread()
Creates a new thread for managing the scan engine.
Definition: VirusScan.cc:168