ze-filter  (ze-filter-0.8.0-develop-180218)
ze-avclient.c
Go to the documentation of this file.
1 /*
2  *
3  * ze-filter - Mail Server Filter for sendmail
4  *
5  * Copyright (c) 2001-2018 - Jose-Marcio Martins da Cruz
6  *
7  * Auteur : Jose Marcio Martins da Cruz
8  * jose.marcio.mc@gmail.org
9  *
10  * Historique :
11  * Creation : janvier 2002
12  *
13  * This program is free software, but with restricted license :
14  *
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19  *
20  * More details about ze-filter license can be found at ze-filter
21  * web site : http://foss.jose-marcio.org
22  */
23 
24 #include <ze-sys.h>
25 
26 #include "libmilter/mfapi.h"
27 
28 #include "ze-avclient.h"
29 
30 #include "ze-filter.h"
31 
32 #define DEBUG_LEVEL 15
33 
34 #define MAX_ERR 8
35 #define AVRD_TO 10
36 
37 
38 /* ****************************************************************************
39  * *
40  * *
41  **************************************************************************** */
42 static int decode_answer(char *, size_t, char *, size_t, char *);
43 static int decode_answer_clamav(char *, size_t, char *, size_t,
44  char *);
45 
46 static bool args_ok = FALSE;
47 static int inetport = -1;
48 static int socktype = -1;
49 static char *sockname = NULL;
50 static char *inethost = NULL;
51 
52 
53 /* ****************************************************************************
54  * *
55  * *
56  **************************************************************************** */
57 static bool
58 av_decode_args(arg)
59  char *arg;
60 {
61  char *p = arg;
62 
63  char *s = "unix:";
64 
65  FREE(sockname);
66  FREE(inethost);
67  socktype = -1;
68  inetport = -1;
69 
70  if (strncasecmp(p, s, strlen(s)) == 0)
71  {
72  p += strlen(s);
73  if (strlen(p) > 0)
74  {
75  if ((sockname = strdup(p)) == NULL)
76  {
77  ZE_LogSysError("strdup(sockname) error");
78  return FALSE;
79  }
80  }
81  socktype = AF_UNIX;
82  return TRUE;
83  }
84 
85  s = "local:";
86  if (strncasecmp(p, s, strlen(s)) == 0)
87  {
88  p += strlen(s);
89  if (strlen(p) > 0)
90  {
91  if ((sockname = strdup(p)) == NULL)
92  {
93  ZE_LogSysError("strdup(sockname) error");
94  return FALSE;
95  }
96  }
97  socktype = AF_UNIX;
98  return TRUE;
99  }
100 
101  s = "inet:";
102  if (strncasecmp(p, s, strlen(s)) == 0)
103  {
104  char tmp[16];
105  int n;
106 
107  p += strlen(s);
108  n = strspn(p, "0123456789");
109  if ((n > 0) && (n < sizeof (tmp)))
110  {
111  strncpy(tmp, p, n);
112  tmp[n] = '\0';
113 
114  inetport = atoi(tmp);
115 
116  p += n;
117  if (*p == '@')
118  p++;
119  if (strlen(p) > 0)
120  inethost = strdup(p);
121  else
122  inethost = strdup("localhost");
123  if (inethost == NULL)
124  ZE_LogSysError("strdup(inethost) error");
125  }
126 
127  if ((inethost == NULL) || (inetport < 0))
128  {
129  FREE(inethost);
130  inetport = -1;
131  return FALSE;
132  }
133  socktype = AF_INET;
134  return TRUE;
135  }
136 
137  return FALSE;
138 }
139 
140 
141 /* ****************************************************************************
142  * *
143  * *
144  **************************************************************************** */
145 static bool
146 av_client_init()
147 {
148  static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
149 
150  MUTEX_LOCK(&mutex);
151  if (!args_ok)
152  {
153  char *scan_arg = NULL;
154 
155  if ((scan_arg = cf_get_str(CF_SCANNER_SOCK)) != NULL)
156  args_ok = av_decode_args(scan_arg);
157 
158  ZE_MessageInfo(DEBUG_LEVEL, "SOCK : %s",
159  STRNULL(scan_arg, "NULL"));
160  ZE_MessageInfo(DEBUG_LEVEL, "SOCKTYPE : %d", socktype);
161  ZE_MessageInfo(DEBUG_LEVEL, "SOCKNAME : %s",
162  STRNULL(sockname, "NULL"));
163  ZE_MessageInfo(DEBUG_LEVEL, "INETHOST : %s",
164  STRNULL(inethost, "NULL"));
165  ZE_MessageInfo(DEBUG_LEVEL, "INETPORT : %d", inetport);
166  ZE_MessageInfo(DEBUG_LEVEL, "INIT OK : %d %s", args_ok,
167  STRBOOL(args_ok, "TRUE", "FALSE"));
168  }
169  MUTEX_UNLOCK(&mutex);
170 
171  return args_ok;
172 }
173 
174 
175 /* ****************************************************************************
176  * *
177  * *
178  **************************************************************************** */
179 static int
180 disconnect2server(sd)
181  int sd;
182 {
183  if (sd >= 0)
184  {
185  shutdown(sd, 2);
186  close(sd);
187  }
188  return -1;
189 }
190 
191 /* ****************************************************************************
192  * *
193  * *
194  **************************************************************************** */
195 static int
196 connect2server()
197 {
198  int sd = -1;
199 
200  if (socktype == AF_INET)
201  {
202  struct sockaddr_in his_sock;
203  struct hostent *hp;
204 
205  if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
206  {
207  ZE_LogSysError("AF_INET : socket");
208  return -1;
209  }
210 
211  /* adresse destinataire XXX */
212  if ((hp = gethostbyname(inethost)) == NULL)
213  {
214  ZE_LogSysError("gethostbyname(%s)", STRNULL(inethost, "NULL"));
215  return -1;
216  }
217 
218  memcpy((char *) &his_sock.sin_addr, hp->h_addr, hp->h_length);
219  his_sock.sin_family = AF_INET;
220  his_sock.sin_port = htons(inetport);
221 
222 #if 0
223  if (ze_logLevel >= DEBUG_LEVEL)
224  log_sock_addr(&his_sock);
225 #endif
226 
227  /* emission sur sd vers his_sock d'un message de taille size */
228  if (connect_timed(sd, (struct sockaddr *) &his_sock, sizeof (his_sock), 10) != 0)
229  {
230  ZE_LogSysError("connect error (%s:%d)", STRNULL(inethost, "NULL"),
231  inetport);
232  sd = disconnect2server(sd);
233  }
234  return sd;
235  }
236 
237  if (socktype == AF_UNIX)
238  {
239  struct sockaddr_un his_sock;
240 
241  if ((sockname != NULL) && (strlen(sockname) == 0))
242  {
243  ZE_LogSysError("AF_UNIX : No sockname given...");
244  return -1;
245  }
246 
247  if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
248  {
249  ZE_LogSysError("AF_UNIX : socket");
250  return -1;
251  }
252 
253  memset(&his_sock, 0, sizeof (his_sock));
254  his_sock.sun_family = AF_UNIX;
255 
256  strncpy(his_sock.sun_path, sockname, strlen(sockname) + 1);
257 
258  /* emission sur sd vers his_sock d'un message de taille size */
259  if (connect(sd, (struct sockaddr *) &his_sock, sizeof (his_sock)) != 0)
260  {
261  ZE_LogSysError("connect error (%s)", STRNULL(sockname, "NULL"));
262  sd = disconnect2server(sd);
263  }
264 
265  return sd;
266  }
267 
268  ZE_MessageError(8, "Family socket unknown... %d", socktype);
269 
270  return -1;
271 }
272 
273 #define BUFSIZE 2048
274 
275 /* ****************************************************************************
276  * *
277  * *
278  **************************************************************************** */
279 static int
280 read_scanner_answer(sd, buf, sz, to)
281  int sd;
282  char *buf;
283  size_t sz;
284  int to;
285 {
286  size_t nb, nbr, ntr = 0;
287  char *ptr;
288 
289  nbr = sz;
290  ptr = buf;
291 
292  nb = 0;
293  switch (jfd_ready(sd, ZE_SOCK_READ, to))
294  {
295  case ZE_SOCK_READY:
296  nb = recv(sd, ptr, nbr, 0);
297  ptr += nb;
298  nbr -= nb;
299  ntr += nb;
300  break;
301  case ZE_SOCK_TIMEOUT:
302  break;
303  default:
304  ZE_LogMsgWarning(0, "Error waiting for antivirus answer...");
305  break;
306  }
307 
308  return nb >= 0;
309 }
310 
311 /* ****************************************************************************
312  * *
313  * *
314  **************************************************************************** */
315 int
316 av_client(answer, sz_answer, msg, sz_msg, question)
317  char *answer;
318  size_t sz_answer;
319  char *msg;
320  size_t sz_msg;
321  char *question;
322 {
323  int sd;
324  char buf[BUFSIZE];
325  int nb;
326  int res = AV_OK;
327  int protocol = cf_get_int(CF_SCANNER_PROTOCOL);
328 
329  int av_timeout = cf_get_int(CF_SCANNER_TIMEOUT);
330 
331  if (av_timeout < 5)
332  av_timeout = AVRD_TO;
333 
334  ZE_LogMsgInfo(DEBUG_LEVEL, "Entering...");
335 
336  if ((answer == NULL) || (question == NULL) || (strlen(question) == 0))
337  {
338  ZE_LogMsgError(0, "question=(%s) answer=(%s)",
339  STRNULL(question, "NULL"), STRNULL(answer, "NULL"));
340  return AV_ZERO;
341  }
342 
343  memset(buf, 0, sizeof (buf));
344  /* on cree le socket d'emission ... */
345 
346  if (!args_ok && !av_client_init())
347  return AV_ZERO;
348 
349  sd = connect2server();
350 
351  if (sd < 0)
352  {
353  ZE_LogMsgWarning(0, "Can't connect to antivirus scanner server");
354  res = AV_ERROR;
355 
356  goto fin;
357  }
358 
359  {
360  int r = 0;
361 
362  memset(buf, 0, sizeof (buf));
363  switch (protocol)
364  {
365  case OPT_INTERNAL:
366  (void) read_scanner_answer(sd, buf, sizeof (buf), 5);
367  snprintf(buf, sizeof (buf), "SCANFILE %s\n", question);
368  break;
369  case OPT_CLAMAV:
370  snprintf(buf, sizeof (buf), "nSCAN %s\n", question);
371  break;
372  default:
373  snprintf(buf, sizeof (buf), "SCANFILE %s\n", question);
374  break;
375  }
376 
377  ZE_LogMsgDebug(DEBUG_LEVEL, "Let's check if ready...");
378  if ((r = jfd_ready(sd, ZE_SOCK_WRITE, 10000)) == ZE_SOCK_READY)
379  {
380  ZE_LogMsgDebug(DEBUG_LEVEL, "READY...! Let's go !");
381  if ((nb = sendto(sd, buf, strlen(buf), 0, NULL, 0)) < 0)
382  {
383  ZE_LogSysError("ze-avclient : sendto error");
384  res = AV_ERROR;
385 
386  goto fin;
387  }
388  ZE_LogMsgDebug(DEBUG_LEVEL, " Sent... %s", buf);
389  } else
390  ZE_LogSysWarning("jfd_ready returned NOT READY %d ", r);
391 
392  ZE_LogMsgDebug(DEBUG_LEVEL, "ze-avclient - SEND : %s", buf);
393  }
394 
395  if (sd >= 0)
396  {
397  time_t av_to = av_timeout * 1000;
398  int nerr = 0;
399  time_t now;
400  bool done = FALSE;
401  char *ptr;
402  size_t nbr;
403 
404  memset(buf, 0, sizeof (buf));
405 
406  if (av_to < 500)
407  av_to = 60000;
408 
409  now = time(NULL) + av_timeout;
410 
411  ptr = buf;
412  nbr = sizeof (buf);
413  do
414  {
415  switch (jfd_ready(sd, ZE_SOCK_READ, av_to))
416  {
417  case ZE_SOCK_READY:
418  nerr = 0;
419  if ((nb = recv(sd, ptr, nbr, 0)) >= 0)
420  {
421  ptr[nb] = '\0';
422  zeStrChomp(ptr);
423 
424  ptr += nb;
425  nbr -= nb;
426 #if 1
427  done = TRUE;
428 #endif
429  } else
430  {
431  nerr++;
432  ZE_LogSysWarning("Error reading antivirus answer : %ld bytes read",
433  (long) nb);
434  }
435  break;
436  case ZE_SOCK_TIMEOUT:
437  ZE_LogMsgWarning(0, "Timeout waiting for antivirus answer...");
438  res = AV_ERROR;
439  goto fin;
440  break;
441  default:
442  nerr++;
443  ZE_LogMsgWarning(0, "Error waiting for antivirus answer...");
444  res = AV_ERROR;
445  done = TRUE;
446  goto fin;
447  break;
448  }
449  if (now < time(NULL))
450  break;
451  if (nerr >= MAX_ERR)
452  {
453  ZE_LogMsgError(0, "ERROR : Too many while errors waiting for antivirus answer...");
454  res = AV_ERROR;
455  goto fin;
456  }
457  } while (!done && (nbr > 0));
458 
459  zeStrChomp(buf);
460  strlcpy(answer, buf, sz_answer);
461  ZE_LogMsgDebug(DEBUG_LEVEL, "RECV (%s)", buf);
462  switch (protocol)
463  {
464  case OPT_INTERNAL:
465  res = decode_answer(answer, sz_answer, msg, sz_msg, buf);
466  break;
467  case OPT_CLAMAV:
468  res = decode_answer_clamav(answer, sz_answer, msg, sz_msg, buf);
469  break;
470  default:
471  res = decode_answer(answer, sz_answer, msg, sz_msg, buf);
472  break;
473  }
474  ZE_LogMsgDebug(DEBUG_LEVEL, "DECODED : RES=(%d) ANSWER=(%s)", res, answer);
475  done = TRUE;
476  }
477 
478  fin:
479  /* fermeture du socket d'emission */
480  sd = disconnect2server(sd);
481 
482  return res;
483 }
484 
485 
486 /* ****************************************************************************
487  * *
488  * *
489  **************************************************************************** */
490 
491 static int
492 decode_answer(answer, sz_answer, msg, sz_msg, buf)
493  char *answer;
494  size_t sz_answer;
495  char *msg;
496  size_t sz_msg;
497  char *buf;
498 {
499  int res = 0;
500  char *tokbuf = NULL;
501 
502  if ((tokbuf = strdup(buf)) != NULL)
503  {
504  char *ptr, *s;
505 
506  for (s = strtok_r(tokbuf, "\r\n", &ptr);
507  s != NULL; s = strtok_r(NULL, "\r\n", &ptr))
508  {
509  char *expr = "^6[0-9]{2} .*";
510 
511  ZE_MessageInfo(18, "Checking %s against %s", s, expr);
512 
513  if (zeStrRegex(s, expr, NULL, NULL, TRUE))
514  {
515  char code[8];
516 
517  memset(code, 0, sizeof (code));
518  strncpy(code, s, strspn(s, "0123456789"));
519  res = atoi(code);
520  s += strspn(s, "0123456789 ");
521  strlcpy(answer, s, sz_answer);
522  strlcpy(msg, s, sz_msg);
523  if (res != 600)
524  break;
525  }
526  }
527  }
528  FREE(tokbuf);
529 
530  return res;
531 }
532 
533 /* ****************************************************************************
534  * *
535  * *
536  **************************************************************************** */
537 static int
538 decode_answer_clamav(answer, sz_answer, msg, sz_msg, buf)
539  char *answer;
540  size_t sz_answer;
541  char *msg;
542  size_t sz_msg;
543  char *buf;
544 {
545  char *p = buf;
546  int i, res;
547 
548  if ((p = strrchr(answer, ' ')) == NULL)
549  return AV_ERROR;
550 
551  memset(msg, 0, sz_msg);
552  res = AV_ERROR;
553  if (strstr(p, "FOUND") != NULL)
554  res = AV_VIRUS;
555  else
556  {
557  if (strstr(p, "OK") != NULL)
558  res = AV_OK;
559  }
560  if (res == AV_OK || res == AV_ERROR)
561  goto fin;
562 
563  p = answer;
564  p += strcspn(p, " \t");
565  p += strspn(p, " \t");
566  i = strcspn(p, " \t");
567 #if 1
568  zeSafeStrnCpy(msg, sz_msg, p, i);
569 #else
570  if (i >= sz_msg)
571  i = sz_msg - 1;
572  strncpy(msg, p, i);
573 #endif
574 
575  fin:
576  return res;
577 }
#define BUFSIZE
Definition: ze-avclient.c:273
#define STRBOOL(x, t, f)
Definition: macros.h:87
#define DEBUG_LEVEL
Definition: ze-avclient.c:32
#define strrchr
Definition: ze-sys.h:219
#define ZE_SOCK_WRITE
Definition: ze-inet.h:58
#define OPT_INTERNAL
Definition: ze-cf.h:73
#define FREE(x)
Definition: macros.h:37
#define MUTEX_UNLOCK(mutex)
Definition: macros.h:101
int ze_logLevel
Definition: zeSyslog.c:34
#define STRNULL(x, r)
Definition: macros.h:81
#define CF_SCANNER_SOCK
Definition: cfh-defs.h:98
#define AV_VIRUS
Definition: ze-avclient.h:34
#define MUTEX_LOCK(mutex)
Definition: macros.h:93
#define ZE_LogMsgInfo(level,...)
Definition: zeSyslog.h:110
pthread_mutex_t mutex
Definition: ze-connopen.c:63
int jfd_ready(int, bool, long)
Definition: ze-inet.c:529
#define FALSE
Definition: macros.h:160
#define CF_SCANNER_TIMEOUT
Definition: cfh-defs.h:100
#define strlcpy
Definition: zeString.h:32
#define ZE_LogMsgError(level,...)
Definition: zeSyslog.h:113
bool zeStrRegex(char *, char *, long *, long *, bool)
Definition: zeStrings.c:544
#define ZE_SOCK_TIMEOUT
Definition: ze-inet.h:62
#define AV_ERROR
Definition: ze-avclient.h:33
char * zeStrChomp(char *)
Definition: zeStrings.c:501
int cf_get_int(int id)
Definition: ze-cf.c:803
#define AV_OK
Definition: ze-avclient.h:32
int connect_timed(int, struct sockaddr *, socklen_t, int)
Definition: ze-client.c:639
#define ZE_LogMsgDebug(level,...)
Definition: zeSyslog.h:109
#define ZE_MessageInfo(level,...)
Definition: zeSyslog.h:90
int nb
Definition: ze-connopen.c:61
int zeSafeStrnCpy(char *, size_t, char *, size_t)
Definition: zeStrings.c:136
#define TRUE
Definition: macros.h:157
#define memcpy(d, s, n)
Definition: ze-sys.h:224
#define ZE_LogSysError(...)
Definition: zeSyslog.h:129
#define ZE_MessageError(level,...)
Definition: zeSyslog.h:93
#define ZE_LogSysWarning(...)
Definition: zeSyslog.h:128
char * cf_get_str(int id)
Definition: ze-cf.c:854
#define MAX_ERR
Definition: ze-avclient.c:34
#define ZE_LogMsgWarning(level,...)
Definition: zeSyslog.h:112
#define AVRD_TO
Definition: ze-avclient.c:35
#define OPT_CLAMAV
Definition: ze-cf.h:74
#define ZE_SOCK_READ
Definition: ze-inet.h:57
#define CF_SCANNER_PROTOCOL
Definition: cfh-defs.h:99
#define ZE_SOCK_READY
Definition: ze-inet.h:61
#define AV_ZERO
Definition: ze-avclient.h:31
int msg[MAX_SCORE+2]
Definition: ze-stats.c:41
int av_client(char *answer, size_t sz_answer, char *msg, size_t sz_msg, char *question)
Definition: ze-avclient.c:316