]> git.pld-linux.org Git - packages/snort.git/blob - snort-2.6.0.2-clamav.diff
- release changed from 0.1 to 1, problems with arch x86_64 was fixed
[packages/snort.git] / snort-2.6.0.2-clamav.diff
1 diff -uNr snort-2.6.0.2/autojunk.sh snort-2.6.0.2.clam/autojunk.sh
2 --- snort-2.6.0.2/autojunk.sh   1970-01-01 01:00:00.000000000 +0100
3 +++ snort-2.6.0.2.clam/autojunk.sh      2006-11-06 08:24:08.000000000 +0100
4 @@ -0,0 +1,7 @@
5 +#!/bin/sh
6 +# the list of commands that need to run before we do a compile
7 +aclocal -I m4
8 +autoheader
9 +automake --add-missing --copy
10 +autoconf
11 +
12 diff -uNr snort-2.6.0.2/configure.in snort-2.6.0.2.clam/configure.in
13 --- snort-2.6.0.2/configure.in  2006-08-29 21:22:16.000000000 +0200
14 +++ snort-2.6.0.2.clam/configure.in     2006-11-06 08:24:08.000000000 +0100
15 @@ -1033,6 +1033,59 @@
16         fi
17  fi
18  
19 +AC_ARG_ENABLE(clamav,
20 +[  --enable-clamav          Enable the clamav preprocessor],
21 +                enable_clamav="$enableval", enable_clamav="no")
22 +if test "$enable_clamav" = "yes"; then
23 +    CFLAGS="$CFLAGS -DCLAMAV"
24 +
25 +    AC_ARG_WITH(clamav_includes,
26 +        [  --with-clamav-includes=DIR   clamav include directory],
27 +        [with_clamav_includes="$withval"],[with_clamav_includes=no])
28 +
29 +    AC_ARG_WITH(clamav_defdir,
30 +        [  --with-clamav-defdir=DIR   clamav virusdefinitions directory],
31 +        [with_clamav_defdir="$withval"],[with_clamav_defdir=no])
32 +
33 +
34 +    if test "$with_clamav_defdir" != "no"; then
35 +        echo "Virusdefs: $with_clamav_defdir"
36 +        CFLAGS="$CFLAGS -DCLAMAV_DEFDIR=\"$with_clamav_defdir\""
37 +    fi
38 +
39 +    if test "$with_clamav_includes" != "no"; then
40 +        CPPFLAGS="${CPPFLAGS} -I${with_clamav_includes}"
41 +    fi
42 +
43 +    LCLAM=""
44 +    AC_CHECK_HEADERS(clamav.h,, LCLAM="no")
45 +    if test "$LCLAM" = "no"; then
46 +        echo
47 +        echo "   ERROR!  clamav.h header not found, go get it from"
48 +        echo "   http://www.clamav.net/ or use the --with-clamav-includes"
49 +        echo "   options, if you have it installed in an unusual place"
50 +        exit
51 +    fi
52 +
53 +    LCLAM=""
54 +    AC_CHECK_LIB(clamav,cl_scanbuff,, LCLAM="no")
55 +    if test "$LCLAM" = "no"; then
56 +        echo
57 +        echo "   ERROR!  libclamav library not found, go get it from"
58 +        echo "   http://www.clamav.net/ or make sure that the place"
59 +        echo "   you installed it is in the library path."
60 +        exit
61 +    fi
62 +
63 +    # in 0.80 cl_buildtrie is renamed to cl_build
64 +    LCLAM=""
65 +    AC_CHECK_LIB(clamav, cl_build,, LCLAM="no")
66 +    if test "$LCLAM" != "no"; then
67 +        CFLAGS="$CFLAGS -DCLAMAV_HAVE_CL_BUILD"
68 +    fi
69 +
70 +    LIBS="${LIBS} -lclamav"
71 +fi
72  
73  # let's make some fixes..
74  
75 diff -uNr snort-2.6.0.2/doc/README.clamav snort-2.6.0.2.clam/doc/README.clamav
76 --- snort-2.6.0.2/doc/README.clamav     1970-01-01 01:00:00.000000000 +0100
77 +++ snort-2.6.0.2.clam/doc/README.clamav        2006-11-06 08:24:08.000000000 +0100
78 @@ -0,0 +1,34 @@
79 ++Known limitations
80 ++=================
81 +- Please note that detection depends on ClamAV. If clam doesn't know a virus, it will not be detected. So keep your defs up-to-date.
82 +- Archives are not scanned, unless so small that it fits in one packet/uber-packet.
83 +- OLE2 virusses are not detected.
84 +- Attachments to email that are in some way encoded are not scanned.
85 +- As the clam guy's make there detection more specific it is harder for us to detect viri in on the fly packets.  Much work needs to be done to create application layer decoders - i.e. strip out actual packet payload and remove things such as http headers from packets.
86 +- turn on clamav by going into snort_inline.conf
87 +- If you don't configure an action and we detect a vrius, the virus is logged and detection is disabled for the rest of snort, and we flush the stream containing the packet in stream4. We have to do this due to logging restrictions and stream4 reassembly.
88 +
89 +preprocessor clamav
90 +
91 +This turns on the defaults for clamav which are to listen on ports 21 25 80 81 110 119 139 445 143
92 +uses the default database location of /var/lib/clamav unless another dbdir was specified at ./configure
93 +Alerts are written to alert logs no packets are rejected or dropped.
94 +
95 +options are 
96 +
97 +preprocessor clamav:  ports {portlist separated by " "}, {flow can be toclientonly or toserveronly or defaults to both} {action can be action-drop or  action-reset otherwise default to writing to alert file},{dbdir},{dbreload-time time in seconds to refresh the read of the AV signatures}, {tmpdir for fd mode}
98 +
99 +so 
100 +
101 +preprocessor clamav: ports all !25 !443 !22, action-reset
102 +
103 +
104 +will turn on clamav will listen for virus activity on all ports except 25 443 22 and send a reset and drop the packet if a virus is detected.
105 +
106 +
107 +preprocessor clamav: ports 139 445 21, toclientonly, action-drop, dbdir /var/lib2/clamav
108 +
109 +will turn on clamav, will listen for virus activity on ports 129 445 21 will only watch traffic that flows to the client, will drop the packet, sets the virus-sig database path to /var/lib2/clamav 
110 +
111 +to scan uberpackets from stream4 reassembly make sure that stream4 is initialized before ClamAV in your snort_inline.conf
112 +
113 diff -uNr snort-2.6.0.2/src/generators.h snort-2.6.0.2.clam/src/generators.h
114 --- snort-2.6.0.2/src/generators.h      2006-08-29 18:59:37.000000000 +0200
115 +++ snort-2.6.0.2.clam/src/generators.h 2006-11-06 08:24:08.000000000 +0100
116 @@ -297,6 +297,9 @@
117  
118  #define GENERATOR_DNS                             131
119  
120 +#define GENERATOR_SPP_CLAMAV                       132
121 +#define     CLAMAV_VIRUSFOUND                       1
122 +
123  /*  This is where all the alert messages will be archived for each
124      internal alerts */
125  
126 @@ -472,4 +475,6 @@
127  
128  #define PSNG_OPEN_PORT_STR "(portscan) Open Port"
129  
130 +#define CLAMAV_VIRUSFOUND_STR "(spp_clamav) Virus Found:"
131 +
132  #endif /* __GENERATORS_H__ */
133 diff -uNr snort-2.6.0.2/src/plugbase.c snort-2.6.0.2.clam/src/plugbase.c
134 --- snort-2.6.0.2/src/plugbase.c        2006-02-20 20:02:35.000000000 +0100
135 +++ snort-2.6.0.2.clam/src/plugbase.c   2006-11-06 08:24:08.000000000 +0100
136 @@ -60,6 +60,10 @@
137  #include "preprocessors/spp_sfportscan.h"
138  #include "preprocessors/spp_frag3.h"
139  
140 +#ifdef CLAMAV
141 +#include "preprocessors/spp_clamav.h"
142 +#endif /* CLAMAV */
143 +
144  /* built-in detection plugins */
145  #include "detection-plugins/sp_pattern_match.h"
146  #include "detection-plugins/sp_tcp_flag_check.h"
147 @@ -429,6 +433,9 @@
148      SetupFlow();
149      SetupPsng();
150      SetupFrag3();
151 +#ifdef CLAMAV
152 +    SetupClamAV();
153 +#endif /* CLAMAV */
154  }
155  
156  void CheckPreprocessorsConfig()
157 diff -uNr snort-2.6.0.2/src/preprocessors/Makefile.am snort-2.6.0.2.clam/src/preprocessors/Makefile.am
158 --- snort-2.6.0.2/src/preprocessors/Makefile.am 2006-02-03 15:11:46.000000000 +0100
159 +++ snort-2.6.0.2.clam/src/preprocessors/Makefile.am    2006-11-06 08:24:08.000000000 +0100
160 @@ -25,7 +25,8 @@
161  spp_frag2.c spp_frag2.h \
162  spp_frag3.c spp_frag3.h \
163  str_search.c str_search.h \
164 -stream_api.c stream_api.h
165 +stream_api.c stream_api.h \
166 +spp_clamav.c spp_clamav.h
167  
168  
169  INCLUDES = @INCLUDES@
170 diff -uNr snort-2.6.0.2/src/preprocessors/spp_clamav.c snort-2.6.0.2.clam/src/preprocessors/spp_clamav.c
171 --- snort-2.6.0.2/src/preprocessors/spp_clamav.c        1970-01-01 01:00:00.000000000 +0100
172 +++ snort-2.6.0.2.clam/src/preprocessors/spp_clamav.c   2006-11-06 08:53:57.000000000 +0100
173 @@ -0,0 +1,1031 @@
174 +/* $Id$ */
175 +/* Snort Preprocessor for Antivirus Checking with ClamAV */
176 +
177 +/*
178 +** Copyright (C) 1998-2002 Martin Roesch <roesch@sourcefire.com>
179 +** Copyright (C) 2003 Sourcefire, Inc.
180 +** Copyright (C) 2004 William Metcalf <William_Metcalf@kcmo.org> and
181 +**                    Victor Julien <victor@nk.nl>
182 +**
183 +** This program is free software; you can redistribute it and/or modify
184 +** it under the terms of the GNU General Public License as published by
185 +** the Free Software Foundation; either version 2 of the License, or
186 +** (at your option) any later version.
187 +**
188 +** This program is distributed in the hope that it will be useful,
189 +** but WITHOUT ANY WARRANTY; without even the implied warranty of
190 +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
191 +** GNU General Public License for more details.
192 +**
193 +** You should have received a copy of the GNU General Public License
194 +** along with this program; if not, write to the Free Software
195 +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
196 +*/
197 +
198 +#ifdef CLAMAV
199 +
200 +/* spp_clamav.c
201 + *
202 + * Purpose: Sends packet p to ClamAV for Antivirus checking.
203 + *
204 + * Arguments: None
205 + *
206 + * Effect: Who needs virus.rules??? :-)
207 + *
208 + * Comments:
209 + *
210 + *
211 + * TODO:
212 + * - documentation
213 + * - are the defaultports in ParseClamAVArgs ok?
214 + * - options structure like s4data in Stream4 for cl_root, VirusScanPorts, drop/reject/alert, defs dirlocation **IN PROGRESS**
215 + * - maybe more protocol specific support for less false negatives?
216 + *
217 + *
218 + * Changes:
219 + *
220 + * 2004/11/10: added code for the automatic reloading of the virusdefs
221 + *             added support for ClamAV 0.80
222 + * 2006/02/03: added check for http traffic so we better handle http downloads
223 + *             removed cl_scanbuf since it was broken anyway
224 + *             cleanups and comments added.
225 + *             added 8080 to the default ports
226 + *
227 + */
228 +#ifdef HAVE_CONFIG_H
229 +#include "config.h"
230 +#endif
231 +
232 +#ifndef DEBUG
233 +   #ifndef INLINE
234 +       #define INLINE inline
235 +   #endif
236 +#else
237 +   #ifdef INLINE
238 +       #undef INLINE
239 +   #endif
240 +   #define INLINE
241 +#endif /* DEBUG */
242 +
243 +// enable if this is included in the Snort_inline project as opposed to normal Snort
244 +//#define SNORTINLINE_SF_NET
245 +#ifdef SNORTINLINE_SF_NET
246 +#define STICKYDROP
247 +#endif
248 +
249 +#include <sys/types.h>
250 +#include <stdlib.h>
251 +#include <ctype.h>
252 +#include <rpc/types.h>
253 +#include <errno.h>
254 +#include "generators.h"
255 +#include "event_wrapper.h"
256 +#include "util.h"
257 +#include "plugbase.h"
258 +#include "parser.h"
259 +#include "decode.h"
260 +#include "debug.h"
261 +#include "mstring.h"
262 +#include "log.h"
263 +#include "spp_clamav.h"
264 +#include "stream_api.h"
265 +#include "preprocessors/spp_stream4.h"
266 +#ifdef GIDS
267 +#ifdef STICKYDROP
268 +#include "preprocessors/spp_stickydrop.h"
269 +#endif
270 +#include "inline.h"
271 +#endif
272 +
273 +#include "snort.h"
274 +#include <clamav.h>
275 +
276 +#ifdef HAVE_STRINGS_H
277 +#include <strings.h>
278 +#endif
279 +
280 +#ifdef GIDS
281 +#ifdef STICKYDROP
282 +/* stickydrop */
283 +extern SDtimeout sdt;
284 +//extern Stream4Data s4data;
285 +#endif /* STICKYDROP */
286 +#endif /* GIDS */
287 +
288 +/* we need this to stringify the CLAMAV_DEFDIR which is supplied at compiletime see:
289 +  http://gcc.gnu.org/onlinedocs/gcc-3.4.1/cpp/Stringification.html#Stringification */
290 +#define xstr(s) str(s)
291 +#define str(s) #s
292 +
293 +/* the config struct */
294 +struct ClamAVConfig
295 +{
296 +   /* scan limitations */
297 +   char toclientonly; /* if set to 1 scan only traffic to the client */
298 +   char toserveronly; /* if set to 1 scan only traffic to the server */
299 +   char VirusScanPorts[65536/8]; /* array containing info about which ports we care about */
300 +
301 +   /* actions */
302 +   char drop;
303 +   char reset;
304 +#ifdef SNORTINLINE_SF_NET
305 +   char rboth;
306 +#endif /* SNORTINLINE_SF_NET */
307 +
308 +   /* virdef dir */
309 +   char dbdir[255];
310 +
311 +   /* temp dir for file descriptors */
312 +   char desctmpdir[255];
313 +
314 +   /* reload time in seconds */
315 +   u_int16_t reloadtime;
316 +   u_int32_t next_reload_time;
317 +
318 +} clamcnf;
319 +
320 +/* pointer to ClamAV's in-memory virusdatabase */
321 +struct cl_node *cl_root;
322 +/* scanner limits */
323 +struct cl_limits clam_limits;
324 +static void ClamAVInit(u_char *);
325 +extern void SetupClamAV();
326 +static int VirusInPacket(Packet *);
327 +int check_4_http_headers(u_int8_t *, int);
328 +static void VirusChecker(Packet *, void *);
329 +extern u_int32_t event_id;
330 +
331 +/* db reloading */
332 +struct cl_stat dbstat;
333 +
334 +
335 +/*
336 + * Function: SetupClamAV()
337 + *
338 + * Purpose: Registers the preprocessor.
339 + *
340 + * Arguments: None.
341 + *
342 + * Returns: void function
343 + *
344 + */
345 +void SetupClamAV()
346 +{
347 +   RegisterPreprocessor("ClamAV", ClamAVInit);
348 +}
349 +
350 +
351 +/*
352 + * Function: ProcessPorts(u_char *)
353 + *
354 + * Purpose: Sets the port limits
355 + *
356 + * Arguments: pointer to string with portlist.
357 + *
358 + * Returns: void function
359 + *
360 + */
361 +static void ProcessPorts(u_char *portlist)
362 +{
363 +   int j = 0;
364 +   int i = 0;
365 +   char **ports;
366 +   int num_ports;
367 +   char *port;
368 +   u_int32_t portnum;
369 +
370 +   /* reset the ports array */
371 +   bzero(&clamcnf.VirusScanPorts, sizeof(clamcnf.VirusScanPorts));
372 +
373 +   ports = mSplit(portlist, " ", 40, &num_ports, 0);
374 +
375 +   /* run through the ports */
376 +   for(j = 0; j < num_ports; j++)
377 +   {
378 +       port = ports[j];
379 +
380 +       /* we need to set this port */
381 +       if(isdigit((int)port[0]))
382 +       {
383 +           portnum = atoi(port);
384 +           if(portnum > 65535)
385 +           {
386 +               FatalError("%s(%d) => Bad port list to scan: "
387 +                   "port '%d' out of range\n", portnum, file_name, file_line);
388 +           }
389 +
390 +           /* mark this port as being interesting using some portscan2-type voodoo,
391 +              and also add it to the port list string while we're at it so we can
392 +              later print out all the ports with a single LogMessage() */
393 +           clamcnf.VirusScanPorts[(portnum/8)] |= 1<<(portnum%8);
394 +       }
395 +       /* we need to unset this port */
396 +       else if(port[0] == '!')
397 +       {
398 +           for(i = 0; i < strlen(port) && port[i+1] != '\0'; i++)
399 +           {
400 +               port[i] = port[i+1];
401 +           }
402 +           port[i] = '\0';
403 +
404 +           if(isdigit((int)port[0]))
405 +           {
406 +               portnum = atoi(port);
407 +               if(portnum > 65535)
408 +               {
409 +                   FatalError("%s(%d) => Bad port list to scan: "
410 +                       "port '%d' out of range\n", portnum, file_name, file_line);
411 +               }
412 +
413 +               /* clear the bit - this removes the port from the array */
414 +               clamcnf.VirusScanPorts[(portnum/8)] &= ~(1<<(portnum%8));
415 +           }
416 +           else
417 +           {
418 +               FatalError("%s(%d) => Bad port list to scan: "
419 +                          "bad port\n", file_name, file_line);
420 +           }
421 +       }
422 +       /* we need to set all ports */
423 +       else if(!strncasecmp(port, "all", 3))
424 +       {
425 +           /* enable all ports */
426 +           for(portnum = 0; portnum <= 65535; portnum++)
427 +               clamcnf.VirusScanPorts[(portnum/8)] |= 1<<(portnum%8);
428 +       }
429 +       else if(!strncasecmp(port, "ports", 5));
430 +       else
431 +       {
432 +           FatalError("%s(%d) => Bad port list to scan: "
433 +                      "bad port\n", file_name, file_line);
434 +       }
435 +   }
436 +
437 +   mSplitFree(&ports, num_ports);
438 +
439 +   /* some pretty printing */
440 +   if(!pv.quiet_flag)
441 +   {
442 +       /* print the portlist */
443 +       LogMessage("    Ports: ");
444 +
445 +       for(portnum = 0, j = 0; portnum <= 65535; portnum++)
446 +       {
447 +           if((clamcnf.VirusScanPorts[(portnum/8)] & (1<<(portnum%8))))
448 +           {
449 +               LogMessage("%d ", portnum);
450 +               j++;
451 +           }
452 +
453 +           if(j > 20)
454 +           {
455 +               LogMessage("...\n");
456 +               return;
457 +           }
458 +       }
459 +   }
460 +}
461 +
462 +
463 + /*
464 + * Function: ParseClamAVArgs(u_char *)
465 + *
466 + * Purpose: reads the options and sets the defaults.
467 + *
468 + * Arguments: pointer to string with options
469 + *
470 + * Returns: void function
471 + */
472 +void ParseClamAVArgs(u_char *args)
473 +{
474 +   char **toks;
475 +   int num_toks;
476 +   int i = 0;
477 +   char *index;
478 +   int ports_done = 0;
479 +   char **dbdirtoks;
480 +   int num_dbdirtoks = 0;
481 +   char **dbtimetoks;
482 +   int num_dbtimetoks = 0;
483 +   char **desctmptoks;
484 +   int num_desctmptoks = 0;
485 +
486 +
487 +   /* ftp, smtp, http, pop3, nntp, samba (2x), imap */
488 +   u_char *default_ports = "21 25 80 81 110 119 139 445 143 8080";
489 +
490 +#ifdef GIDS
491 +   clamcnf.drop = 0;
492 +   clamcnf.reset = 0;
493 +#ifdef SNORTINLINE_SF_NET
494 +   clamcnf.rboth = 0;
495 +#endif /* SNORTINLINE_SF_NET */
496 +#endif /* GIDS */
497 +   clamcnf.toclientonly = 0;
498 +   clamcnf.toserveronly = 0;
499 +
500 +   /* default tmp dir */
501 +   if(strlcpy(clamcnf.desctmpdir, "/tmp", sizeof(clamcnf.desctmpdir)) >= sizeof(clamcnf.desctmpdir))
502 +   {
503 +       FatalError("The tempdir supplied at compile time is too long\n");
504 +   }
505 +
506 +#ifdef CLAMAV_DEFDIR
507 +   /* copy the default that was set at compile time, if any */
508 +   if(strlcpy(clamcnf.dbdir, xstr(CLAMAV_DEFDIR), sizeof(clamcnf.dbdir)) >= sizeof(clamcnf.dbdir))
509 +#else
510 +   /* otherwise a buildin default */
511 +   if(strlcpy(clamcnf.dbdir, "/var/lib/clamav/", sizeof(clamcnf.dbdir)) >= sizeof(clamcnf.dbdir))
512 +#endif
513 +   {
514 +       FatalError("The defdir supplied at compile time is too long\n");
515 +   }
516 +
517 +
518 +   /* reload time default to 10 minutes */
519 +   clamcnf.reloadtime = 600;
520 +
521 +
522 +   if(!pv.quiet_flag)
523 +   {
524 +       LogMessage("ClamAV config:\n");
525 +   }
526 +
527 +
528 +   /* if no args, load the default config */
529 +   if(args == NULL)
530 +   {
531 +       if(!pv.quiet_flag)
532 +       {
533 +           LogMessage("    no options, using defaults.\n");
534 +       }
535 +   }
536 +   /* process the args */
537 +   else
538 +   {
539 +       toks = mSplit(args, ",", 12, &num_toks, 0);
540 +
541 +       for(i = 0; i < num_toks; i++)
542 +       {
543 +           index = toks[i];
544 +           while(isspace((int)*index)) index++;
545 +
546 +           if(!strncasecmp(index, "ports", 5))
547 +           {
548 +               ProcessPorts(toks[i]);
549 +               ports_done = 1;
550 +           }
551 +#ifdef GIDS
552 +           else if(!strncasecmp(index, "action-reset", 12))
553 +           {
554 +              clamcnf.reset = 1;
555 +           }
556 +#ifdef SNORTINLINE_SF_NET
557 +           else if(!strncasecmp(index, "action-rboth", 12))
558 +           {
559 +              clamcnf.rboth = 1;
560 +           }
561 +#endif /* SNORTINLINE_SF_NET */
562 +           else if(!strncasecmp(index, "action-drop", 11))
563 +           {
564 +              clamcnf.drop = 1;
565 +           }
566 +#endif /* GIDS */
567 +           else if(!strncasecmp(index, "toclientonly", 12))
568 +           {
569 +              clamcnf.toclientonly = 1;
570 +           }
571 +           else if(!strncasecmp(index, "toserveronly", 12))
572 +           {
573 +              clamcnf.toserveronly = 1;
574 +           }
575 +           else if(!strncasecmp(index, "dbdir", 5))
576 +           {
577 +               /* get the argument for the option */
578 +               dbdirtoks = mSplit(index, " ", 1, &num_dbdirtoks, 0);
579 +
580 +               /* copy it to the clamcnf */
581 +               if(strlcpy(clamcnf.dbdir, dbdirtoks[1], sizeof(clamcnf.dbdir)) >= sizeof(clamcnf.dbdir))
582 +               {
583 +                   FatalError("The defdir supplied in the config is too long\n");
584 +               }
585 +             mSplitFree(&dbdirtoks, num_dbdirtoks);
586 +           }
587 +           else if(!strncasecmp(index, "dbreload-time", 13))
588 +           {
589 +               /* get the argument for the option */
590 +               dbtimetoks = mSplit(index, " ", 1, &num_dbtimetoks, 0);
591 +
592 +               if(isdigit((int)dbtimetoks[1][0]))
593 +               {
594 +                   clamcnf.reloadtime  = atoi(dbtimetoks[1]);
595 +               }
596 +               else
597 +               {
598 +                   FatalError("We need an integer in seconds for dbreload interval\n");
599 +               }
600 +             mSplitFree(&dbtimetoks, num_dbtimetoks);
601 +           }
602 +           else if((!strncasecmp(index, "descriptor-temp-dir", 19)))
603 +           {
604 +               /* get the argument for the option */
605 +               desctmptoks = mSplit(index, " ", 1, &num_desctmptoks, 0);
606 +
607 +               /* copy it to the clamcnf */
608 +               if(strlcpy(clamcnf.desctmpdir, desctmptoks[1], sizeof(clamcnf.desctmpdir)) >= sizeof(clamcnf.desctmpdir))
609 +               {
610 +                   FatalError("The tmpdir supplied in the config is too long\n");
611 +               }
612 +             mSplitFree(&desctmptoks, num_desctmptoks);
613 +           }
614 +           else
615 +           {
616 +               FatalError("%s(%d) => Bad ClamAV option specified: "
617 +                          "\"%s\"\n", file_name, file_line, toks[i]);
618 +           }
619 +       }
620 +
621 +       mSplitFree(&toks, num_toks);
622 +   }
623 +
624 +#ifdef GIDS
625 +   /* sanety checks */
626 +   if(clamcnf.drop && clamcnf.reset)
627 +   {
628 +       FatalError("Can't set action-drop and action-reset together!\n");
629 +   }
630 +#endif /* GIDS */
631 +   if(clamcnf.toclientonly && clamcnf.toserveronly)
632 +   {
633 +       FatalError("Can't set toclientonly and toserveronly together!\n");
634 +   }
635 +
636 +
637 +   /* if at this stage the ports are not yet done, load the default ports */
638 +   if(!ports_done)
639 +       ProcessPorts(default_ports);
640 +
641 +
642 +   /* some pretty printing */
643 +   if(!pv.quiet_flag)
644 +   {
645 +       /* action */
646 +#ifdef GIDS
647 +       if(clamcnf.drop == 1)
648 +           LogMessage("    Virus found action: DROP\n");
649 +       else if(clamcnf.reset == 1)
650 +           LogMessage("    Virus found action: RESET\n");
651 +#ifdef SNORTINLINE_SF_NET
652 +       else if(clamcnf.rboth == 1)
653 +           LogMessage("    Virus found action: RESET-BOTH\n");
654 +#endif /* SNORTINLINE_SF_NET */
655 +       else
656 +           LogMessage("    Virus found action: ALERT\n");
657 +#endif /* GIDS */
658 +       /* dbdir */
659 +       LogMessage("    Virus definitions dir: '%s'\n", clamcnf.dbdir);
660 +       /* limits */
661 +       LogMessage("    Virus DB reload time: '%i'\n", clamcnf.reloadtime);
662 +       if(clamcnf.toclientonly == 1)
663 +           LogMessage("    Scan only traffic to the client\n");
664 +       else if(clamcnf.toserveronly == 1)
665 +           LogMessage("    Scan only traffic to the server\n");
666 +       LogMessage("    Directory for tempfiles (file descriptor mode): '%s'\n",
667 +                           clamcnf.desctmpdir);
668 +   }
669 +}
670 +
671 +
672 +/*
673 + * Function: ClamAVInit(u_char *)
674 + *
675 + * All it does right now is register the plugin, eventually we will take port args
676 + */
677 +void ClamAVInit(u_char *args)
678 +{
679 +   int ret = 0;
680 +   int n = 0;
681 +   struct timeval t;
682 +   cl_root = NULL;
683 +
684 +   /* first parse the commandline args */
685 +   ParseClamAVArgs(args);
686 +
687 +
688 +   /* get the current time for the reloading */
689 +   memset(&t, 0, sizeof(struct timeval));
690 +   gettimeofday(&t, NULL);
691 +   clamcnf.next_reload_time = t.tv_sec + clamcnf.reloadtime;
692 +
693 +   /* setup for check */
694 +   memset(&dbstat, 0, sizeof(struct cl_stat));
695 +   cl_statinidir(clamcnf.dbdir, &dbstat);
696 +
697 +   /* open the defs dir */
698 +   ret = cl_loaddbdir(clamcnf.dbdir, &cl_root, &n);
699 +   if(ret != 0)
700 +   {
701 +       FatalError("ClamAV: cl_loaddbdir() %s\n", cl_strerror(ret));
702 +   }
703 +
704 +   /* run buildtrie */
705 +#ifdef CLAMAV_HAVE_CL_BUILD
706 +   if((ret = cl_build(cl_root)))
707 +#else
708 +   if((ret = cl_buildtrie(cl_root)))
709 +#endif /* CLAMAV_HAVE_CL_BUILD */
710 +   {
711 +       FatalError("ClamAV: cl_build() %s\n", cl_strerror(ret));
712 +   }
713 +
714 +   /* set the limits */
715 +   memset(&clam_limits, 0, sizeof(clam_limits));
716 +   /* maximal number of files in archive */;
717 +   clam_limits.maxfiles = 1000;
718 +   /* maximal archived file size */
719 +   clam_limits.maxfilesize = 10 * 1048576; /* 10 MB */
720 +   /* maximal recursion level */
721 +   clam_limits.maxreclevel = 5;
722 +   /* maximal compression ratio */
723 +   clam_limits.maxratio = 200;
724 +   /* disable memory limit for bzip2 scanner */
725 +   clam_limits.archivememlim = 0;
726 +
727 +   /* register the VirusChecker */
728 +   AddFuncToPreprocList(VirusChecker, PRIORITY_SCANNER, PP_CLAMAV);
729 +}
730 +
731 +
732 +/*
733 + * Function:  ClamAVReloadDB(void)
734 + *
735 + * Purpose:   Determines if the virus definition files need
736 + *            to be reloaded. If so, reload them.
737 + *
738 + * Arguments: pointer to Packet *p, for the time only
739 + *
740 + * Returns:   none, void function
741 + *
742 + */
743 +static void ClamAVReloadDB(Packet *p)
744 +{
745 +   int ret = 0;
746 +   int n = 0;
747 +
748 +   DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"going to Reload ClamAV Database\n"););
749 +   clamcnf.next_reload_time = p->pkth->ts.tv_sec + clamcnf.reloadtime;
750 +
751 +   /* now really check */
752 +   if(cl_statchkdir(&dbstat) == 1)
753 +   {
754 +       /* okay, we are going to reload the db */
755 +#ifdef CLAMAV_HAVE_CL_BUILD
756 +       cl_free(cl_root);
757 +#else
758 +       cl_freetrie(cl_root);
759 +#endif /* CLAMAV_HAVE_CL_BUILD */
760 +       cl_root = NULL;
761 +
762 +       /* open the defs dir */
763 +       ret = cl_loaddbdir(clamcnf.dbdir, &cl_root, &n);
764 +       if(ret != 0)
765 +       {
766 +           FatalError("ClamAV: cl_loaddbdir() %s\n", cl_strerror(ret));
767 +       }
768 +
769 +       /* run buildtrie */
770 +#ifdef CLAMAV_HAVE_CL_BUILD
771 +       if((ret = cl_build(cl_root)))
772 +#else
773 +       if((ret = cl_buildtrie(cl_root)))
774 +#endif /* CLAMAV_HAVE_CL_BUILD */
775 +       {
776 +           FatalError("ClamAV: cl_build() %s\n", cl_strerror(ret));
777 +       }
778 +
779 +       DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"New ClamAV Database loaded\n"););
780 +
781 +       /* re-init the dbstat */
782 +       cl_statfree(&dbstat);
783 +
784 +       memset(&dbstat, 0, sizeof(struct cl_stat));
785 +       cl_statinidir(clamcnf.dbdir, &dbstat);
786 +   }
787 +   else
788 +   {
789 +       DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"Database is up2date\n"););
790 +   }
791 +
792 +   return;
793 +}
794 +
795 +
796 +/*
797 + * Function:  check_for_http(Packet *p, u_int8_t **data, int *dsize)
798 + *
799 + * Purpose:   Determines if the packet is a HTTP response. We are interested
800 + *            in those because we want to cut off the header from the
801 + *            response, so ClamAV has less trouble detecting virusses.
802 + *
803 + *            It doesn't actually modify any buffer, it only returns (through
804 + *            the **data argument) the start of the buffer after the header.
805 + *            *dsize is the size of the buffer without the header.
806 + *
807 + *            The check for HTTP should be very cheap, so we do it on all
808 + *            traffic. Later we can change this if there is a need to.
809 + *
810 + * Arguments: pointer to the packet, pointer to a data pointer, pointer to
811 + *            any int.
812 + *
813 + * Returns:   nothing, void function
814 + */
815 +int
816 +strip_http_headers_p(Packet *p, u_int8_t **data, int *dsize)
817 +{
818 +       u_int8_t *needle = NULL;
819 +       int offset = 0;
820 +       int tmpsize = 0;
821 +       int chunked = 0;
822 +       u_int8_t *newbuf = NULL;
823 +  
824 +       /* set needle to past the HTTP keyword */
825 +       needle = p->data + 5;
826 +
827 +       /* search the http headers for Transfer-Encoding */
828 +       needle = strstr(needle, "Transfer-Encoding: chunked");
829 +
830 +       if(needle != NULL)
831 +       {
832 +              /* chunked encoding used */
833 +              chunked = 1;
834 +       }
835 +       else
836 +       { 
837 +           /* needle is NULL set the pointer back */
838 +           needle = p->data + 5;
839 +       }
840 +       /* search for the end of the http header string */
841 +       needle = strstr(needle, "\r\n\r\n");
842 +       if(needle == NULL)
843 +       {
844 +               return 0;
845 +       }
846 +       /* set newbuf past the http headers */
847 +       newbuf = needle + 4;
848 +       /* size of packet minus headers */ 
849 +       offset = newbuf - p->data;
850 +
851 +       /* point the new buf to the beginning of the payload */
852 +       if(chunked)
853 +       {
854 +         /* chunked encoding used put needle past */
855 +         needle = needle + 4; 
856 +         needle = strstr(needle, "\r\n");
857 +         if(needle == NULL)
858 +         {
859 +               /* chunked but couldn't return after chunksize */
860 +               return 0;
861 +         }
862 +         /* just get us past the first chunksize */
863 +         newbuf = needle + 2;
864 +         
865 +         /* size of packet minus headers and chunksize */
866 +         offset = newbuf - p->data; 
867 +       }
868 +
869 +       DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"p->data %p, needle %p, newbuf %p, offset %d, p->dsize %u\n",
870 +               p->data, needle, newbuf, offset, p->dsize););
871 +
872 +       /* some sanity checks */
873 +       if(offset >= p->dsize)
874 +               return 0;
875 +       if(offset < 0)
876 +               return 0;
877 +
878 +       tmpsize = p->dsize - offset;
879 +
880 +       while(check_4_http_headers(newbuf,tmpsize))
881 +       {
882 +          needle = newbuf + 5;
883 +          /* stripping http headers from buffer */
884 +          needle = strstr(needle, "\r\n\r\n");
885 +          if(needle == NULL)
886 +          {
887 +               return 0;
888 +          }
889 +
890 +          /* point the new buf to the beginning of the payload */
891 +          newbuf = needle + 4;
892 +          offset = newbuf - p->data;
893 +          tmpsize = p->dsize - offset;
894 +       }
895 +
896 +       /* some sanity checks */
897 +       if(offset >= p->dsize)
898 +               return 0;
899 +       if(offset < 0)
900 +               return 0;
901 +
902 +       /* finally pass the new buf ptr and size back to the caller */
903 +       *data = newbuf;
904 +       *dsize = p->dsize - offset;
905 +       return 1;
906 +}
907 +
908 +
909 +int
910 +check_4_http_headers(u_int8_t *buf, int dsize)
911 +{
912 +    if((dsize > 5) &&
913 +        (buf[0] == 'H' || buf[1] == 'T' ||
914 +         buf[2] == 'T' || buf[3] == 'P'))
915 +    {
916 +       /* http headers found */ 
917 +       return 1;
918 +    }
919 +
920 +    //printf("no http headers found\n");
921 +    return 0;
922 +}
923 +
924 +
925 +/*
926 + * Function:  ScanPort(Packet *p)
927 + *
928 + * Purpose:   Determines if a packet needs to be scanned based
929 + *            on the source and destination ports, and for tcp
930 + *            packets also if we want to scan only toclient or
931 + *            toserver packets.
932 + *
933 + * Arguments: pointer to the packet
934 + *
935 + * Returns:   returns 1 if the packet needs to be scanned,
936 + *                    0 otherwise.
937 + */
938 +static INLINE int ScanPort(Packet *p)
939 +{
940 +   /* tcp packet */
941 +   if(p->tcph)
942 +   {
943 +       if(p->packet_flags & PKT_FROM_SERVER && !clamcnf.toserveronly)
944 +       {
945 +           /* server to client packet: check sp */
946 +           if(!(clamcnf.VirusScanPorts[(p->sp/8)] & (1<<(p->sp%8))))
947 +               return 0;
948 +           else
949 +               return 1;
950 +       }
951 +       else if(p->packet_flags & PKT_FROM_CLIENT && !clamcnf.toclientonly)
952 +       {
953 +           /* client to server packet: check dp */
954 +           if(!(clamcnf.VirusScanPorts[(p->dp/8)] & (1<<(p->dp%8))))
955 +               return 0;
956 +           else
957 +               return 1;
958 +       }
959 +       else
960 +       {
961 +           /* if we get here the packet was FROM_SERVER while in toserveronly mode
962 +              or vice-versa. So we don't scan. */
963 +           return 0;
964 +       }
965 +   }
966 +   /* if the packet is not tcp we have no idea about the direction of the
967 +    * packet. So we check both the dp and the sp. */
968 +   else if(!(clamcnf.VirusScanPorts[(p->dp/8)] & (1<<(p->dp%8))) && /* destination ports */
969 +           !(clamcnf.VirusScanPorts[(p->sp/8)] & (1<<(p->sp%8))))   /* source ports */
970 +   {
971 +       return 0;
972 +   }
973 +   else
974 +   {
975 +       return 1;
976 +   }
977 +}
978 +
979 +
980 +static ssize_t writepacket(int fd, const void *pbuffer, size_t psize)
981 +{
982 +   ssize_t pdwritten = 0, pd;
983 +
984 +   do {
985 +       DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"we have written %i of %i bytes in writepacket loop\n",pdwritten,psize););
986 +
987 +       if((pd = write(fd, &((const char *)pbuffer)[pdwritten], psize - pdwritten)) == -1)
988 +       {
989 +           if(errno == EINTR)
990 +           {
991 +               DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"we wrote %i of %i bytes to disk and got a signal interrupt looping\n",pdwritten,psize););
992 +           }
993 +           else
994 +           {
995 +               return -1;
996 +           }
997 +       }
998 +       pdwritten += pd;
999 +
1000 +       DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"we have written %i of %i bytes in writepacket loop\n",pdwritten,psize););
1001 +   } while (pdwritten < psize);
1002 +
1003 +   return pdwritten;
1004 +}
1005 +
1006 +
1007 +/*      returns:
1008 +       1: virus or error, so not scanned
1009 +       0: ok
1010 +*/
1011 +static INLINE int StoreAndScan(Packet *p)
1012 +{
1013 +   int fd = 0;
1014 +   char tempfilename[256]="";
1015 +   size_t size = 0;
1016 +   int ret = 0, retval = 0;
1017 +   Event event;
1018 +   char outstring[255];
1019 +   const char *cl_virusname = NULL;
1020 +   u_int8_t *buf = p->data;
1021 +   int dsize = p->dsize;
1022 +
1023 +   /* create a secure temp file */
1024 +   size = snprintf(tempfilename, sizeof(tempfilename), "%s/snort_inline-clamav-XXXXXX", clamcnf.desctmpdir);
1025 +   DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"tempfilename is %s\n",tempfilename););
1026 +   if(size >= sizeof(tempfilename))
1027 +   {
1028 +       FatalError("buffer too small");
1029 +   }
1030 +   fd = mkstemp(tempfilename);
1031 +   if(fd == -1)
1032 +   {
1033 +       DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"mkstemp error: %s.\n", strerror(errno)););
1034 +       return 1;
1035 +   }
1036 +
1037 +   /* check if this is a HTTP response, if it is, the buffer
1038 +      and buffer size will be adjusted */
1039 +   if(check_4_http_headers(buf,dsize))
1040 +   {
1041 +      if(strip_http_headers_p(p, &buf, &dsize))
1042 +      {
1043 +         //printf("headers stripped\n");
1044 +      }
1045 +   }
1046 +   
1047 +   /* writing the data to the tempfile */
1048 +   ret = writepacket(fd, buf, dsize);
1049 +   if(ret < dsize)
1050 +   {
1051 +       DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"tmpfile too small\n"););
1052 +   }
1053 +
1054 +   /* scan the descriptor */
1055 +   ret = cl_scandesc(fd, &cl_virusname, NULL, cl_root, &clam_limits, CL_SCAN_STDOPT);
1056 +   if(ret == CL_CLEAN)
1057 +   {
1058 +       DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"Packet is clean\n"););
1059 +   }
1060 +   else if(ret == CL_VIRUS)
1061 +   {
1062 +       snprintf(outstring, sizeof(outstring), "%s %s", CLAMAV_VIRUSFOUND_STR, cl_virusname);
1063 +
1064 +       DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"tempfile and or packet is infected: '%s'.\n", outstring););
1065 +
1066 +       /* alert! */
1067 +       SetEvent(&event, GENERATOR_SPP_CLAMAV, CLAMAV_VIRUSFOUND, 1, 0, 0, 0);
1068 +       CallAlertFuncs(p, outstring, NULL, &event);
1069 +       CallLogFuncs(p, outstring, NULL, &event);
1070 +       retval = 1;
1071 +   }
1072 +   else
1073 +   {
1074 +       /* error */
1075 +       if(ret < 0)
1076 +       {
1077 +           /* close the fd */
1078 +           close(fd);
1079 +           /* remove the file */
1080 +           unlink(tempfilename);
1081 +           /* ret < 0 is very serious, bail out */
1082 +           FatalError("ClamAV scan error: %s.\n", cl_strerror(ret));
1083 +       }
1084 +       else
1085 +           /* error, but dont bail out (like we did before), because
1086 +            * we dont want to be DoS-ed because of a clamav bug.
1087 +            */
1088 +           DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"ClamAV scan error: %s.\n", cl_strerror(ret)););
1089 +   }
1090 +
1091 +   /* close the fd */
1092 +   close(fd);
1093 +   /* remove it */
1094 +   unlink(tempfilename);
1095 +
1096 +   return retval;
1097 +}
1098 +
1099 +
1100 +/*
1101 + * Function: VirusInPacket(Packet *)
1102 + *
1103 + * Purpose: Perform Virusscanning on the payload of a packet.
1104 + *
1105 + * Arguments: p => pointer to the current packet data struct
1106 + *
1107 + * Returns: 1: is a virus was found
1108 + *          0: if the packet is clean
1109 + *
1110 + */
1111 +static int VirusInPacket(Packet *p)
1112 +{
1113 +   /* check if we need to reload the virus db */
1114 +   if(p->pkth->ts.tv_sec >= clamcnf.next_reload_time)
1115 +   {
1116 +       ClamAVReloadDB(p);
1117 +   }
1118 +
1119 +
1120 +   /* virus scanning requires data, so check if we have any */
1121 +   if(p->dsize == 0)
1122 +   {
1123 +       return 0;
1124 +   }
1125 +
1126 +
1127 +   /* check the port list to see if we need to scan this port */
1128 +   if(!ScanPort(p))
1129 +   {
1130 +       return 0;
1131 +   }
1132 +
1133 +
1134 +   /* if cl_root is NULL we return */
1135 +   if(!cl_root)
1136 +   {
1137 +       return 0;
1138 +   }
1139 +
1140 +
1141 +   /* do the actual scanning */
1142 +   return(StoreAndScan(p));
1143 +}
1144 +
1145 +
1146 +/*
1147 + * Function: PreprocFunction(Packet *)
1148 + *
1149 + * Purpose: Perform the preprocessor's intended function.  This can be
1150 + *          simple (statistics collection) or complex (IP defragmentation)
1151 + *          as you like.  Try not to destroy the performance of the whole
1152 + *          system by trying to do too much....
1153 + *
1154 + * Arguments: p => pointer to the current packet data struct
1155 + *
1156 + * Returns: void function
1157 + *
1158 + */
1159 +static void VirusChecker(Packet *p, void *context)
1160 +{
1161 +   if(VirusInPacket(p))
1162 +   {
1163 +#ifdef GIDS
1164 +       if(clamcnf.reset)
1165 +       {
1166 +           DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"ClamAV virus found sending Reject\n"););
1167 +           InlineReject(p);
1168 +       }
1169 +#ifdef SNORTINLINE_SF_NET
1170 +       else if(clamcnf.rboth)
1171 +       {
1172 +           DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"ClamAV virus found sending Reject\n"););
1173 +           InlineRejectBoth(p);
1174 +       }
1175 +#endif /* SNORTINLINE_SF_NET */
1176 +       else if(clamcnf.drop)
1177 +       {
1178 +           DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"ClamAV virus found sending Drop\n"););
1179 +           InlineDrop(p);
1180 +       }
1181 +       else
1182 +       {
1183 +            /* we only call AlertFlushStream when we are in alert mode
1184 +              because if the packet stays in our reassembled buffer
1185 +              (in stream4inline mode) we might keep alerting on it. */
1186 +            stream_api->alert_flush_stream(p);
1187 +            /* we also disable detection because if snort alerts it will
1188 +              try to access the reassebled session, which is already gone.
1189 +              This will cause us to SEGV. */
1190 +            DisableDetect(p);
1191 +       }
1192 +#ifdef STICKYDROP
1193 +       if((SppStickydIsRunning()) && sdt.clamav)
1194 +       {
1195 +           DEBUG_WRAP(DebugMessage(DEBUG_CLAMAV,"ClamAV adding ip to StickyDrop treei\n"););
1196 +           AddIpToBlockTree(p, 0, sdt.clamav);
1197 +       }
1198 +#endif /* STICKYDROP */
1199 +#endif /* GIDS */
1200 +   }
1201 +   return;
1202 +}
1203 +
1204 +#endif /* CLAMAV */
1205 diff -uNr snort-2.6.0.2/src/preprocessors/spp_clamav.h snort-2.6.0.2.clam/src/preprocessors/spp_clamav.h
1206 --- snort-2.6.0.2/src/preprocessors/spp_clamav.h        1970-01-01 01:00:00.000000000 +0100
1207 +++ snort-2.6.0.2.clam/src/preprocessors/spp_clamav.h   2006-11-06 08:24:08.000000000 +0100
1208 @@ -0,0 +1,19 @@
1209 +/* $Id$ */  
1210 +/* Snort Preprocessor Plugin Header File Template */
1211 +
1212 +/* This file gets included in plugbase.h when it is integrated into the rest
1213 + * of the program.
1214 + */
1215 +#ifndef __SPP_CLAMAV_H__
1216 +#define __SPP_CLAMAV_H__
1217 +
1218 +#ifdef CLAMAV
1219 +/*
1220 + * list of function prototypes to export for this preprocessor
1221 + */
1222 +void SetupClamAV();
1223 +
1224 +#endif /* CLAMAV */
1225 +
1226 +#endif  /* __SPP_CLAMAV_H__ */
1227 +
1228 diff -uNr snort-2.6.0.2/src/preprocids.h snort-2.6.0.2.clam/src/preprocids.h
1229 --- snort-2.6.0.2/src/preprocids.h      2006-02-17 22:39:35.000000000 +0100
1230 +++ snort-2.6.0.2.clam/src/preprocids.h 2006-11-06 08:24:08.000000000 +0100
1231 @@ -41,6 +41,7 @@
1232  #define PP_SSH                    24
1233  #define PP_DNS                    25
1234  #define PP_STREAM5                26
1235 +#define PP_CLAMAV                 28
1236  
1237  #define PRIORITY_FIRST 0x0
1238  #define PRIORITY_NETWORK 0x10
This page took 0.148836 seconds and 3 git commands to generate.