Skyld AV  0.6
On access virus scanning for Linux
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
main.cc
Go to the documentation of this file.
1 /*
2  * File: skyldav.c
3  *
4  * Copyright 2012 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  */
19 
24 #include <sys/capability.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <iostream>
28 #include <signal.h>
29 #include <sstream>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <string>
34 #include <syslog.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <unistd.h>
38 #include "conf.h"
39 #include "config.h"
40 #include "Environment.h"
41 #include "FanotifyPolling.h"
42 #include "Messaging.h"
43 #include "skyldav.h"
44 #include "StringSet.h"
45 
53 static int configurationCallback(const char *key, const char *value, void *info) {
54  Environment *e = static_cast<Environment *> (info);
55  int ret = 0;
56 
57  if (e == NULL) {
58  throw 0;
59  }
60 
61  if (!strcmp(key, "CACHE_MAX_SIZE")) {
62  unsigned int cacheMaxSize;
63 
64  std::stringstream ss(value);
65  ss >> cacheMaxSize;
66  if (ss.fail()) {
67  ret = 1;
68  }
69  e->setCacheMaxSize(cacheMaxSize);
70  } else if (!strcmp(key, "CLEAN_CACHE_ON_UPDATE")) {
71  int cleanCacheOnUpdate = 1;
72 
73  if (!strcmp(value, "yes")) {
75  } else if (!strcmp(value, "no")) {
77  } else {
78  fprintf(stderr, "illegal value '%s' for CLEAN_CACHE_ON_UPDATE \n",
79  value);
80  }
81  } else if (!strcmp(key, "EXCLUDE_PATH")) {
82  std::string val = value;
83  // Append missing trailing path separator.
84  if (0 == val.length() || *(val.rbegin()++) != '/') {
85  val += "/";
86  }
87  e->getExcludePaths()->add(val.c_str());
88  } else if (!strcmp(key, "LOCAL_FS")) {
89  e->getLocalFileSystems()->add(value);
90  } else if (!strcmp(key, "NOMARK_FS")) {
91  e->getNoMarkFileSystems()->add(value);
92  } else if (!strcmp(key, "NOMARK_MNT")) {
93  e->getNoMarkMounts()->add(value);
94  } else if (!strcmp(key, "THREADS")) {
95  int nThread;
96 
97  std::stringstream ss(value);
98  ss >> nThread;
99  if (ss.fail()) {
100  ret = 1;
101  }
102  e->setNumberOfThreads(nThread);
103  } else {
104  ret = 1;
105  }
106  return ret;
107 }
108 
114 static void hdl(int sig) {
115  if (sig == SIGINT) {
116  fprintf(stderr, "Main received SIGINT\n");
117  }
118  if (sig == SIGTERM) {
119  fprintf(stderr, "Main received SIGTERM\n");
120  }
121  if (sig == SIGUSR1) {
122  fprintf(stderr, "Main received SIGUSR1\n");
123  }
124 }
125 
129 static void pidfile() {
130  char buffer[40];
131  int len;
132  const char *filename = PID_FILE;
133  int fd;
134  int ret;
135 
136  fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY,
137  S_IRUSR | S_IWUSR);
138  if (fd == -1) {
139  std::stringstream msg;
140  msg << "Cannot create pid file '" << filename << "'";
142  }
143  len = snprintf(buffer, sizeof (buffer), "%d", (int) getpid());
144  ret = write(fd, buffer, len);
145  if (ret == -1) {
146  std::stringstream msg;
147  char errbuf[256];
148  msg << "Cannot write to pid file '" << filename << "': "
149  << strerror_r(errno, errbuf, sizeof(errbuf));
151  }
152  close(fd);
153 }
154 
158 static void help() {
159  printf("%s", HELP_TEXT);
160  exit(EXIT_FAILURE);
161 }
162 
166 static void version() {
167  printf("Skyld AV, version %s\n", VERSION);
168  printf("%s", VERSION_TEXT);
169  exit(EXIT_SUCCESS);
170 }
171 
178 static int capable(cap_value_t cap) {
179  cap_t caps;
180  cap_flag_value_t value;
181  int ret = 0;
182  caps = cap_get_proc();
183  if (caps == NULL) {
184  fprintf(stderr, "Cannot access capabilities\n");
185  return 0;
186  }
187  if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &value) == -1) {
188  fprintf(stderr, "Cannot get capability 1.\n");
189  } else if (value == CAP_SET) {
190  ret = 1;
191  }
192  if (cap_free(caps)) {
193  fprintf(stderr, "Failure to free capability state\n");
194  ret = 0;
195  };
196  return ret;
197 }
198 
202 static void authcheck(Environment *e) {
203  if (!capable(CAP_SYS_ADMIN)) {
204  fprintf(stderr,
205  "Missing capability CAP_SYS_ADMIN.\n"
206  "Call the program as root.\n");
207  delete e;
208  exit(EXIT_FAILURE);
209  }
210 }
211 
215 static void daemonize(Environment *e) {
216  pid_t pid;
217 
218  // Check if this process is already a daemon.
219  if (getppid() == 1) {
220  return;
221  }
222 
223  // Do not wait for children.
224  signal(SIGCHLD, SIG_IGN);
225 
226  // Create child process.
227  pid = fork();
228  if (pid == -1) {
229  perror("Cannot fork");
230  delete e;
231  exit(EXIT_FAILURE);
232  }
233  if (pid != 0) {
234  // Exit calling process.
235  exit(EXIT_SUCCESS);
236  }
237  // Change working directory.
238  if (chdir("/") == -1) {
239  perror("Cannot change directory");
240  delete e;
241  exit(EXIT_FAILURE);
242  }
243  // Set the user file creation mask.
244  umask(022);
245  // Set new session ID
246  if (setsid() == -1) {
247  perror("Cannot create session");
248  }
249  // Redirect standard files
250  if (NULL == freopen("/dev/null", "r", stdin)) {
251  perror("Cannot redirect /dev/null to stdin");
252  };
253  if (NULL == freopen("/dev/null", "w", stdout)) {
254  perror("Cannot redirect stdout to /dev/null");
255  };
256  if (NULL == freopen("/dev/null", "w", stderr)) {
257  perror("Cannot redirect stderr to /dev/null");
258  };
259 }
260 
268 int main(int argc, char *argv[]) {
269  // environment
270  Environment *e;
271  // running as daemon
272  int daemonized = 0;
273  // shall run as daemon
274  int shalldaemonize = 0;
275  // action to take when signal occurs
276  struct sigaction act;
277  // signal mask
278  sigset_t blockset;
279  // counter
280  int i;
281  // configuration file
282  char *cfile = (char *) CONF_FILE;
283  // Fanotify polling object
284  FanotifyPolling *fp;
285  // Message level
286  int messageLevel = Messaging::INFORMATION;
287  // Number of threads
288  int nThread;
289 
290  e = new Environment();
291 
292  // Set the number of threads to the number of available CPUs.
293  nThread = sysconf(_SC_NPROCESSORS_ONLN);
294  if (nThread < 1) {
295  // Use at least one thread.
296  nThread = 1;
297  }
298  e->setNumberOfThreads(nThread);
299 
300  // Analyze command line options.
301  for (i = 1; i < argc; i++) {
302  char *opt;
303 
304  opt = argv[i];
305  if (*opt == '-') {
306  opt++;
307  } else {
308  help();
309  }
310  if (*opt == '-') {
311  opt++;
312  }
313  switch (*opt) {
314  case 'c':
315  // configuration file
316  i++;
317  if (i < argc) {
318  cfile = argv[i];
319  } else {
320  help();
321  }
322  break;
323  case 'd':
324  // daemonize
325  shalldaemonize = 1;
326  break;
327  case 'm':
328  // message level
329  i++;
330  if (i < argc) {
331  std::istringstream(argv[i]) >> messageLevel;
332  } else {
333  help();
334  }
335  if (messageLevel > Messaging::ERROR
336  || messageLevel < Messaging::DEBUG) {
337  help();
338  }
339  break;
340  case 'v':
341  // version
342  version();
343  ;
344  break;
345  default:
346  // all others
347  help();
348  }
349  }
350 
351  // Parse configuration file.
352  if (parseConfigurationFile(cfile, configurationCallback, (void *) e)) {
353  return EXIT_FAILURE;
354  }
355 
356  // Check number of threads.
357  if (nThread < 1) {
358  fprintf(stderr,
359  "At least one thread is needed for scanning.\n");
360  return EXIT_FAILURE;
361  }
362 
363  // Check authorization.
364  authcheck(e);
365 
366  // Daemonize if requested.
367  if (shalldaemonize) {
368  daemonize(e);
369  daemonized = 1;
370  }
371 
372  Messaging::setLevel((Messaging::Level) messageLevel);
373 
374  // If daemonized write PID file.
375  if (daemonized) {
376  pidfile();
377  }
378 
379  Messaging::message(Messaging::DEBUG, "Starting on access scanning.");
380 
381  // Block signals.
382  sigemptyset(&blockset);
383  sigaddset(&blockset, SIGUSR1);
384  if (sigprocmask(SIG_BLOCK, &blockset, NULL) == -1) {
385  Messaging::error("main, pthread_sigmask");
386  return EXIT_FAILURE;
387  }
388 
389  // Set handler for SIGTERM.
390  act.sa_handler = hdl;
391  sigemptyset(&act.sa_mask);
392  act.sa_flags = 0;
393  if (sigaction(SIGTERM, &act, NULL)
394  || sigaction(SIGINT, &act, NULL)
395  || sigaction(SIGUSR1, &act, NULL)) {
396  Messaging::error("main, sigaction");
397  return EXIT_FAILURE;
398  }
399 
400  try {
401  fp = new FanotifyPolling(e);
402  } catch (FanotifyPolling::Status ex) {
404  "Failure starting fanotify listener.");
405  delete e;
406  return EXIT_FAILURE;
407  }
408 
409  Messaging::message(Messaging::INFORMATION, "On access scanning started.");
410  if (daemonized) {
411  pause();
412  } else {
413  printf("Press any key to terminate\n");
414  getchar();
415  }
416 
417  try {
418  delete fp;
419  } catch (FanotifyPolling::Status e) {
420  }
421  Messaging::message(Messaging::INFORMATION, "On access scanning stopped.");
422  delete e;
424  printf("done\n");
425  return EXIT_SUCCESS;
426 }
static void hdl(int sig)
Handles signal.
Definition: main.cc:114
static void teardown()
Deletes the singleton.
Definition: Messaging.cc:167
static void setLevel(const enum Level)
Sets message level.
Definition: Messaging.cc:149
Error, e.g. malfunction of the code, malware detected.
Definition: Messaging.h:56
StringSet * getLocalFileSystems()
Gets the list of file systems considered local. This list can be used to decide if scan results shall...
Definition: Environment.cc:83
void setCacheMaxSize(unsigned int)
Sets the maximum number of entries in the cache with scan results.
Definition: Environment.cc:101
const char * CONF_FILE
Definition: skyldav.h:32
void add(const char *value)
Adds entry to string set.
Definition: StringSet.cc:38
int parseConfigurationFile(char *filename, conf_cb cb, void *info)
Parses configuration file. If cb is NULL the key value pairs are output to the console. Returns 0 if successful.
Definition: conf.c:123
const char * VERSION_TEXT
Definition: notify.h:41
Send messages.
Set of strings.
static void version()
Shows version information and exits.
Definition: main.cc:166
static void error(const std::string &)
Sends an error message based on errno.
Definition: Messaging.cc:88
static int configurationCallback(const char *key, const char *value, void *info)
Callback function for reading configuration file.
Definition: main.cc:53
StringSet * getNoMarkMounts()
Gets the list of mounts not to be scanned.
Definition: Environment.cc:73
static void pidfile()
Creates pidfile for daemon.
Definition: main.cc:129
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
static void authcheck(Environment *e)
Checks authorization.
Definition: main.cc:202
void setCleanCacheOnUpdate(int)
Sets if cache shall be cleaned when the virus scanner receives a new pattern file.
Definition: Environment.cc:129
int main(int argc, char *argv[])
Main.
Definition: main.cc:268
void setNumberOfThreads(int)
sets the number of threads used to call the virus scanner.
Definition: Environment.cc:138
Poll fanotify events.
static void help()
Prints help message and exits.
Definition: main.cc:158
On access virus scanner.
StringSet * getNoMarkFileSystems()
Gets the list of file systems that shall not be scanned.
Definition: Environment.cc:64
Envronment.
static int capable(cap_value_t cap)
Check if the process has a capability.
Definition: main.cc:178
const char * PID_FILE
Definition: skyldav.h:64
Polls fanotify events.
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
Level
Message levels available.
Definition: Messaging.h:40
static void daemonize(Environment *e)
Daemonizes.
Definition: main.cc:215
const char * HELP_TEXT
Definition: notify.h:32
Analyze configuration file.
StringSet * getExcludePaths()
Gets the set of paths that shall not be scanned.
Definition: Environment.cc:55