View | Details | Raw Unified | Return to bug 201306 | Differences between
and this patch

Collapse All | Expand All

(-)Makefile (-5 / +10 lines)
Lines 1-9 Link Here
1
# Created by: Xavier Beaudouin <kiwi@oav.net>
1
# Created by: Xavier Beaudouin <kiwi@oav.net>
2
# $FreeBSD: head/www/mod_evasive/Makefile 336589 2013-12-15 22:11:20Z ohauer $
2
# $FreeBSD: head/www/mod_evasive/Makefile 336589 2013-12-15 22:11:20Z ohauer $
3
# (Changed by: Walter Schwarzenfeld <w.schwarzenfeld@untanet.at>)
4
# (Date 2015-07-05)
3
5
4
PORTNAME=	mod_evasive
6
PORTNAME=	mod_evasive
5
PORTVERSION=	1.10.1
7
PORTVERSION=	1.10.1
6
PORTREVISION=	1
8
PORTREVISION=	2
7
CATEGORIES=	www security
9
CATEGORIES=	www security
8
MASTER_SITES=	http://www.zdziarski.com/blog/wp-content/uploads/2010/02/
10
MASTER_SITES=	http://www.zdziarski.com/blog/wp-content/uploads/2010/02/
9
DISTNAME=	mod_evasive_${PORTVERSION}
11
DISTNAME=	mod_evasive_${PORTVERSION}
Lines 16-22 Link Here
16
18
17
WRKSRC=		${WRKDIR}/${PORTNAME}
19
WRKSRC=		${WRKDIR}/${PORTNAME}
18
20
19
USE_APACHE=	22
21
USE_APACHE=	22+
20
AP_FAST_BUILD=	yes
22
AP_FAST_BUILD=	yes
21
AP_GENPLIST=	yes
23
AP_GENPLIST=	yes
22
MODULENAME=	${PORTNAME}20
24
MODULENAME=	${PORTNAME}20
Lines 24-35 Link Here
24
PORTDOCS=	README test.pl
26
PORTDOCS=	README test.pl
25
27
26
post-patch:
28
post-patch:
29
	#cp l${WRKSRC}/mod_evasive.c ${WRKSRC}/mod_evasive20.c
27
	@${REINPLACE_CMD} -e "s|/bin/mail|/usr/bin/mail|g" \
30
	@${REINPLACE_CMD} -e "s|/bin/mail|/usr/bin/mail|g" \
28
		${WRKSRC}/mod_evasive.c ${WRKSRC}/mod_evasive20.c \
31
		${WRKSRC}/mod_evasive.c ${WRKSRC}/mod_evasive20.c \
29
		${WRKSRC}/mod_evasiveNSAPI.c
32
		${WRKSRC}/mod_evasiveNSAPI.c
30
33
	
31
post-install:
34
post-install:
35
	@${REINPLACE_CMD} -e "s|evasive.c|evasive20.c|g" \
36
		${WORKDIR}work/.PLIST.mktmp
32
	@${MKDIR} ${STAGEDIR}${DOCSDIR}
37
	@${MKDIR} ${STAGEDIR}${DOCSDIR}
33
	@${INSTALL_DATA} ${PORTDOCS:S|^|${WRKSRC}/|} ${STAGEDIR}${DOCSDIR}
38
		@${INSTALL_DATA} ${PORTDOCS:S|^|${WRKSRC}/|} ${STAGEDIR}${DOCSDIR}
34
39
	
35
.include <bsd.port.mk>
40
.include <bsd.port.mk>
(-)files/_patch-mod_evasive20.c (+83 lines)
Line 0 Link Here
1
--- mod_evasive20.c.orig	2015-07-03 17:42:29 UTC
2
+++ mod_evasive20.c
3
@@ -139,11 +139,11 @@ static int access_checker(request_rec *r
4
       time_t t = time(NULL);
5
 
6
       /* Check whitelist */
7
-      if (is_whitelisted(r->connection->remote_ip)) 
8
+      if (is_whitelisted(r->connection->client_ip)) 
9
         return OK;
10
 
11
       /* First see if the IP itself is on "hold" */
12
-      n = ntt_find(hit_list, r->connection->remote_ip);
13
+      n = ntt_find(hit_list, r->connection->client_ip);
14
 
15
       if (n != NULL && t-n->timestamp<blocking_period) {
16
  
17
@@ -155,14 +155,14 @@ static int access_checker(request_rec *r
18
       } else {
19
 
20
         /* Has URI been hit too much? */
21
-        snprintf(hash_key, 2048, "%s_%s", r->connection->remote_ip, r->uri);
22
+        snprintf(hash_key, 2048, "%s_%s", r->connection->client_ip, r->uri);
23
         n = ntt_find(hit_list, hash_key);
24
         if (n != NULL) {
25
 
26
           /* If URI is being hit too much, add to "hold" list and 403 */
27
           if (t-n->timestamp<page_interval && n->count>=page_count) {
28
             ret = HTTP_FORBIDDEN;
29
-            ntt_insert(hit_list, r->connection->remote_ip, time(NULL));
30
+            ntt_insert(hit_list, r->connection->client_ip, time(NULL));
31
           } else {
32
 
33
             /* Reset our hit count list as necessary */
34
@@ -177,14 +177,14 @@ static int access_checker(request_rec *r
35
         }
36
 
37
         /* Has site been hit too much? */
38
-        snprintf(hash_key, 2048, "%s_SITE", r->connection->remote_ip);
39
+        snprintf(hash_key, 2048, "%s_SITE", r->connection->client_ip);
40
         n = ntt_find(hit_list, hash_key);
41
         if (n != NULL) {
42
 
43
           /* If site is being hit too much, add to "hold" list and 403 */
44
           if (t-n->timestamp<site_interval && n->count>=site_count) {
45
             ret = HTTP_FORBIDDEN;
46
-            ntt_insert(hit_list, r->connection->remote_ip, time(NULL));
47
+            ntt_insert(hit_list, r->connection->client_ip, time(NULL));
48
           } else {
49
 
50
             /* Reset our hit count list as necessary */
51
@@ -205,27 +205,27 @@ static int access_checker(request_rec *r
52
         struct stat s;
53
         FILE *file;
54
 
55
-        snprintf(filename, sizeof(filename), "%s/dos-%s", log_dir != NULL ? log_dir : DEFAULT_LOG_DIR, r->connection->remote_ip);
56
+        snprintf(filename, sizeof(filename), "%s/dos-%s", log_dir != NULL ? log_dir : DEFAULT_LOG_DIR, r->connection->client_ip);
57
         if (stat(filename, &s)) {
58
           file = fopen(filename, "w");
59
           if (file != NULL) {
60
             fprintf(file, "%ld\n", getpid());
61
             fclose(file);
62
 
63
-            LOG(LOG_ALERT, "Blacklisting address %s: possible DoS attack.", r->connection->remote_ip);
64
+            LOG(LOG_ALERT, "Blacklisting address %s: possible DoS attack.", r->connection->client_ip);
65
             if (email_notify != NULL) {
66
               snprintf(filename, sizeof(filename), MAILER, email_notify);
67
               file = popen(filename, "w");
68
               if (file != NULL) {
69
                 fprintf(file, "To: %s\n", email_notify);
70
-                fprintf(file, "Subject: HTTP BLACKLIST %s\n\n", r->connection->remote_ip);
71
-                fprintf(file, "mod_evasive HTTP Blacklisted %s\n", r->connection->remote_ip);
72
+                fprintf(file, "Subject: HTTP BLACKLIST %s\n\n", r->connection->client_ip);
73
+                fprintf(file, "mod_evasive HTTP Blacklisted %s\n", r->connection->client_ip);
74
                 pclose(file);
75
               }
76
             }
77
 
78
             if (system_command != NULL) {
79
-              snprintf(filename, sizeof(filename), system_command, r->connection->remote_ip);
80
+              snprintf(filename, sizeof(filename), system_command, r->connection->client_ip);
81
               system(filename);
82
             }
83
  
(-)files/patch-mod_evasive20.c (+102 lines)
Line 0 Link Here
1
--- mod_evasive20.c.orig	2015-07-05 17:29:09 UTC
2
+++ mod_evasive20.c
3
@@ -115,6 +115,7 @@ static void * create_hit_list(apr_pool_t
4
     /* Create a new hit list for this listener */
5
 
6
     hit_list = ntt_create(hash_table_size);
7
+    return 0;	
8
 }
9
 
10
 static const char *whitelist(cmd_parms *cmd, void *dconfig, const char *ip)
11
@@ -139,11 +140,11 @@ static int access_checker(request_rec *r
12
       time_t t = time(NULL);
13
 
14
       /* Check whitelist */
15
-      if (is_whitelisted(r->connection->remote_ip)) 
16
+      if (is_whitelisted(r->connection->client_ip)) 
17
         return OK;
18
 
19
       /* First see if the IP itself is on "hold" */
20
-      n = ntt_find(hit_list, r->connection->remote_ip);
21
+      n = ntt_find(hit_list, r->connection->client_ip);
22
 
23
       if (n != NULL && t-n->timestamp<blocking_period) {
24
  
25
@@ -155,14 +156,14 @@ static int access_checker(request_rec *r
26
       } else {
27
 
28
         /* Has URI been hit too much? */
29
-        snprintf(hash_key, 2048, "%s_%s", r->connection->remote_ip, r->uri);
30
+        snprintf(hash_key, 2048, "%s_%s", r->connection->client_ip, r->uri);
31
         n = ntt_find(hit_list, hash_key);
32
         if (n != NULL) {
33
 
34
           /* If URI is being hit too much, add to "hold" list and 403 */
35
           if (t-n->timestamp<page_interval && n->count>=page_count) {
36
             ret = HTTP_FORBIDDEN;
37
-            ntt_insert(hit_list, r->connection->remote_ip, time(NULL));
38
+            ntt_insert(hit_list, r->connection->client_ip, time(NULL));
39
           } else {
40
 
41
             /* Reset our hit count list as necessary */
42
@@ -177,14 +178,14 @@ static int access_checker(request_rec *r
43
         }
44
 
45
         /* Has site been hit too much? */
46
-        snprintf(hash_key, 2048, "%s_SITE", r->connection->remote_ip);
47
+        snprintf(hash_key, 2048, "%s_SITE", r->connection->client_ip);
48
         n = ntt_find(hit_list, hash_key);
49
         if (n != NULL) {
50
 
51
           /* If site is being hit too much, add to "hold" list and 403 */
52
           if (t-n->timestamp<site_interval && n->count>=site_count) {
53
             ret = HTTP_FORBIDDEN;
54
-            ntt_insert(hit_list, r->connection->remote_ip, time(NULL));
55
+            ntt_insert(hit_list, r->connection->client_ip, time(NULL));
56
           } else {
57
 
58
             /* Reset our hit count list as necessary */
59
@@ -204,28 +205,29 @@ static int access_checker(request_rec *r
60
         char filename[1024];
61
         struct stat s;
62
         FILE *file;
63
+	int getpid();
64
 
65
-        snprintf(filename, sizeof(filename), "%s/dos-%s", log_dir != NULL ? log_dir : DEFAULT_LOG_DIR, r->connection->remote_ip);
66
+        snprintf(filename, sizeof(filename), "%s/dos-%s", log_dir != NULL ? log_dir : DEFAULT_LOG_DIR, r->connection->client_ip);
67
         if (stat(filename, &s)) {
68
           file = fopen(filename, "w");
69
           if (file != NULL) {
70
-            fprintf(file, "%ld\n", getpid());
71
+            fprintf(file, "%d\n", getpid());
72
             fclose(file);
73
 
74
-            LOG(LOG_ALERT, "Blacklisting address %s: possible DoS attack.", r->connection->remote_ip);
75
+            LOG(LOG_ALERT, "Blacklisting address %s: possible DoS attack.", r->connection->client_ip);
76
             if (email_notify != NULL) {
77
               snprintf(filename, sizeof(filename), MAILER, email_notify);
78
               file = popen(filename, "w");
79
               if (file != NULL) {
80
                 fprintf(file, "To: %s\n", email_notify);
81
-                fprintf(file, "Subject: HTTP BLACKLIST %s\n\n", r->connection->remote_ip);
82
-                fprintf(file, "mod_evasive HTTP Blacklisted %s\n", r->connection->remote_ip);
83
+                fprintf(file, "Subject: HTTP BLACKLIST %s\n\n", r->connection->client_ip);
84
+                fprintf(file, "mod_evasive HTTP Blacklisted %s\n", r->connection->client_ip);
85
                 pclose(file);
86
               }
87
             }
88
 
89
             if (system_command != NULL) {
90
-              snprintf(filename, sizeof(filename), system_command, r->connection->remote_ip);
91
+              snprintf(filename, sizeof(filename), system_command, r->connection->client_ip);
92
               system(filename);
93
             }
94
  
95
@@ -298,6 +300,7 @@ static apr_status_t destroy_hit_list(voi
96
   ntt_destroy(hit_list);
97
   free(email_notify);
98
   free(system_command);
99
+  return 0;
100
 }
101
 
102
 
(-)work/mod_evasive/.cvsignore (-5 / +91 lines)
Line 0 Link Here
1
Index: Makefile
2
===================================================================
3
--- Makefile	(.../mod_evasive)	(working copy)
4
+++ Makefile	(working copy)
5
@@ -1,9 +1,11 @@
6
 # Created by: Xavier Beaudouin <kiwi@oav.net>
7
 # $FreeBSD: head/www/mod_evasive/Makefile 336589 2013-12-15 22:11:20Z ohauer $
8
+# (Changed by: Walter Schwarzenfeld <w.schwarzenfeld@untanet.at>)
9
+# (Date 2015-07-05)
10
 
11
 PORTNAME=	mod_evasive
12
 PORTVERSION=	1.10.1
13
-PORTREVISION=	1
14
+PORTREVISION=	2
15
 CATEGORIES=	www security
16
 MASTER_SITES=	http://www.zdziarski.com/blog/wp-content/uploads/2010/02/
17
 DISTNAME=	mod_evasive_${PORTVERSION}
18
@@ -16,7 +18,7 @@
19
 
20
 WRKSRC=		${WRKDIR}/${PORTNAME}
21
 
22
-USE_APACHE=	22
23
+USE_APACHE=	22+
24
 AP_FAST_BUILD=	yes
25
 AP_GENPLIST=	yes
26
 MODULENAME=	${PORTNAME}20
27
@@ -24,12 +26,15 @@
28
 PORTDOCS=	README test.pl
29
 
30
 post-patch:
31
+	#cp l${WRKSRC}/mod_evasive.c ${WRKSRC}/mod_evasive20.c
32
 	@${REINPLACE_CMD} -e "s|/bin/mail|/usr/bin/mail|g" \
33
 		${WRKSRC}/mod_evasive.c ${WRKSRC}/mod_evasive20.c \
34
 		${WRKSRC}/mod_evasiveNSAPI.c
35
-
36
+	
37
 post-install:
38
+	@${REINPLACE_CMD} -e "s|evasive.c|evasive20.c|g" \
39
+		${WORKDIR}work/.PLIST.mktmp
40
 	@${MKDIR} ${STAGEDIR}${DOCSDIR}
41
-	@${INSTALL_DATA} ${PORTDOCS:S|^|${WRKSRC}/|} ${STAGEDIR}${DOCSDIR}
42
-
43
+		@${INSTALL_DATA} ${PORTDOCS:S|^|${WRKSRC}/|} ${STAGEDIR}${DOCSDIR}
44
+	
45
 .include <bsd.port.mk>
46
47
Property changes on: Makefile
48
___________________________________________________________________
49
Deleted: svn:eol-style
50
## -1 +0,0 ##
51
-native
52
\ No newline at end of property
53
Deleted: svn:keywords
54
## -1 +0,0 ##
55
-FreeBSD=%H
56
\ No newline at end of property
57
Deleted: svn:mime-type
58
## -1 +0,0 ##
59
-text/plain
60
\ No newline at end of property
61
Index: distinfo
62
===================================================================
63
--- distinfo	(.../mod_evasive)	(working copy)
64
+++ distinfo	(working copy)
65
66
Property changes on: distinfo
67
___________________________________________________________________
68
Deleted: fbsd:nokeywords
69
## -1 +0,0 ##
70
-yes
71
\ No newline at end of property
72
Deleted: svn:eol-style
73
## -1 +0,0 ##
74
-native
75
\ No newline at end of property
76
Deleted: svn:mime-type
77
## -1 +0,0 ##
78
-text/plain
79
\ No newline at end of property
80
Index: files/_patch-mod_evasive20.c
81
===================================================================
82
--- files/_patch-mod_evasive20.c	(.../mod_evasive)	(working copy)
83
+++ files/_patch-mod_evasive20.c	(working copy)
84
@@ -0,0 +1,83 @@
85
+--- mod_evasive20.c.orig	2015-07-03 17:42:29 UTC
86
++++ mod_evasive20.c
87
+@@ -139,11 +139,11 @@ static int access_checker(request_rec *r
88
+       time_t t = time(NULL);
89
+ 
90
+       /* Check whitelist */
91
+-      if (is_whitelisted(r->connection->remote_ip)) 
Lines 1-5 Link Here
1
.libs
2
*.la
3
*.slo
4
*.o
5
*.so
(-)work/mod_evasive/CHANGELOG (-72 lines)
Lines 1-72 Link Here
1
2
Version 1.10.1
3
--------------
4
5
[20051008] jonz: Fixed IP Whitelisting in Apache 1.3 Version
6
7
[20051008] jonz: Corrected initialization to prevent dumping IP database
8
9
[20051008] jonz: Documentation and code cleaned up, renamed mod_evasive
10
11
Version 1.10.0
12
--------------
13
14
[20050117] jonz: Security fix: Tempdir configuration directive (race condition)
15
16
Version 1.9.0
17
-------------
18
19
[20031030] jonz: Added NSAPI/SunONE Support
20
21
[20031030] jonz: Added TEMP_HOME definition to change temporary file locations
22
23
Version 1.8.0
24
-------------
25
26
[20030901] jonz: Added support for IP Whitelisting
27
28
Version 1.7.1
29
-------------
30
31
[20030826] jonz: Minor bugfixes to Apache 2.0 module
32
33
[20030826] jonz: Corrections to object creation and cleanup
34
35
Version 1.7.0
36
-------------
37
38
[20030822] jonz: Support for Apache 2.0
39
40
Version 1.6.1
41
-------------
42
43
[20030811] jonz: Bugfix: free() of another static variable
44
 
45
Version 1.6.0
46
-------------
47
48
[20030806] jonz: Added syslog support
49
50
Version 1.5.1
51
-------------
52
53
[20030516] jonz: Bugfix: free() of a static variable
54
55
Version 1.5.0
56
-------------
57
58
[20030425] jonz: Added automated email notification 
59
60
[20030425] jonz: Added configurable system command
61
62
Version 1.4.0
63
-------------
64
65
[20021031] jonz: Added support fror httpd.conf directives
66
67
[20021031] jonz: Added --add-module support
68
69
Version 1.0.0
70
-------------
71
72
[20021015] jonz: Initial Release
(-)work/mod_evasive/LICENSE (-146 lines)
Lines 1-146 Link Here
1
GNU GENERAL PUBLIC LICENSE
2
Version 2, June 1991 
3
4
Copyright (C) 1989, 1991 Free Software Foundation, Inc.  
5
59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
6
7
Everyone is permitted to copy and distribute verbatim copies
8
of this license document, but changing it is not allowed.
9
10
Preamble
11
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. 
12
13
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. 
14
15
To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. 
16
17
For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. 
18
19
We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. 
20
21
Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. 
22
23
Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. 
24
25
The precise terms and conditions for copying, distribution and modification follow. 
26
27
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
28
0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". 
29
30
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 
31
32
1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. 
33
34
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 
35
36
2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: 
37
38
39
a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. 
40
41
b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. 
42
43
c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) 
44
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. 
45
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. 
46
47
In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 
48
49
3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: 
50
51
a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, 
52
53
b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, 
54
55
c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) 
56
The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. 
57
If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 
58
59
4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 
60
61
5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 
62
63
6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 
64
65
7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. 
66
67
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. 
68
69
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. 
70
71
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 
72
73
8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 
74
75
9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. 
76
77
Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 
78
79
10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. 
80
81
NO WARRANTY
82
83
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 
84
85
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 
86
87
88
END OF TERMS AND CONDITIONS
89
How to Apply These Terms to Your New Programs
90
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. 
91
92
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. 
93
94
one line to give the program's name and an idea of what it does.
95
Copyright (C) yyyy  name of author
96
97
This program is free software; you can redistribute it and/or
98
modify it under the terms of the GNU General Public License
99
as published by the Free Software Foundation; either version 2
100
of the License, or (at your option) any later version.
101
102
This program is distributed in the hope that it will be useful,
103
but WITHOUT ANY WARRANTY; without even the implied warranty of
104
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
105
GNU General Public License for more details.
106
107
You should have received a copy of the GNU General Public License
108
along with this program; if not, write to the Free Software
109
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
110
111
Also add information on how to contact you by electronic and paper mail. 
112
113
If the program is interactive, make it output a short notice like this when it starts in an interactive mode: 
114
115
Gnomovision version 69, Copyright (C) year name of author
116
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
117
type `show w'.  This is free software, and you are welcome
118
to redistribute it under certain conditions; type `show c' 
119
for details.
120
121
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. 
122
123
You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: 
124
125
Yoyodyne, Inc., hereby disclaims all copyright
126
interest in the program `Gnomovision'
127
(which makes passes at compilers) written 
128
by James Hacker.
129
130
signature of Ty Coon, 1 April 1989
131
Ty Coon, President of Vice
132
133
This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. 
134
135
136
--------------------------------------------------------------------------------
137
Return to GNU's home page. 
138
FSF & GNU inquiries & questions to gnu@gnu.org. Other ways to contact the FSF. 
139
140
Comments on these web pages to webmasters@www.gnu.org, send other questions to gnu@gnu.org. 
141
142
Copyright notice above.
143
Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA 
144
145
Updated: Last modified: Sun Jul 15 13:13:30 CEST 2001 
146
(-)work/mod_evasive/Makefile.tmpl (-15 lines)
Lines 1-15 Link Here
1
2
#Dependencies
3
4
$(OBJS) $(OBJS_PIC): Makefile
5
6
# DO NOT REMOVE
7
mod_evasive.o: mod_evasive.c $(INCDIR)/httpd.h \
8
 $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
9
 $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
10
 $(INCDIR)/ap_ctype.h $(INCDIR)/hsregex.h \
11
 $(INCDIR)/ap_alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \
12
 $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \
13
 $(INCDIR)/http_core.h $(INCDIR)/http_log.h \
14
 $(INCDIR)/http_main.h $(INCDIR)/http_protocol.h \
15
 $(INCDIR)/util_script.h
(-)work/mod_evasive/README (-378 lines)
Lines 1-378 Link Here
1
Apache Evasive Maneuvers Module
2
For Apache 1.3 and 2.0
3
Copyright (c) Deep Logic, Inc.
4
Version 1.10 [2005.0117]
5
6
LICENSE
7
8
This program is free software; you can redistribute it and/or
9
modify it under the terms of the GNU General Public License
10
as published by the Free Software Foundation; version 2
11
of the License.
12
13
This program is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
GNU General Public License for more details.
17
18
You should have received a copy of the GNU General Public License
19
along with this program; if not, write to the Free Software
20
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21
22
WHAT IS MOD_EVASIVE ?
23
24
mod_evasive is an evasive maneuvers module for Apache to provide evasive
25
action in the event of an HTTP DoS or DDoS attack or brute force attack.  It 
26
is also designed to be a detection tool, and can be easily configured to talk 
27
to ipchains, firewalls, routers, and etcetera.  
28
29
Detection is performed by creating an internal dynamic hash table of IP 
30
Addresses and URIs, and denying any single IP address from any of the following:
31
32
- Requesting the same page more than a few times per second
33
- Making more than 50 concurrent requests on the same child per second
34
- Making any requests while temporarily blacklisted (on a blocking list)
35
36
This method has worked well in both single-server script attacks as well 
37
as distributed attacks, but just like other evasive tools, is only as 
38
useful to the point of bandwidth and processor consumption (e.g. the
39
amount of bandwidth and processor required to receive/process/respond
40
to invalid requests), which is why it's a good idea to integrate this
41
with your firewalls and routers.
42
43
This module instantiates for each listener individually, and therefore has
44
a built-in cleanup mechanism and scaling capabilities.  Because of this,
45
legitimate requests are rarely ever compromised, only legitimate attacks.  Even
46
a user repeatedly clicking on 'reload' should not be affected unless they do
47
it maliciously.
48
49
Three different module sources have been provided:
50
51
Apache v1.3 API:	mod_evasive.c
52
Apache v2.0 API:	mod_evasive20.c
53
NSAPI (iPlanet):	mod_evasiveNSAPI.c *
54
55
NOTE: mod_evasiveNSAPI is a port submitted by Reine Persson <reiper@rsv.se>
56
      and is not officially supported as part of the mod_evasive project.
57
58
HOW IT WORKS
59
60
A web hit request comes in. The following steps take place:
61
62
- The IP address of the requestor is looked up on the temporary blacklist
63
- The IP address of the requestor and the URI are both hashed into a "key".  
64
  A lookup is performed in the listener's internal hash table to determine 
65
  if the same host has requested this page more than once within the past 
66
  1 second.  
67
- The IP address of the requestor is hashed into a "key".
68
  A lookup is performed in the listerner's internal hash table to determine
69
  if the same host has requested more than 50 objects within the past
70
  second (from the same child).
71
72
If any of the above are true, a 403 response is sent.  This conserves
73
bandwidth and system resources in the event of a DoS attack.  Additionally,
74
a system command and/or an email notification can also be triggered to block
75
all the originating addresses of a DDoS attack. 
76
77
Once a single 403 incident occurs, mod_evasive now blocks the entire IP 
78
address for a period of 10 seconds (configurable).  If the host requests a 
79
page within this period, it is forced to wait even longer.  Since this is 
80
triggered from requesting the same URL multiple times per second, this 
81
again does not affect legitimate users.
82
83
The blacklist can/should be configured to talk to your network's firewalls 
84
and/or routers to push the attack out to the front lines, but this is not 
85
required.
86
87
mod_evasive also performs syslog reporting using daemon.alert.  Messages
88
will look like this:
89
90
Aug  6 17:41:49 elijah mod_evasive[23184]: [ID 801097 daemon.alert] Blacklisting address x.x.x.x: possible attack.
91
92
WHAT IS THIS TOOL USEFUL FOR?
93
94
This tool is *excellent* at fending off request-based DoS attacks or scripted
95
attacks, and brute force attacks. When integrated with firewalls or IP filters,
96
mod_evasive can stand up to even large attacks. Its features will prevent you 
97
from wasting bandwidth or having a few thousand CGI scripts running as a 
98
result of an attack.  
99
100
If you do not have an infrastructure capable of fending off any other types
101
of DoS attacks, chances are this tool will only help you to the point of
102
your total bandwidth or server capacity for sending 403's.  Without a solid
103
infrastructure and address filtering tool in place, a heavy distributed DoS 
104
will most likely still take you offline.  
105
106
HOW TO INSTALL
107
108
APACHE v1.3
109
-----------
110
111
Without DSO Support:
112
113
1. Extract this archive into src/modules in the Apache source tree
114
115
2. Run ./configure --add-module=src/modules/evasive/mod_evasive.c
116
117
3. make, install
118
119
4. Restart Apache 
120
121
With DSO Support, Ensim, or CPanel:
122
123
1. $APACHE_ROOT/bin/apxs -iac mod_evasive.c
124
125
2. Restart Apache
126
127
APACHE v2.0
128
-----------
129
130
1. Extract this archive
131
132
2. Run $APACHE_ROOT/bin/apxs -i -a -c mod_evasive20.c
133
134
3. The module will be built and installed into $APACHE_ROOT/modules, and loaded into your httpd.conf
135
136
4. Restart Apache
137
138
NSAPI
139
SunONE (iPlanet,netscape) Installation
140
--------------------------------------
141
142
Tested on:
143
iPlanet 4.1sp12
144
iPlanet 6.0sp5
145
146
Edit compile script for your environment and compile mod_evasiveNSAPI.c
147
to a shared library.
148
149
CONFIGURATION
150
151
mod_evasive has default options configured, but you may also add the
152
following block to your httpd.conf:
153
154
APACHE v1.3
155
-----------
156
157
<IfModule mod_evasive.c>
158
    DOSHashTableSize    3097
159
    DOSPageCount        2
160
    DOSSiteCount        50
161
    DOSPageInterval     1
162
    DOSSiteInterval     1
163
    DOSBlockingPeriod   10
164
</IfModule>
165
166
APACHE v2.0
167
-----------
168
<IfModule mod_evasive20.c>
169
    DOSHashTableSize    3097
170
    DOSPageCount        2
171
    DOSSiteCount        50
172
    DOSPageInterval     1
173
    DOSSiteInterval     1
174
    DOSBlockingPeriod   10
175
</IfModule>
176
177
Optionally you can also add the following directives:
178
179
    DOSEmailNotify	you@yourdomain.com
180
    DOSSystemCommand	"su - someuser -c '/sbin/... %s ...'"
181
    DOSLogDir		"/var/lock/mod_evasive"
182
183
You will also need to add this line if you are building with dynamic support:
184
185
APACHE v1.3
186
-----------
187
188
AddModule	mod_evasive.c
189
190
APACHE v2.0
191
-----------
192
193
LoadModule evasive20_module modules/mod_evasive20.so
194
195
(This line is already added to your configuration by apxs)
196
197
NSAPI
198
SunONE (iPlanet,Netscape) Configuration
199
--------------------------------------
200
                                                                                
201
Configure iPlanet 4.1
202
---------------------
203
204
Edit obj.conf:
205
                                                                                
206
Init fn="load-modules" funcs="mod_evasive_init,mod_evasive_check" shlib="/opt/ns-4.1/plugins/lib/mod_evasive.sl"
207
                                                                                
208
Init fn="mod_evasive_init" DOSPageCount=2 DOSSiteCount=50 DOSPageInterval=1 DOSSiteInterval=1 DOSBlockingPeriod=10 DOSWhitelist="10.60.0.7,10.65.0.10"
209
                                                                                
210
In the default object:
211
PathCheck fn=mod_evasive_check
212
                                                                                
213
Or an own object
214
<Object name="evasive" ppath="/DoSProtectedArea*">
215
NameTrans fn=mod_evasive_check
216
</Object>
217
                                                                                
218
                                                                                
219
Configure iPlanet 6.0
220
---------------------
221
                                                                                
222
Edit magnus.conf:
223
                                                                                
224
Init fn="load-modules" funcs="mod_evasive_init,mod_evasive_check" shlib="/opt/iplanet-6.0/plugins/lib/mod_evasive.sl"
225
                                                                                
226
Init fn="mod_evasive_init" DOSWhitelist="10.60.0.7,10.65.0.10"
227
                                                                                
228
Edit obj.conf:
229
In the default object:
230
PathCheck fn=mod_evasive_check
231
                                                                                
232
Or an own object
233
<Object name="evasive" ppath="/DoSProtectedArea*">
234
NameTrans fn=mod_evasive_check
235
</Object>
236
237
DOSHashTableSize
238
----------------
239
240
The hash table size defines the number of top-level nodes for each child's 
241
hash table.  Increasing this number will provide faster performance by 
242
decreasing the number of iterations required to get to the record, but 
243
consume more memory for table space.  You should increase this if you have
244
a busy web server.  The value you specify will automatically be tiered up to 
245
the next prime number in the primes list (see mod_evasive.c for a list 
246
of primes used).
247
248
DOSPageCount
249
------------
250
251
This is the threshhold for the number of requests for the same page (or URI)
252
per page interval.  Once the threshhold for that interval has been exceeded,
253
the IP address of the client will be added to the blocking list.
254
 
255
DOSSiteCount
256
------------
257
258
This is the threshhold for the total number of requests for any object by
259
the same client on the same listener per site interval.  Once the threshhold 
260
for that interval has been exceeded, the IP address of the client will be added
261
to the blocking list.
262
263
DOSPageInterval
264
---------------
265
266
The interval for the page count threshhold; defaults to 1 second intervals.
267
268
DOSSiteInterval
269
---------------
270
271
The interval for the site count threshhold; defaults to 1 second intervals.
272
273
DOSBlockingPeriod
274
-----------------
275
276
The blocking period is the amount of time (in seconds) that a client will be
277
blocked for if they are added to the blocking list.  During this time, all
278
subsequent requests from the client will result in a 403 (Forbidden) and
279
the timer being reset (e.g. another 10 seconds).  Since the timer is reset
280
for every subsequent request, it is not necessary to have a long blocking
281
period; in the event of a DoS attack, this timer will keep getting reset. 
282
283
DOSEmailNotify
284
--------------
285
286
If this value is set, an email will be sent to the address specified
287
whenever an IP address becomes blacklisted.  A locking mechanism using /tmp
288
prevents continuous emails from being sent.
289
290
NOTE: Be sure MAILER is set correctly in mod_evasive.c 
291
      (or mod_evasive20.c).  The default is "/bin/mail -t %s" where %s is 
292
      used to denote the destination email address set in the configuration.  
293
      If you are running on linux or some other operating system with a 
294
      different type of mailer, you'll need to change this.
295
296
DOSSystemCommand
297
----------------
298
299
If this value is set, the system command specified will be executed
300
whenever an IP address becomes blacklisted.  This is designed to enable
301
system calls to ip filter or other tools.  A locking mechanism using /tmp
302
prevents continuous system calls.  Use %s to denote the IP address of the
303
blacklisted IP.
304
305
DOSLogDir
306
---------
307
308
Choose an alternative temp directory
309
310
By default "/tmp" will be used for locking mechanism, which opens some 
311
security issues if your system is open to shell users.
312
313
  	http://security.lss.hr/index.php?page=details&ID=LSS-2005-01-01
314
315
In the event you have nonprivileged shell users, you'll want to create a
316
directory writable only to the user Apache is running as (usually root),
317
then set this in your httpd.conf.
318
319
WHITELISTING IP ADDRESSES
320
321
IP addresses of trusted clients can be whitelisted to insure they are never 
322
denied.  The purpose of whitelisting is to protect software, scripts, local 
323
searchbots, or other automated tools from being denied for requesting large 
324
amounts of data from the server.  Whitelisting should *not* be used to add 
325
customer lists or anything of the sort, as this will open the server to abuse.
326
This module is very difficult to trigger without performing some type of 
327
malicious attack, and for that reason it is more appropriate to allow the 
328
module to decide on its own whether or not an individual customer should be 
329
blocked.
330
331
To whitelist an address (or range) add an entry to the Apache configuration 
332
in the following fashion:
333
334
DOSWhitelist	127.0.0.1
335
DOSWhitelist	127.0.0.*
336
337
Wildcards can be used on up to the last 3 octets if necessary.  Multiple
338
DOSWhitelist commands may be used in the configuration.
339
340
TWEAKING APACHE
341
342
The keep-alive settings for your children should be reasonable enough to 
343
keep each child up long enough to resist a DOS attack (or at least part of 
344
one).  Remember, it is the child processes that maintain their own internal
345
IP address tables, and so when one exits, so does all of the IP information it
346
had. For every child that exits, another 5-10 copies of the page may get 
347
through before putting the attacker back into '403 Land'.  With this said, 
348
you should have a very high MaxRequestsPerChild, but not unlimited as this
349
will prevent cleanup.
350
351
You'll want to have a MaxRequestsPerChild set to a non-zero value, as
352
DosEvasive cleans up its internal hashes only on exit.  The default
353
MaxRequestsPerChild is usually 10000.  This should suffice in only allowing
354
a few requests per 10000 per child through in the event of an attack (although
355
if you use DOSSystemCommand to firewall the IP address, a hole will no
356
longer be open in between child cycles).
357
358
TESTING
359
360
Want to make sure it's working? Run test.pl, and view the response codes.
361
It's best to run it several times on the same machine as the web server until
362
you get 403 Forbidden messages. Some larger servers with high child counts 
363
may require more of a beating than smaller servers before blacklisting
364
addresses. 
365
366
Please don't use this script to DoS others without their permission.
367
368
KNOWN BUGS
369
370
- This module appears to conflict with the Microsoft Frontpage Extensions.
371
  Frontpage sucks anyway, so if you're using Frontpage I assume you're asking
372
  for problems, and not really interested in conserving server resources anyway.
373
374
FEEDBACK 
375
376
Please email me with questions, constructive comments, or feedback:
377
  jonathan@nuclearelephant.com
378
(-)work/mod_evasive/mod_evasive.c (-709 lines)
Lines 1-709 Link Here
1
/* $Id: mod_evasive.c,v 1.3 2005/10/08 19:17:14 jonz Exp $ */
2
3
/*
4
mod_evasive for Apache 1.3
5
Copyright (c) by Jonathan A. Zdziarski
6
7
LICENSE
8
9
This program is free software; you can redistribute it and/or
10
modify it under the terms of the GNU General Public License
11
as published by the Free Software Foundation; version 2
12
of the License.
13
14
This program is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
GNU General Public License for more details.
18
19
You should have received a copy of the GNU General Public License
20
along with this program; if not, write to the Free Software
21
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
                                                                                
23
*/
24
25
#include <sys/types.h>
26
#include <sys/socket.h>
27
#include <sys/stat.h>
28
#include <netinet/in.h>
29
#include <arpa/inet.h>
30
#include <string.h>
31
#include <stdlib.h>
32
#include <sys/types.h>
33
#include <time.h>
34
#include <syslog.h>
35
#include <errno.h>
36
37
#include "httpd.h"
38
#include "http_core.h"
39
#include "http_config.h"
40
#include "http_log.h"
41
#include "http_request.h"
42
43
module MODULE_VAR_EXPORT evasive_module;
44
45
/* BEGIN DoS Evasive Maneuvers Definitions */
46
47
#define MAILER	"/bin/mail -t %s"
48
#define  LOG( A, ... ) { openlog("mod_evasive", LOG_PID, LOG_DAEMON); syslog( A, __VA_ARGS__ ); closelog(); }
49
50
#define DEFAULT_HASH_TBL_SIZE   3079ul  // Default hash table size
51
#define DEFAULT_PAGE_COUNT      2       // Default max page hit count/interval
52
#define DEFAULT_SITE_COUNT      50      // Default max site hit count/interval
53
#define DEFAULT_PAGE_INTERVAL   1       // Default 1 second page interval
54
#define DEFAULT_SITE_INTERVAL   1       // Default 1 second site interval
55
#define DEFAULT_BLOCKING_PERIOD 10      // Default block time (Seconds)
56
#define DEFAULT_LOG_DIR		"/tmp"
57
58
/* END DoS Evasive Maneuvers Definitions */
59
60
/* BEGIN NTT (Named Timestamp Tree) Headers */
61
62
enum { ntt_num_primes = 28 };
63
64
/* ntt root tree */
65
struct ntt {
66
    long size;
67
    long items;
68
    struct ntt_node **tbl;
69
};
70
71
/* ntt node (entry in the ntt root tree) */
72
struct ntt_node {
73
    char *key;
74
    time_t timestamp;
75
    long count;
76
    struct ntt_node *next;
77
};
78
79
/* ntt cursor */
80
struct ntt_c {
81
  long iter_index;
82
  struct ntt_node *iter_next;
83
};
84
85
struct ntt *ntt_create(long size);
86
struct ntt_node	*ntt_find(struct ntt *ntt, const char *key);
87
struct ntt_node	*ntt_insert(struct ntt *ntt, const char *key, time_t timestamp);
88
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c);
89
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c);
90
int ntt_destroy(struct ntt *ntt);
91
int ntt_delete(struct ntt *ntt, const char *key);
92
long ntt_hashcode(struct ntt *ntt, const char *key);
93
94
/* END NTT (Named Timestamp Tree) Headers */
95
96
97
/* BEGIN DoS Evasive Maneuvers Globals */
98
99
struct ntt *hit_list;	// Our dynamic hash table
100
struct ntt *white_list = NULL; // White list table
101
102
static unsigned long hash_table_size = DEFAULT_HASH_TBL_SIZE;
103
static int page_count      = DEFAULT_PAGE_COUNT;
104
static int page_interval   = DEFAULT_PAGE_INTERVAL;
105
static int site_count      = DEFAULT_SITE_COUNT;
106
static int site_interval   = DEFAULT_SITE_INTERVAL;
107
static int blocking_period = DEFAULT_BLOCKING_PERIOD;
108
static char *log_dir       = NULL;
109
static char *email_notify  = NULL;
110
static char *sys_command   = NULL;
111
int is_whitelisted(const char *ip);
112
static const char *whitelist(cmd_parms *cmd, void *mconfig, char *ip);
113
114
/* END DoS Evasive Maneuvers Globals */
115
116
static void evasive_child_init(server_rec *s, pool *p)
117
{
118
    hit_list   = ntt_create(hash_table_size);
119
}
120
121
static int check_access(request_rec *r) 
122
{
123
    int ret = OK;
124
125
    /* BEGIN Evasive Maneuvers Code */
126
127
    if (r->prev == NULL && r->main == NULL && hit_list != NULL) {
128
      unsigned long address = r->connection->remote_addr.sin_addr.s_addr;
129
      char *text_add = inet_ntoa(r->connection->remote_addr.sin_addr);
130
      char hash_key[2048];
131
      struct ntt_node *n;
132
      time_t t = time(NULL);
133
134
      /* Check whitelist */
135
       
136
      if (is_whitelisted(text_add))
137
        return OK;
138
139
      /* First see if the IP itself is on "hold" */
140
      snprintf(hash_key, 2048, "%ld", address);
141
      n = ntt_find(hit_list, hash_key);
142
143
      if (n != NULL && t-n->timestamp<blocking_period) {
144
 
145
        /* If the IP is on "hold", make it wait longer in 403 land */
146
        ret = FORBIDDEN;
147
        n->timestamp = time(NULL);
148
149
      /* Not on hold, check hit stats */
150
      } else {
151
152
        /* Has URI been hit too much? */
153
        snprintf(hash_key, 2048, "%ld_%s", address, r->uri);
154
        n = ntt_find(hit_list, hash_key);
155
        if (n != NULL) {
156
157
          /* If URI is being hit too much, add to "hold" list and 403 */
158
          if (t-n->timestamp<page_interval && n->count>=page_count) {
159
            ret = FORBIDDEN;
160
            snprintf(hash_key, 2048, "%ld", address);
161
            ntt_insert(hit_list, hash_key, time(NULL));
162
          } else {
163
164
            /* Reset our hit count list as necessary */
165
            if (t-n->timestamp>=page_interval) {
166
              n->count=0;
167
            }
168
          }
169
          n->timestamp = t;
170
          n->count++;
171
        } else {
172
          ntt_insert(hit_list, hash_key, t);
173
        }
174
175
        /* Has site been hit too much? */
176
        snprintf(hash_key, 2048, "%ld_SITE", address);
177
        n = ntt_find(hit_list, hash_key);
178
        if (n != NULL) {
179
180
          /* If site is being hit too much, add to "hold" list and 403 */
181
          if (t-n->timestamp<site_interval && n->count>=site_count) {
182
            ret = FORBIDDEN;
183
            snprintf(hash_key, 2048, "%ld", address);
184
            ntt_insert(hit_list, hash_key, time(NULL));
185
          } else {
186
187
            /* Reset our hit count list as necessary */
188
            if (t-n->timestamp>=site_interval) {
189
              n->count=0;
190
            }
191
          }
192
          n->timestamp = t;
193
          n->count++;
194
        } else {
195
          ntt_insert(hit_list, hash_key, t);
196
        }
197
      }
198
199
      /* Perform email notification and system functions */
200
      if (ret == FORBIDDEN) {
201
        char filename[1024];
202
        struct stat s;
203
        FILE *file;
204
205
        snprintf(filename, sizeof(filename), "%s/dos-%s", log_dir != NULL ? log_dir : DEFAULT_LOG_DIR, text_add);
206
        if (stat(filename, &s)) {
207
          file = fopen(filename, "w");
208
          if (file != NULL) {
209
            fprintf(file, "%ld\n", getpid());
210
            fclose(file);
211
212
            LOG(LOG_ALERT, "Blacklisting address %s: possible attack.", text_add)
213
            if (email_notify != NULL) {
214
              snprintf(filename, sizeof(filename), MAILER, email_notify);
215
              file = popen(filename, "w");
216
              if (file != NULL) {
217
                fprintf(file, "To: %s\n", email_notify);
218
                fprintf(file, "Subject: HTTP BLACKLIST %s\n\n", text_add);
219
                fprintf(file, "mod_evasive HTTP Blacklisted %s\n", text_add);
220
                pclose(file);
221
              }
222
            }
223
224
            if (sys_command != NULL) {
225
              snprintf(filename, sizeof(filename), sys_command, text_add);
226
              system(filename);
227
            }
228
 
229
          } else {
230
		LOG(LOG_ALERT, "Couldn't open logfile %s: %s",filename, strerror(errno));
231
	  }
232
233
        } /* if (temp file does not exist) */
234
235
      } /* if (ret == FORBIDDEN) */
236
237
    } /* if (r->prev == NULL && r->main == NULL && hit_list != NULL) */
238
239
    /* END Evasive Maneuvers Code */
240
241
    if (ret == FORBIDDEN
242
	&& (ap_satisfies(r) != SATISFY_ANY || !ap_some_auth_required(r))) {
243
	ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
244
		  "client denied by server configuration: %s",
245
		  r->filename);
246
    }
247
248
    return ret;
249
}
250
251
static void evasive_child_exit(server_rec *s, pool *p) 
252
{
253
    ntt_destroy(hit_list);
254
    free(email_notify);
255
    free(sys_command);
256
}
257
258
259
/* BEGIN NTT (Named Timestamp Tree) Functions */
260
261
static unsigned long ntt_prime_list[ntt_num_primes] = 
262
{
263
    53ul,         97ul,         193ul,       389ul,       769ul,
264
    1543ul,       3079ul,       6151ul,      12289ul,     24593ul,
265
    49157ul,      98317ul,      196613ul,    393241ul,    786433ul,
266
    1572869ul,    3145739ul,    6291469ul,   12582917ul,  25165843ul,
267
    50331653ul,   100663319ul,  201326611ul, 402653189ul, 805306457ul,
268
    1610612741ul, 3221225473ul, 4294967291ul
269
};
270
271
272
/* Find the numeric position in the hash table based on key and modulus */
273
274
long ntt_hashcode(struct ntt *ntt, const char *key) {
275
    unsigned long val = 0;
276
    for (; *key; ++key) val = 5 * val + *key;
277
    return(val % ntt->size);
278
}
279
280
/* Creates a single node in the tree */
281
282
struct ntt_node *ntt_node_create(const char *key) {
283
    char *node_key;
284
    struct ntt_node* node;
285
286
    node = (struct ntt_node *) malloc(sizeof(struct ntt_node));
287
    if (node == NULL) {
288
	return NULL;
289
    }
290
    if ((node_key = strdup(key)) == NULL) {
291
        free(node);
292
	return NULL;
293
    }
294
    node->key = node_key;
295
    node->timestamp = time(NULL);
296
    node->next = NULL;
297
    return(node);
298
}
299
300
/* Tree initializer */
301
302
struct ntt *ntt_create(long size) {
303
    long i = 0;
304
    struct ntt *ntt = (struct ntt *) malloc(sizeof(struct ntt));
305
306
    if (ntt == NULL)
307
        return NULL;
308
    while (ntt_prime_list[i] < size) { i++; }
309
    ntt->size  = ntt_prime_list[i];
310
    ntt->items = 0;
311
    ntt->tbl   = (struct ntt_node **) calloc(ntt->size, sizeof(struct ntt_node *));
312
    if (ntt->tbl == NULL) {
313
        free(ntt);
314
        return NULL;
315
    }
316
    return(ntt);
317
}
318
319
/* Find an object in the tree */
320
321
struct ntt_node *ntt_find(struct ntt *ntt, const char *key) {
322
    long hash_code;
323
    struct ntt_node *node;
324
325
    if (ntt == NULL) return NULL;
326
327
    hash_code = ntt_hashcode(ntt, key);
328
    node = ntt->tbl[hash_code];
329
330
    while (node) {
331
        if (!strcmp(key, node->key)) {
332
            return(node);
333
        }
334
        node = node->next;
335
    }
336
    return((struct ntt_node *)NULL);
337
}
338
339
/* Insert a node into the tree */
340
341
struct ntt_node *ntt_insert(struct ntt *ntt, const char *key, time_t timestamp) {
342
    long hash_code;
343
    struct ntt_node *parent;
344
    struct ntt_node *node;
345
    struct ntt_node *new_node = NULL;
346
347
    if (ntt == NULL) return NULL;
348
349
    hash_code = ntt_hashcode(ntt, key);
350
    parent	= NULL;
351
    node	= ntt->tbl[hash_code];
352
353
    while (node != NULL) {
354
        if (strcmp(key, node->key) == 0) { 
355
            new_node = node;
356
            node = NULL;
357
        }
358
359
	if (new_node == NULL) {
360
          parent = node;
361
          node = node->next;
362
        }
363
    }
364
365
    if (new_node != NULL) {
366
        new_node->timestamp = timestamp;
367
        new_node->count = 0;
368
        return new_node; 
369
    }
370
371
    /* Create a new node */
372
    new_node = ntt_node_create(key);
373
    new_node->timestamp = timestamp;
374
    new_node->timestamp = 0;
375
376
    ntt->items++;
377
378
    /* Insert */
379
    if (parent) {  /* Existing parent */
380
	parent->next = new_node;
381
        return new_node;  /* Return the locked node */
382
    }
383
384
    /* No existing parent; add directly to hash table */
385
    ntt->tbl[hash_code] = new_node;
386
    return new_node;
387
}
388
389
/* Tree destructor */
390
391
int ntt_destroy(struct ntt *ntt) {
392
    struct ntt_node *node, *next;
393
    struct ntt_c c;
394
395
    if (ntt == NULL) return -1;
396
397
    node = c_ntt_first(ntt, &c);
398
    while(node != NULL) {
399
        next = c_ntt_next(ntt, &c);
400
        ntt_delete(ntt, node->key);
401
        node = next;
402
    }
403
404
    free(ntt->tbl);
405
    free(ntt);
406
    ntt = (struct ntt *) NULL;
407
408
    return 0;
409
}
410
411
/* Delete a single node in the tree */
412
413
int ntt_delete(struct ntt *ntt, const char *key) {
414
    long hash_code;
415
    struct ntt_node *parent = NULL;
416
    struct ntt_node *node;
417
    struct ntt_node *del_node = NULL;
418
419
    if (ntt == NULL) return -1;
420
421
    hash_code = ntt_hashcode(ntt, key);
422
    node        = ntt->tbl[hash_code];
423
424
    while (node != NULL) {
425
        if (strcmp(key, node->key) == 0) {
426
            del_node = node;
427
            node = NULL;
428
        }
429
430
        if (del_node == NULL) {
431
          parent = node;
432
          node = node->next;
433
        }
434
    }
435
436
    if (del_node != NULL) {
437
438
        if (parent) {
439
            parent->next = del_node->next;
440
        } else {
441
            ntt->tbl[hash_code] = del_node->next;
442
        }
443
444
        free(del_node->key);
445
        free(del_node);
446
        ntt->items--;
447
448
        return 0;
449
    }
450
451
    return -5;
452
}
453
454
/* Point cursor to first item in tree */
455
456
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c) {
457
458
    c->iter_index = 0;
459
    c->iter_next = (struct ntt_node *)NULL;
460
    return(c_ntt_next(ntt, c));
461
}
462
463
/* Point cursor to next iteration in tree */
464
465
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c) {
466
    long index;
467
    struct ntt_node *node = c->iter_next;
468
469
    if (ntt == NULL) return NULL;
470
471
    if (node) {
472
        if (node != NULL) {
473
            c->iter_next = node->next;
474
            return (node);
475
        }
476
    }
477
478
    if (! node) {
479
        while (c->iter_index < ntt->size) {
480
            index = c->iter_index++;
481
482
            if (ntt->tbl[index]) {
483
                c->iter_next = ntt->tbl[index]->next;
484
                return(ntt->tbl[index]);
485
            }
486
        }
487
    }
488
    return((struct ntt_node *)NULL);
489
}
490
491
/* END NTT (Named Pointer Tree) Functions */
492
493
/* BEGIN Configuration Functions */
494
495
static const char *
496
get_hash_tbl_size(cmd_parms *cmd, void *dconfig, char *value) {
497
    long n = strtol(value, NULL, 0);
498
499
    if (n<=0) 
500
        hash_table_size = DEFAULT_HASH_TBL_SIZE;
501
    else 
502
        hash_table_size = n;
503
504
    return NULL;
505
}
506
507
static const char *
508
get_page_count(cmd_parms *cmd, void *dconfig, char *value) {
509
    long n = strtol(value, NULL, 0);
510
    if (n<=0) 
511
        page_count = DEFAULT_PAGE_COUNT;
512
    else
513
        page_count = n;
514
515
    return NULL;
516
}
517
518
static const char *
519
get_site_count(cmd_parms *cmd, void *dconfig, char *value) {
520
    long n = strtol(value, NULL, 0);
521
    if (n<=0) 
522
        site_count = DEFAULT_SITE_COUNT;
523
    else
524
        site_count = n;
525
526
    return NULL;
527
}
528
529
static const char *
530
get_page_interval(cmd_parms *cmd, void *dconfig, char *value) {
531
    long n = strtol(value, NULL, 0);
532
    if (n<=0) 
533
        page_interval = DEFAULT_PAGE_INTERVAL;
534
    else 
535
        page_interval = n;
536
537
    return NULL;
538
}
539
540
static const char *
541
get_site_interval(cmd_parms *cmd, void *dconfig, char *value) {
542
    long n = strtol(value, NULL, 0);
543
    if (n<=0) 
544
        site_interval = DEFAULT_SITE_INTERVAL;
545
    else
546
        site_interval = n;
547
548
  return NULL;
549
}
550
551
static const char *
552
get_blocking_period(cmd_parms *cmd, void *dconfig, char *value) {
553
    long n = strtol(value, NULL, 0);
554
    if (n<=0) 
555
        blocking_period = DEFAULT_BLOCKING_PERIOD;
556
    else 
557
        blocking_period = n;
558
559
    return NULL;
560
}
561
562
static const char *
563
get_log_dir(cmd_parms *cmd, void *dconfig, char *value) {
564
    if (value != NULL && value[0] != 0) {
565
        if (log_dir != NULL)
566
            free(log_dir);
567
        log_dir = strdup(value);
568
    }
569
570
    return NULL;
571
}
572
573
static const char *
574
get_email_notify(cmd_parms *cmd, void *dconfig, char *value) {
575
    if (value != NULL && value[0] != 0) {
576
        if (email_notify != NULL)
577
            free(email_notify);
578
        email_notify = strdup(value);
579
    }
580
581
    return NULL;
582
}
583
584
static const char *
585
get_sys_command(cmd_parms *cmd, void *dconfig, char *value) {
586
    if (value != NULL && value[0] != 0) {
587
        if (sys_command != NULL)
588
            free(sys_command);
589
        sys_command = strdup(value);
590
    }
591
 
592
    return NULL;
593
} 
594
595
static const char *whitelist(cmd_parms *cmd, void *mconfig, char *ip) {
596
    char entry[128];
597
598
    if (white_list == NULL) 
599
        white_list = ntt_create(53ul);
600
    snprintf(entry, sizeof(entry), "%s", ip);
601
    ntt_insert(white_list, entry, time(NULL));
602
603
    return NULL;
604
}
605
606
/* END Configuration Functions */
607
608
int is_whitelisted(const char *ip) {
609
    char hashkey[128];
610
    char octet[4][4];
611
    char *dip;
612
    char *oct;
613
    int i = 0;
614
                                                                                
615
    memset(octet, 0, 16);
616
    dip = strdup(ip);
617
    if (dip == NULL)
618
        return 0;
619
                                                                                
620
    oct = strtok(dip, ".");
621
    while(oct != NULL && i<4) {
622
        if (strlen(oct)<=3)
623
          strcpy(octet[i], oct);
624
        i++;
625
        oct = strtok(NULL, ".");
626
    }
627
    free(dip);
628
                                                                                
629
    /* Exact Match */
630
    snprintf(hashkey, sizeof(hashkey), "%s", ip);
631
    if (ntt_find(white_list, hashkey)!=NULL)
632
        return 1;
633
                                                                                
634
    /* IPv4 Wildcards */
635
    snprintf(hashkey, sizeof(hashkey), "%s.*.*.*", octet[0]);
636
    if (ntt_find(white_list, hashkey)!=NULL)
637
        return 1;
638
                                                                                
639
    snprintf(hashkey, sizeof(hashkey), "%s.%s.*.*", 
640
             octet[0], octet[1]);
641
    if (ntt_find(white_list, hashkey)!=NULL)
642
        return 1;
643
644
    snprintf(hashkey, sizeof(hashkey), "%s.%s.%s.*", 
645
             octet[0], octet[1], octet[2]);
646
    if (ntt_find(white_list, hashkey)!=NULL)
647
        return 1;
648
649
    /* No match */
650
    return 0;
651
}
652
653
static command_rec command_table[] = {
654
655
        { "DOSWhitelist", whitelist, NULL, RSRC_CONF, ITERATE,
656
        "Whitelist an IP or Wildcard. "},
657
658
	{ "DOSHashTableSize", get_hash_tbl_size, NULL, RSRC_CONF, TAKE1,
659
	"Set size of hash table. " },
660
661
	{ "DOSPageCount", get_page_count, NULL, RSRC_CONF, TAKE1,
662
	"Set maximum page hit count per interval. " },
663
664
	{ "DOSSiteCount", get_site_count, NULL, RSRC_CONF, TAKE1,
665
	"Set maximum site hit count per interval. " },
666
667
	{ "DOSPageInterval", get_page_interval, NULL, RSRC_CONF, TAKE1,
668
	"Set page interval. " },
669
670
	{ "DOSSiteInterval", get_site_interval, NULL, RSRC_CONF, TAKE1,
671
	"Set site interval. " }, 
672
673
	{ "DOSLogDir", get_log_dir, NULL, RSRC_CONF, TAKE1,
674
        "Set log dir. "},
675
676
	{ "DOSEmailNotify", get_email_notify, NULL, RSRC_CONF, TAKE1,
677
        "Set email notification. "},
678
679
	{ "DOSSystemCommand", get_sys_command, NULL, RSRC_CONF, TAKE1,
680
        "Set system command. "},
681
682
        { "DOSBlockingPeriod", get_blocking_period, NULL, RSRC_CONF, TAKE1,
683
        "Set blocking period for detected DoS IPs. "},
684
685
	{ NULL }
686
};
687
688
module MODULE_VAR_EXPORT evasive_module = {
689
    STANDARD_MODULE_STUFF,
690
    NULL,                              /* initializer */
691
    NULL,                              /* dir config creator */
692
    NULL,                              /* dir config merger */
693
    NULL,                              /* server config creator */
694
    NULL,                              /* server config merger */
695
    command_table,                     /* command table */
696
    NULL,                              /* handlers */
697
    NULL,                              /* filename translation */
698
    NULL,                              /* check_user_id */
699
    NULL,                              /* check auth */
700
    check_access,                      /* check access */
701
    NULL,                              /* type_checker */
702
    NULL,                              /* fixups */
703
    NULL,                              /* logger */
704
    NULL,                              /* header parser */
705
    evasive_child_init,                /* child_init */
706
    evasive_child_exit,                /* child_exit */
707
    NULL                               /* post read-request */
708
};
709
(-)work/mod_evasive/mod_evasive20.c (-699 lines)
Lines 1-699 Link Here
1
/*
2
mod_evasive for Apache 2
3
Copyright (c) by Jonathan A. Zdziarski
4
5
LICENSE
6
                                                                                
7
This program is free software; you can redistribute it and/or
8
modify it under the terms of the GNU General Public License
9
as published by the Free Software Foundation; either version 2
10
of the License, or (at your option) any later version.
11
                                                                                
12
This program is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
GNU General Public License for more details.
16
                                                                                
17
You should have received a copy of the GNU General Public License
18
along with this program; if not, write to the Free Software
19
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20
21
*/
22
23
#include <sys/types.h>
24
#include <sys/socket.h>
25
#include <sys/stat.h>
26
#include <netinet/in.h>
27
#include <arpa/inet.h>
28
#include <string.h>
29
#include <stdlib.h>
30
#include <sys/types.h>
31
#include <time.h>
32
#include <syslog.h>
33
#include <errno.h>
34
35
#include "httpd.h"
36
#include "http_core.h"
37
#include "http_config.h"
38
#include "http_log.h"
39
#include "http_request.h"
40
41
module AP_MODULE_DECLARE_DATA evasive20_module;
42
43
/* BEGIN DoS Evasive Maneuvers Definitions */
44
45
#define MAILER	"/bin/mail %s"
46
#define  LOG( A, ... ) { openlog("mod_evasive", LOG_PID, LOG_DAEMON); syslog( A, __VA_ARGS__ ); closelog(); }
47
48
#define DEFAULT_HASH_TBL_SIZE   3097ul  // Default hash table size
49
#define DEFAULT_PAGE_COUNT      2       // Default maximum page hit count per interval
50
#define DEFAULT_SITE_COUNT      50      // Default maximum site hit count per interval
51
#define DEFAULT_PAGE_INTERVAL   1       // Default 1 Second page interval
52
#define DEFAULT_SITE_INTERVAL   1       // Default 1 Second site interval
53
#define DEFAULT_BLOCKING_PERIOD 10      // Default for Detected IPs; blocked for 10 seconds
54
#define DEFAULT_LOG_DIR		"/tmp"  // Default temp directory
55
56
/* END DoS Evasive Maneuvers Definitions */
57
58
/* BEGIN NTT (Named Timestamp Tree) Headers */
59
60
enum { ntt_num_primes = 28 };
61
62
/* ntt root tree */
63
struct ntt {
64
    long size;
65
    long items;
66
    struct ntt_node **tbl;
67
};
68
69
/* ntt node (entry in the ntt root tree) */
70
struct ntt_node {
71
    char *key;
72
    time_t timestamp;
73
    long count;
74
    struct ntt_node *next;
75
};
76
77
/* ntt cursor */
78
struct ntt_c {
79
  long iter_index;
80
  struct ntt_node *iter_next;
81
};
82
83
struct ntt *ntt_create(long size);
84
int ntt_destroy(struct ntt *ntt);
85
struct ntt_node	*ntt_find(struct ntt *ntt, const char *key);
86
struct ntt_node	*ntt_insert(struct ntt *ntt, const char *key, time_t timestamp);
87
int ntt_delete(struct ntt *ntt, const char *key);
88
long ntt_hashcode(struct ntt *ntt, const char *key);	
89
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c);
90
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c);
91
92
/* END NTT (Named Timestamp Tree) Headers */
93
94
95
/* BEGIN DoS Evasive Maneuvers Globals */
96
97
struct ntt *hit_list;	// Our dynamic hash table
98
99
static unsigned long hash_table_size = DEFAULT_HASH_TBL_SIZE;
100
static int page_count = DEFAULT_PAGE_COUNT;
101
static int page_interval = DEFAULT_PAGE_INTERVAL;
102
static int site_count = DEFAULT_SITE_COUNT;
103
static int site_interval = DEFAULT_SITE_INTERVAL;
104
static int blocking_period = DEFAULT_BLOCKING_PERIOD;
105
static char *email_notify = NULL;
106
static char *log_dir = NULL;
107
static char *system_command = NULL;
108
static const char *whitelist(cmd_parms *cmd, void *dconfig, const char *ip);
109
int is_whitelisted(const char *ip);
110
111
/* END DoS Evasive Maneuvers Globals */
112
113
static void * create_hit_list(apr_pool_t *p, server_rec *s) 
114
{
115
    /* Create a new hit list for this listener */
116
117
    hit_list = ntt_create(hash_table_size);
118
}
119
120
static const char *whitelist(cmd_parms *cmd, void *dconfig, const char *ip)
121
{
122
  char entry[128];
123
  snprintf(entry, sizeof(entry), "WHITELIST_%s", ip);
124
  ntt_insert(hit_list, entry, time(NULL));
125
  
126
  return NULL;
127
}
128
129
130
static int access_checker(request_rec *r) 
131
{
132
    int ret = OK;
133
134
    /* BEGIN DoS Evasive Maneuvers Code */
135
136
    if (r->prev == NULL && r->main == NULL && hit_list != NULL) {
137
      char hash_key[2048];
138
      struct ntt_node *n;
139
      time_t t = time(NULL);
140
141
      /* Check whitelist */
142
      if (is_whitelisted(r->connection->remote_ip)) 
143
        return OK;
144
145
      /* First see if the IP itself is on "hold" */
146
      n = ntt_find(hit_list, r->connection->remote_ip);
147
148
      if (n != NULL && t-n->timestamp<blocking_period) {
149
 
150
        /* If the IP is on "hold", make it wait longer in 403 land */
151
        ret = HTTP_FORBIDDEN;
152
        n->timestamp = time(NULL);
153
154
      /* Not on hold, check hit stats */
155
      } else {
156
157
        /* Has URI been hit too much? */
158
        snprintf(hash_key, 2048, "%s_%s", r->connection->remote_ip, r->uri);
159
        n = ntt_find(hit_list, hash_key);
160
        if (n != NULL) {
161
162
          /* If URI is being hit too much, add to "hold" list and 403 */
163
          if (t-n->timestamp<page_interval && n->count>=page_count) {
164
            ret = HTTP_FORBIDDEN;
165
            ntt_insert(hit_list, r->connection->remote_ip, time(NULL));
166
          } else {
167
168
            /* Reset our hit count list as necessary */
169
            if (t-n->timestamp>=page_interval) {
170
              n->count=0;
171
            }
172
          }
173
          n->timestamp = t;
174
          n->count++;
175
        } else {
176
          ntt_insert(hit_list, hash_key, t);
177
        }
178
179
        /* Has site been hit too much? */
180
        snprintf(hash_key, 2048, "%s_SITE", r->connection->remote_ip);
181
        n = ntt_find(hit_list, hash_key);
182
        if (n != NULL) {
183
184
          /* If site is being hit too much, add to "hold" list and 403 */
185
          if (t-n->timestamp<site_interval && n->count>=site_count) {
186
            ret = HTTP_FORBIDDEN;
187
            ntt_insert(hit_list, r->connection->remote_ip, time(NULL));
188
          } else {
189
190
            /* Reset our hit count list as necessary */
191
            if (t-n->timestamp>=site_interval) {
192
              n->count=0;
193
            }
194
          }
195
          n->timestamp = t;
196
          n->count++;
197
        } else {
198
          ntt_insert(hit_list, hash_key, t);
199
        }
200
      }
201
202
      /* Perform email notification and system functions */
203
      if (ret == HTTP_FORBIDDEN) {
204
        char filename[1024];
205
        struct stat s;
206
        FILE *file;
207
208
        snprintf(filename, sizeof(filename), "%s/dos-%s", log_dir != NULL ? log_dir : DEFAULT_LOG_DIR, r->connection->remote_ip);
209
        if (stat(filename, &s)) {
210
          file = fopen(filename, "w");
211
          if (file != NULL) {
212
            fprintf(file, "%ld\n", getpid());
213
            fclose(file);
214
215
            LOG(LOG_ALERT, "Blacklisting address %s: possible DoS attack.", r->connection->remote_ip);
216
            if (email_notify != NULL) {
217
              snprintf(filename, sizeof(filename), MAILER, email_notify);
218
              file = popen(filename, "w");
219
              if (file != NULL) {
220
                fprintf(file, "To: %s\n", email_notify);
221
                fprintf(file, "Subject: HTTP BLACKLIST %s\n\n", r->connection->remote_ip);
222
                fprintf(file, "mod_evasive HTTP Blacklisted %s\n", r->connection->remote_ip);
223
                pclose(file);
224
              }
225
            }
226
227
            if (system_command != NULL) {
228
              snprintf(filename, sizeof(filename), system_command, r->connection->remote_ip);
229
              system(filename);
230
            }
231
 
232
          } else {
233
            LOG(LOG_ALERT, "Couldn't open logfile %s: %s",filename, strerror(errno));
234
	  }
235
236
        } /* if (temp file does not exist) */
237
238
      } /* if (ret == HTTP_FORBIDDEN) */
239
240
    } /* if (r->prev == NULL && r->main == NULL && hit_list != NULL) */
241
242
    /* END DoS Evasive Maneuvers Code */
243
244
    if (ret == HTTP_FORBIDDEN
245
	&& (ap_satisfies(r) != SATISFY_ANY || !ap_some_auth_required(r))) {
246
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
247
            "client denied by server configuration: %s",
248
            r->filename);
249
    }
250
251
    return ret;
252
}
253
254
int is_whitelisted(const char *ip) {
255
  char hashkey[128];
256
  char octet[4][4];
257
  char *dip;
258
  char *oct;
259
  int i = 0;
260
261
  memset(octet, 0, 16);
262
  dip = strdup(ip);
263
  if (dip == NULL)
264
    return 0;
265
266
  oct = strtok(dip, ".");
267
  while(oct != NULL && i<4) {
268
    if (strlen(oct)<=3) 
269
      strcpy(octet[i], oct);
270
    i++;
271
    oct = strtok(NULL, ".");
272
  }
273
  free(dip);
274
275
  /* Exact Match */
276
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s", ip); 
277
  if (ntt_find(hit_list, hashkey)!=NULL)
278
    return 1;
279
280
  /* IPv4 Wildcards */ 
281
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.*.*.*", octet[0]);
282
  if (ntt_find(hit_list, hashkey)!=NULL)
283
    return 1;
284
285
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.%s.*.*", octet[0], octet[1]);
286
  if (ntt_find(hit_list, hashkey)!=NULL)
287
    return 1;
288
289
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.%s.%s.*", octet[0], octet[1], octet[2]);
290
  if (ntt_find(hit_list, hashkey)!=NULL)
291
    return 1;
292
293
  /* No match */
294
  return 0;
295
}
296
297
static apr_status_t destroy_hit_list(void *not_used) {
298
  ntt_destroy(hit_list);
299
  free(email_notify);
300
  free(system_command);
301
}
302
303
304
/* BEGIN NTT (Named Timestamp Tree) Functions */
305
306
static unsigned long ntt_prime_list[ntt_num_primes] = 
307
{
308
    53ul,         97ul,         193ul,       389ul,       769ul,
309
    1543ul,       3079ul,       6151ul,      12289ul,     24593ul,
310
    49157ul,      98317ul,      196613ul,    393241ul,    786433ul,
311
    1572869ul,    3145739ul,    6291469ul,   12582917ul,  25165843ul,
312
    50331653ul,   100663319ul,  201326611ul, 402653189ul, 805306457ul,
313
    1610612741ul, 3221225473ul, 4294967291ul
314
};
315
316
317
/* Find the numeric position in the hash table based on key and modulus */
318
319
long ntt_hashcode(struct ntt *ntt, const char *key) {
320
    unsigned long val = 0;
321
    for (; *key; ++key) val = 5 * val + *key;
322
    return(val % ntt->size);
323
}
324
325
/* Creates a single node in the tree */
326
327
struct ntt_node *ntt_node_create(const char *key) {
328
    char *node_key;
329
    struct ntt_node* node;
330
331
    node = (struct ntt_node *) malloc(sizeof(struct ntt_node));
332
    if (node == NULL) {
333
	return NULL;
334
    }
335
    if ((node_key = strdup(key)) == NULL) {
336
        free(node);
337
	return NULL;
338
    }
339
    node->key = node_key;
340
    node->timestamp = time(NULL);
341
    node->next = NULL;
342
    return(node);
343
}
344
345
/* Tree initializer */
346
347
struct ntt *ntt_create(long size) {
348
    long i = 0;
349
    struct ntt *ntt = (struct ntt *) malloc(sizeof(struct ntt));
350
351
    if (ntt == NULL)
352
        return NULL;
353
    while (ntt_prime_list[i] < size) { i++; }
354
    ntt->size  = ntt_prime_list[i];
355
    ntt->items = 0;
356
    ntt->tbl   = (struct ntt_node **) calloc(ntt->size, sizeof(struct ntt_node *));
357
    if (ntt->tbl == NULL) {
358
        free(ntt);
359
        return NULL;
360
    }
361
    return(ntt);
362
}
363
364
/* Find an object in the tree */
365
366
struct ntt_node *ntt_find(struct ntt *ntt, const char *key) {
367
    long hash_code;
368
    struct ntt_node *node;
369
370
    if (ntt == NULL) return NULL;
371
372
    hash_code = ntt_hashcode(ntt, key);
373
    node = ntt->tbl[hash_code];
374
375
    while (node) {
376
        if (!strcmp(key, node->key)) {
377
            return(node);
378
        }
379
        node = node->next;
380
    }
381
    return((struct ntt_node *)NULL);
382
}
383
384
/* Insert a node into the tree */
385
386
struct ntt_node *ntt_insert(struct ntt *ntt, const char *key, time_t timestamp) {
387
    long hash_code;
388
    struct ntt_node *parent;
389
    struct ntt_node *node;
390
    struct ntt_node *new_node = NULL;
391
392
    if (ntt == NULL) return NULL;
393
394
    hash_code = ntt_hashcode(ntt, key);
395
    parent	= NULL;
396
    node	= ntt->tbl[hash_code];
397
398
    while (node != NULL) {
399
        if (strcmp(key, node->key) == 0) { 
400
            new_node = node;
401
            node = NULL;
402
        }
403
404
	if (new_node == NULL) {
405
          parent = node;
406
          node = node->next;
407
        }
408
    }
409
410
    if (new_node != NULL) {
411
        new_node->timestamp = timestamp;
412
        new_node->count = 0;
413
        return new_node; 
414
    }
415
416
    /* Create a new node */
417
    new_node = ntt_node_create(key);
418
    new_node->timestamp = timestamp;
419
    new_node->timestamp = 0;
420
421
    ntt->items++;
422
423
    /* Insert */
424
    if (parent) {  /* Existing parent */
425
	parent->next = new_node;
426
        return new_node;  /* Return the locked node */
427
    }
428
429
    /* No existing parent; add directly to hash table */
430
    ntt->tbl[hash_code] = new_node;
431
    return new_node;
432
}
433
434
/* Tree destructor */
435
436
int ntt_destroy(struct ntt *ntt) {
437
    struct ntt_node *node, *next;
438
    struct ntt_c c;
439
440
    if (ntt == NULL) return -1;
441
442
    node = c_ntt_first(ntt, &c);
443
    while(node != NULL) {
444
        next = c_ntt_next(ntt, &c);
445
        ntt_delete(ntt, node->key);
446
        node = next;
447
    }
448
449
    free(ntt->tbl);
450
    free(ntt);
451
    ntt = (struct ntt *) NULL;
452
453
    return 0;
454
}
455
456
/* Delete a single node in the tree */
457
458
int ntt_delete(struct ntt *ntt, const char *key) {
459
    long hash_code;
460
    struct ntt_node *parent = NULL;
461
    struct ntt_node *node;
462
    struct ntt_node *del_node = NULL;
463
464
    if (ntt == NULL) return -1;
465
466
    hash_code = ntt_hashcode(ntt, key);
467
    node        = ntt->tbl[hash_code];
468
469
    while (node != NULL) {
470
        if (strcmp(key, node->key) == 0) {
471
            del_node = node;
472
            node = NULL;
473
        }
474
475
        if (del_node == NULL) {
476
          parent = node;
477
          node = node->next;
478
        }
479
    }
480
481
    if (del_node != NULL) {
482
483
        if (parent) {
484
            parent->next = del_node->next;
485
        } else {
486
            ntt->tbl[hash_code] = del_node->next;
487
        }
488
489
        free(del_node->key);
490
        free(del_node);
491
        ntt->items--;
492
493
        return 0;
494
    }
495
496
    return -5;
497
}
498
499
/* Point cursor to first item in tree */
500
501
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c) {
502
503
    c->iter_index = 0;
504
    c->iter_next = (struct ntt_node *)NULL;
505
    return(c_ntt_next(ntt, c));
506
}
507
508
/* Point cursor to next iteration in tree */
509
510
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c) {
511
    long index;
512
    struct ntt_node *node = c->iter_next;
513
514
    if (ntt == NULL) return NULL;
515
516
    if (node) {
517
        if (node != NULL) {
518
            c->iter_next = node->next;
519
            return (node);
520
        }
521
    }
522
523
    if (! node) {
524
        while (c->iter_index < ntt->size) {
525
            index = c->iter_index++;
526
527
            if (ntt->tbl[index]) {
528
                c->iter_next = ntt->tbl[index]->next;
529
                return(ntt->tbl[index]);
530
            }
531
        }
532
    }
533
    return((struct ntt_node *)NULL);
534
}
535
536
/* END NTT (Named Pointer Tree) Functions */
537
538
539
/* BEGIN Configuration Functions */
540
541
static const char *
542
get_hash_tbl_size(cmd_parms *cmd, void *dconfig, const char *value) {
543
  long n = strtol(value, NULL, 0);
544
545
  if (n<=0) {
546
    hash_table_size = DEFAULT_HASH_TBL_SIZE;
547
  } else  {
548
    hash_table_size = n;
549
  }
550
551
  return NULL;
552
}
553
554
static const char *
555
get_page_count(cmd_parms *cmd, void *dconfig, const char *value) {
556
  long n = strtol(value, NULL, 0);
557
  if (n<=0) {
558
    page_count = DEFAULT_PAGE_COUNT;
559
  } else {
560
    page_count = n;
561
  }
562
563
  return NULL;
564
}
565
566
static const char *
567
get_site_count(cmd_parms *cmd, void *dconfig, const char *value) {
568
  long n = strtol(value, NULL, 0);
569
  if (n<=0) {
570
    site_count = DEFAULT_SITE_COUNT;
571
  } else {
572
    site_count = n;
573
  }
574
575
  return NULL;
576
}
577
578
static const char *
579
get_page_interval(cmd_parms *cmd, void *dconfig, const char *value) {
580
  long n = strtol(value, NULL, 0);
581
  if (n<=0) {
582
    page_interval = DEFAULT_PAGE_INTERVAL;
583
  } else {
584
    page_interval = n;
585
  }
586
587
  return NULL;
588
}
589
590
static const char *
591
get_site_interval(cmd_parms *cmd, void *dconfig, const char *value) {
592
  long n = strtol(value, NULL, 0);
593
  if (n<=0) {
594
    site_interval = DEFAULT_SITE_INTERVAL;
595
  } else {
596
    site_interval = n;
597
  }
598
599
  return NULL;
600
}
601
602
static const char *
603
get_blocking_period(cmd_parms *cmd, void *dconfig, const char *value) {
604
  long n = strtol(value, NULL, 0);
605
  if (n<=0) {
606
    blocking_period = DEFAULT_BLOCKING_PERIOD;
607
  } else {
608
    blocking_period = n;
609
  }
610
611
  return NULL;
612
}
613
614
static const char *
615
get_log_dir(cmd_parms *cmd, void *dconfig, const char *value) {
616
  if (value != NULL && value[0] != 0) {
617
    if (log_dir != NULL)
618
      free(log_dir);
619
    log_dir = strdup(value);
620
  }
621
622
  return NULL;
623
}
624
625
static const char *
626
get_email_notify(cmd_parms *cmd, void *dconfig, const char *value) {
627
  if (value != NULL && value[0] != 0) {
628
    if (email_notify != NULL)
629
      free(email_notify);
630
    email_notify = strdup(value);
631
  }
632
633
  return NULL;
634
}
635
636
static const char *
637
get_system_command(cmd_parms *cmd, void *dconfig, const char *value) {
638
  if (value != NULL && value[0] != 0) {
639
    if (system_command != NULL)
640
      free(system_command);
641
    system_command = strdup(value);
642
  }
643
 
644
  return NULL;
645
} 
646
647
/* END Configuration Functions */
648
649
static const command_rec access_cmds[] =
650
{
651
	AP_INIT_TAKE1("DOSHashTableSize", get_hash_tbl_size, NULL, RSRC_CONF, 
652
		"Set size of hash table"),
653
654
        AP_INIT_TAKE1("DOSPageCount", get_page_count, NULL, RSRC_CONF,
655
		"Set maximum page hit count per interval"),
656
657
        AP_INIT_TAKE1("DOSSiteCount", get_site_count, NULL, RSRC_CONF,
658
		"Set maximum site hit count per interval"),
659
660
        AP_INIT_TAKE1("DOSPageInterval", get_page_interval, NULL, RSRC_CONF,
661
		"Set page interval"),
662
663
	AP_INIT_TAKE1("DOSSiteInterval", get_site_interval, NULL, RSRC_CONF,
664
		"Set site interval"),
665
666
        AP_INIT_TAKE1("DOSBlockingPeriod", get_blocking_period, NULL, RSRC_CONF,
667
		"Set blocking period for detected DoS IPs"),
668
669
	AP_INIT_TAKE1("DOSEmailNotify", get_email_notify, NULL, RSRC_CONF,
670
		"Set email notification"),
671
672
	AP_INIT_TAKE1("DOSLogDir", get_log_dir, NULL, RSRC_CONF,
673
		"Set log dir"),
674
675
	AP_INIT_TAKE1("DOSSystemCommand", get_system_command, NULL, RSRC_CONF,
676
		"Set system command on DoS"),
677
678
        AP_INIT_ITERATE("DOSWhitelist", whitelist, NULL, RSRC_CONF,
679
                "IP-addresses wildcards to whitelist"),
680
681
	{ NULL }
682
};
683
684
static void register_hooks(apr_pool_t *p) {
685
  ap_hook_access_checker(access_checker, NULL, NULL, APR_HOOK_MIDDLE);
686
  apr_pool_cleanup_register(p, NULL, apr_pool_cleanup_null, destroy_hit_list);
687
};
688
689
module AP_MODULE_DECLARE_DATA evasive20_module =
690
{
691
    STANDARD20_MODULE_STUFF,
692
    NULL,
693
    NULL,
694
    create_hit_list,
695
    NULL,
696
    access_cmds,
697
    register_hooks
698
};
699
(-)work/mod_evasive/mod_evasiveNSAPI.c (-608 lines)
Lines 1-608 Link Here
1
/*
2
mod_dosevasive/1.8 for NSAPI
3
 
4
Copyright 2002 by Jonathan A. Zdziarski.  All rights reserved.
5
 
6
LICENSE
7
-------
8
 
9
This distribution may be freely distributed in its original form.  
10
License is granted to make modifications to the source for internal,
11
private use only, provided you retain this notice, disclaimers, author's
12
copyright, and credits.
13
 
14
 
15
DISCLAIMER
16
----------
17
 
18
THIS SOFTWARE IS PROVIDE "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
19
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO WAY SHALL THE
21
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
27
OF THE POSSIBILITY OF SUCH DAMAGE.
28
 
29
*/
30
31
/* This is a port to NSAPI from mod_dosevasive/1.8 for Apache 2.0 
32
   2003-10-29 Reine Persson
33
*/
34
35
#include <sys/types.h>
36
#include <sys/socket.h>
37
#include <sys/stat.h>
38
#include <netinet/in.h>
39
#include <arpa/inet.h>
40
#include <string.h>
41
#include <stdlib.h>
42
#include <sys/types.h>
43
#include <time.h>
44
#include <syslog.h>
45
#include <errno.h>
46
47
#include <nsapi.h>
48
 
49
50
/* BEGIN DoS Evasive Maneuvers Definitions */
51
52
#define DEFAULT_LOG_DIR "/tmp"
53
#define MAILER	"/bin/mail %s"
54
#define  LOG( A, ... ) { openlog("mod_dosevasive", LOG_PID, LOG_DAEMON); syslog( A, __VA_ARGS__ ); closelog(); }
55
56
#define DEFAULT_HASH_TBL_SIZE   3097ul  // Default hash table size
57
#define DEFAULT_PAGE_COUNT      2       // Default maximum page hit count per interval
58
#define DEFAULT_SITE_COUNT      50      // Default maximum site hit count per interval
59
#define DEFAULT_PAGE_INTERVAL   1       // Default 1 Second page interval
60
#define DEFAULT_SITE_INTERVAL   1       // Default 1 Second site interval
61
#define DEFAULT_BLOCKING_PERIOD 10      // Default for Detected IPs; blocked for 10 seconds
62
63
/* END DoS Evasive Maneuvers Definitions */
64
65
static CRITICAL mod_dosevasive_crit;
66
67
/* BEGIN NTT (Named Timestamp Tree) Headers */
68
69
enum { ntt_num_primes = 28 };
70
71
/* ntt root tree */
72
struct ntt {
73
    long size;
74
    long items;
75
    struct ntt_node **tbl;
76
};
77
78
/* ntt node (entry in the ntt root tree) */
79
struct ntt_node {
80
    char *key;
81
    time_t timestamp;
82
    long count;
83
    struct ntt_node *next;
84
};
85
86
/* ntt cursor */
87
struct ntt_c {
88
  long iter_index;
89
  struct ntt_node *iter_next;
90
};
91
92
struct ntt *ntt_create(long size);
93
int ntt_destroy(struct ntt *ntt);
94
struct ntt_node	*ntt_find(struct ntt *ntt, const char *key);
95
struct ntt_node	*ntt_insert(struct ntt *ntt, const char *key, time_t timestamp);
96
int ntt_delete(struct ntt *ntt, const char *key);
97
long ntt_hashcode(struct ntt *ntt, const char *key);	
98
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c);
99
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c);
100
101
/* END NTT (Named Timestamp Tree) Headers */
102
103
104
/* BEGIN DoS Evasive Maneuvers Globals */
105
106
struct ntt *hit_list;	// Our dynamic hash table
107
108
static unsigned long hash_table_size = DEFAULT_HASH_TBL_SIZE;
109
static int page_count = DEFAULT_PAGE_COUNT;
110
static int page_interval = DEFAULT_PAGE_INTERVAL;
111
static int site_count = DEFAULT_SITE_COUNT;
112
static int site_interval = DEFAULT_SITE_INTERVAL;
113
static int blocking_period = DEFAULT_BLOCKING_PERIOD;
114
static char *log_dir = NULL;
115
static char *email_notify = NULL;
116
static char *system_command = NULL;
117
118
static const char * whitelist(const char *ip);
119
int is_whitelisted(const char *ip);
120
static int destroy_hit_list(void *not_used);
121
122
/* END DoS Evasive Maneuvers Globals */
123
124
static char *
125
itemize(char *str,char delim)
126
{
127
    static char *nextitem = NULL;
128
    char *result;
129
 
130
    if(str)
131
        nextitem=str;
132
    if(!nextitem)
133
        return(NULL);
134
    result=nextitem;
135
    while(*nextitem && *nextitem!=delim) 
136
        ++nextitem;
137
    if(*nextitem) 
138
        *nextitem++='\0';
139
    else 
140
        nextitem=NULL;
141
    return(result);
142
}
143
144
 
145
NSAPI_PUBLIC int
146
mod_dosevasive_init(pblock *pb, Session *sn, Request *rq)
147
{
148
  char *ip,*stmp,*white_list=NULL;
149
  int itmp;
150
151
  mod_dosevasive_crit = crit_init();
152
  if ((itmp=atoi(pblock_findval("DOSHashTableSize", pb))) != 0 )
153
    hash_table_size=itmp;
154
  if ((itmp=atoi(pblock_findval("DOSPageCount", pb))) != 0 )
155
    page_count=itmp;
156
  if ((itmp=atoi(pblock_findval("DOSSiteCount", pb))) != 0 )
157
    site_count=itmp;
158
  if ((itmp=atoi(pblock_findval("DOSPageInterval", pb))) != 0 )
159
    page_interval=itmp;
160
  if ((itmp=atoi(pblock_findval("DOSSiteInterval", pb))) != 0 )
161
    site_interval=itmp;
162
  if ((itmp=atoi(pblock_findval("DOSBlockingPeriod", pb))) != 0 )
163
    blocking_period=itmp;
164
  if ((stmp=pblock_findval("DOSLogDir", pb)) != NULL )
165
    log_dir=stmp;
166
  if ((stmp=pblock_findval("DOSEmailNotify", pb)) != NULL )
167
    email_notify=stmp;
168
  if ((stmp=pblock_findval("DOSSystemCommand", pb)) != NULL )
169
    system_command=stmp;
170
171
  white_list=pblock_findval("DOSWhitelist", pb);
172
173
  hit_list = ntt_create(hash_table_size);
174
175
  if ( white_list != NULL ) {
176
    ip=itemize(white_list,',');
177
    while( ip != NULL ) {
178
      whitelist(ip);
179
      ip=itemize(NULL,',');
180
    }
181
  }
182
  return REQ_PROCEED;
183
}
184
 
185
NSAPI_PUBLIC int
186
mod_dosevasive_check(pblock *pb, Session *sn, Request *rq)
187
{
188
  int ret = REQ_PROCEED;
189
190
  /* BEGIN DoS Evasive Maneuvers Code */
191
  
192
  if (pblock_findval("NS_original_uri",rq->vars) == NULL && pblock_findval("referer",rq->headers) == NULL && hit_list != NULL) {
193
    char hash_key[2048];
194
    struct ntt_node *n;
195
    time_t t = time(NULL);
196
197
    /* Check whitelist */
198
    if (is_whitelisted(pblock_findval("ip",sn->client))) 
199
      return REQ_PROCEED;
200
    
201
    /* First see if the IP itself is on "hold" */
202
    n = ntt_find(hit_list, pblock_findval("ip",sn->client));
203
    
204
    if (n != NULL && t-n->timestamp<blocking_period) {
205
      
206
      /* If the IP is on "hold", make it wait longer in 403 land */
207
      ret = REQ_ABORTED;
208
      n->timestamp = time(NULL);
209
      
210
      /* Not on hold, check hit stats */
211
    } else {
212
      
213
      /* Has URI been hit too much? */
214
      snprintf(hash_key, 2048, "%s_%s", pblock_findval("ip",sn->client), pblock_findval("uri",rq->reqpb));
215
      n = ntt_find(hit_list, hash_key);
216
      if (n != NULL) {
217
	
218
	/* If URI is being hit too much, add to "hold" list and 403 */
219
	if (t-n->timestamp<page_interval && n->count>=page_count) {
220
	  ret = REQ_ABORTED;
221
	  ntt_insert(hit_list, pblock_findval("ip",sn->client), time(NULL));
222
	} else {
223
	  
224
	  /* Reset our hit count list as necessary */
225
	  if (t-n->timestamp>=page_interval) {
226
	    n->count=0;
227
	  }
228
	}
229
	n->timestamp = t;
230
	n->count++;
231
      } else {
232
	
233
	ntt_insert(hit_list, hash_key, t);
234
      }
235
      
236
      /* Has site been hit too much? */
237
      snprintf(hash_key, 2048, "%s_SITE", pblock_findval("ip",sn->client));
238
      n = ntt_find(hit_list, hash_key);
239
      if (n != NULL) {
240
	
241
	/* If site is being hit too much, add to "hold" list and 403 */
242
	if (t-n->timestamp<site_interval && n->count>=site_count) {
243
	  ret = REQ_ABORTED;
244
	  ntt_insert(hit_list, pblock_findval("ip",sn->client), time(NULL));
245
	} else {
246
	  
247
	  /* Reset our hit count list as necessary */
248
	  if (t-n->timestamp>=site_interval) {
249
	    n->count=0;
250
	  }
251
	}
252
	n->timestamp = t;
253
	n->count++;
254
      } else {
255
	ntt_insert(hit_list, hash_key, t);
256
      }
257
    }
258
    
259
    /* Perform email notification and system functions */
260
    if (ret == REQ_ABORTED) {
261
      char filename[1024];
262
      struct stat s;
263
      FILE *file;
264
      
265
      snprintf(filename, sizeof(filename), "%s/dos-%s", log_dir != NULL ? log_dir : DEFAULT_LOG_DIR, pblock_findval("ip",sn->client));
266
      if (stat(filename, &s)) {
267
	file = fopen(filename, "w");
268
	if (file != NULL) {
269
	  fprintf(file, "%ld\n", getpid());
270
	  fclose(file);
271
	  
272
	  LOG(LOG_ALERT, "Blacklisting address %s: possible DoS attack.",pblock_findval("ip",sn->client));
273
	  if (email_notify != NULL) {
274
	    snprintf(filename, sizeof(filename), MAILER, email_notify);
275
	    file = popen(filename, "w");
276
	    if (file != NULL) {
277
	      fprintf(file, "To: %s\n", email_notify);
278
	      fprintf(file, "Subject: HTTP BLACKLIST %s\n\n", pblock_findval("ip",sn->client));
279
	      fprintf(file, "mod_dosevasive HTTP Blacklisted %s\n", pblock_findval("ip",sn->client));
280
	      pclose(file);
281
	    } else {
282
	      LOG(LOG_ALERT, "Couldn't open logfile %s: %s",filename, strerror(errno));
283
	    }
284
	  }
285
	  
286
	  if (system_command != NULL) {
287
	    snprintf(filename, sizeof(filename), system_command, pblock_findval("ip",sn->client));
288
	    system(filename);
289
	  }
290
	  
291
	}
292
	
293
      } /* if (temp file does not exist) */
294
      
295
    } /* if (ret == REQ_ABORTED) */
296
    
297
  } /* if (vars->NS_Original_uri == NULL && headers->referer == NULL && hit_list != NULL) */
298
  
299
  /* END DoS Evasive Maneuvers Code */
300
  
301
  if (ret == REQ_ABORTED ) {
302
    log_error(LOG_SECURITY,"mod_dosevasive_check",sn,rq,"client denied by server configuration: %s",pblock_findval("uri",rq->reqpb));
303
    protocol_status(sn, rq, PROTOCOL_FORBIDDEN, NULL);
304
  }
305
  return ret;
306
}
307
308
309
static const char *whitelist(const char *ip)
310
{
311
  char entry[128];
312
  snprintf(entry, sizeof(entry), "WHITELIST_%s", ip);
313
  ntt_insert(hit_list, entry, time(NULL));
314
  
315
  return NULL;
316
}
317
318
319
int is_whitelisted(const char *ip) {
320
  char hashkey[128];
321
  char octet[4][4];
322
  char *dip;
323
  char *oct;
324
  int i = 0;
325
  
326
  memset(octet, 0, 16);
327
  dip = strdup(ip);
328
  if (dip == NULL)
329
    return 0;
330
  
331
  oct = strtok(dip, ".");
332
  while(oct != NULL && i<4) {
333
    if (strlen(oct)<=3) 
334
      strcpy(octet[i], oct);
335
    i++;
336
    oct = strtok(NULL, ".");
337
  }
338
  free(dip);
339
  
340
  /* Exact Match */
341
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s", ip); 
342
  if (ntt_find(hit_list, hashkey)!=NULL)
343
    return 1;
344
  
345
  /* IPv4 Wildcards */ 
346
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.*.*.*", octet[0]);
347
  if (ntt_find(hit_list, hashkey)!=NULL)
348
    return 1;
349
350
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.%s.*.*", octet[0], octet[1]);
351
  if (ntt_find(hit_list, hashkey)!=NULL)
352
    return 1;
353
  
354
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.%s.%s.*", octet[0], octet[1], octet[2]);
355
  if (ntt_find(hit_list, hashkey)!=NULL)
356
    return 1;
357
  
358
  /* No match */
359
  return 0;
360
}
361
362
static int destroy_hit_list(void *not_used) {
363
  ntt_destroy(hit_list);
364
  free(log_dir);
365
  free(email_notify);
366
  free(system_command);
367
  return 0;
368
}
369
370
371
/* BEGIN NTT (Named Timestamp Tree) Functions */
372
373
static unsigned long ntt_prime_list[ntt_num_primes] = 
374
{
375
  53ul,         97ul,         193ul,       389ul,       769ul,
376
  1543ul,       3079ul,       6151ul,      12289ul,     24593ul,
377
  49157ul,      98317ul,      196613ul,    393241ul,    786433ul,
378
  1572869ul,    3145739ul,    6291469ul,   12582917ul,  25165843ul,
379
  50331653ul,   100663319ul,  201326611ul, 402653189ul, 805306457ul,
380
  1610612741ul, 3221225473ul, 4294967291ul
381
};
382
383
384
/* Find the numeric position in the hash table based on key and modulus */
385
386
long ntt_hashcode(struct ntt *ntt, const char *key) {
387
  unsigned long val = 0;
388
  for (; *key; ++key) val = 5 * val + *key;
389
  return(val % ntt->size);
390
}
391
392
/* Creates a single node in the tree */
393
394
struct ntt_node *ntt_node_create(const char *key) {
395
  char *node_key;
396
  struct ntt_node* node;
397
  
398
  node = (struct ntt_node *) malloc(sizeof(struct ntt_node));
399
  if (node == NULL) {
400
    return NULL;
401
  }
402
  if ((node_key = strdup(key)) == NULL) {
403
    free(node);
404
    return NULL;
405
  }
406
  node->key = node_key;
407
  node->timestamp = time(NULL);
408
  node->next = NULL;
409
  return(node);
410
}
411
412
/* Tree initializer */
413
414
struct ntt *ntt_create(long size) {
415
  long i = 0;
416
  struct ntt *ntt = (struct ntt *) malloc(sizeof(struct ntt));
417
  
418
  if (ntt == NULL)
419
    return NULL;
420
  while (ntt_prime_list[i] < size) { i++; }
421
  ntt->size  = ntt_prime_list[i];
422
  ntt->items = 0;
423
  ntt->tbl   = (struct ntt_node **) calloc(ntt->size, sizeof(struct ntt_node *));
424
  if (ntt->tbl == NULL) {
425
        free(ntt);
426
        return NULL;
427
  }
428
  return(ntt);
429
}
430
431
/* Find an object in the tree */
432
433
struct ntt_node *ntt_find(struct ntt *ntt, const char *key) {
434
  long hash_code;
435
  struct ntt_node *node;
436
  
437
  if (ntt == NULL) return NULL;
438
  
439
  hash_code = ntt_hashcode(ntt, key);
440
  node = ntt->tbl[hash_code];
441
  
442
  while (node) {
443
    if (!strcmp(key, node->key)) {
444
      return(node);
445
    }
446
    node = node->next;
447
  }
448
  return((struct ntt_node *)NULL);
449
}
450
451
/* Insert a node into the tree */
452
453
struct ntt_node *ntt_insert(struct ntt *ntt, const char *key, time_t timestamp) {
454
  long hash_code;
455
  struct ntt_node *parent;
456
  struct ntt_node *node;
457
  struct ntt_node *new_node = NULL;
458
  
459
  if (ntt == NULL) return NULL;
460
  
461
  hash_code = ntt_hashcode(ntt, key);
462
  parent	= NULL;
463
  node	= ntt->tbl[hash_code];
464
  
465
  crit_enter(mod_dosevasive_crit); /*Lock*/
466
467
  while (node != NULL) {
468
    if (strcmp(key, node->key) == 0) { 
469
      new_node = node;
470
      node = NULL;
471
    }
472
    
473
    if (new_node == NULL) {
474
      parent = node;
475
      node = node->next;
476
    }
477
  }
478
  
479
  if (new_node != NULL) {
480
    new_node->timestamp = timestamp;
481
    new_node->count = 0;
482
    crit_exit(mod_dosevasive_crit); /*Unlock*/
483
    return new_node; 
484
  }
485
  
486
  /* Create a new node */
487
  new_node = ntt_node_create(key);
488
  new_node->timestamp = timestamp;
489
  new_node->timestamp = 0;
490
  
491
  ntt->items++;
492
  
493
  /* Insert */
494
  if (parent) {  /* Existing parent */
495
    parent->next = new_node;
496
    crit_exit(mod_dosevasive_crit); /*Unlock*/
497
    return new_node;  /* Return the locked node */
498
  }
499
  
500
  /* No existing parent; add directly to hash table */
501
  ntt->tbl[hash_code] = new_node;
502
  crit_exit(mod_dosevasive_crit); /*Unlock*/
503
  return new_node;
504
}
505
506
/* Tree destructor */
507
508
int ntt_destroy(struct ntt *ntt) {
509
  struct ntt_node *node, *next;
510
  struct ntt_c c;
511
  
512
  if (ntt == NULL) return -1;
513
  
514
  node = c_ntt_first(ntt, &c);
515
  while(node != NULL) {
516
    next = c_ntt_next(ntt, &c);
517
    ntt_delete(ntt, node->key);
518
    node = next;
519
  }
520
  
521
  free(ntt->tbl);
522
  free(ntt);
523
  ntt = (struct ntt *) NULL;
524
  
525
  return 0;
526
}
527
528
/* Delete a single node in the tree */
529
530
int ntt_delete(struct ntt *ntt, const char *key) {
531
  long hash_code;
532
  struct ntt_node *parent = NULL;
533
  struct ntt_node *node;
534
  struct ntt_node *del_node = NULL;
535
  
536
  if (ntt == NULL) return -1;
537
  
538
  hash_code = ntt_hashcode(ntt, key);
539
  node        = ntt->tbl[hash_code];
540
  
541
  while (node != NULL) {
542
    if (strcmp(key, node->key) == 0) {
543
      del_node = node;
544
      node = NULL;
545
    }
546
    
547
    if (del_node == NULL) {
548
      parent = node;
549
      node = node->next;
550
    }
551
  }
552
  
553
  if (del_node != NULL) {
554
    
555
    if (parent) {
556
      parent->next = del_node->next;
557
    } else {
558
      ntt->tbl[hash_code] = del_node->next;
559
    }
560
    
561
    free(del_node->key);
562
    free(del_node);
563
    ntt->items--;
564
    
565
    return 0;
566
  }
567
  
568
  return -5;
569
}
570
571
/* Point cursor to first item in tree */
572
573
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c) {
574
  
575
  c->iter_index = 0;
576
  c->iter_next = (struct ntt_node *)NULL;
577
  return(c_ntt_next(ntt, c));
578
}
579
580
/* Point cursor to next iteration in tree */
581
582
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c) {
583
  long index;
584
  struct ntt_node *node = c->iter_next;
585
  
586
  if (ntt == NULL) return NULL;
587
  
588
  if (node) {
589
    if (node != NULL) {
590
      c->iter_next = node->next;
591
      return (node);
592
    }
593
  }
594
  
595
  if (! node) {
596
    while (c->iter_index < ntt->size) {
597
      index = c->iter_index++;
598
      
599
      if (ntt->tbl[index]) {
600
	c->iter_next = ntt->tbl[index]->next;
601
	return(ntt->tbl[index]);
602
      }
603
    }
604
  }
605
  return((struct ntt_node *)NULL);
606
}
607
608
/* END NTT (Named Pointer Tree) Functions */
(-)work/mod_evasive/test.pl (-18 lines)
Lines 1-18 Link Here
1
#!/usr/bin/perl
2
3
# test.pl: small script to test mod_dosevasive's effectiveness
4
5
use IO::Socket;
6
use strict;
7
8
for(0..100) {
9
  my($response);
10
  my($SOCKET) = new IO::Socket::INET( Proto   => "tcp",
11
                                      PeerAddr=> "127.0.0.1:80");
12
  if (! defined $SOCKET) { die $!; }
13
  print $SOCKET "GET /?$_ HTTP/1.0\n\n";
14
  $response = <$SOCKET>;
15
  print $response;
16
  close($SOCKET);
17
}
18
(-)mod_evasive/mod_evasive/Makefile (+35 lines)
Line 0 Link Here
1
# Created by: Xavier Beaudouin <kiwi@oav.net>
2
# $FreeBSD: head/www/mod_evasive/Makefile 336589 2013-12-15 22:11:20Z ohauer $
3
4
PORTNAME=	mod_evasive
5
PORTVERSION=	1.10.1
6
PORTREVISION=	1
7
CATEGORIES=	www security
8
MASTER_SITES=	http://www.zdziarski.com/blog/wp-content/uploads/2010/02/
9
DISTNAME=	mod_evasive_${PORTVERSION}
10
DIST_SUBDIR=	apache2
11
12
MAINTAINER=	kiwi@oav.net
13
COMMENT=	Apache module to try to protect the HTTP Server from DoS/DDoS attacks
14
15
LICENSE=	GPLv2
16
17
WRKSRC=		${WRKDIR}/${PORTNAME}
18
19
USE_APACHE=	22
20
AP_FAST_BUILD=	yes
21
AP_GENPLIST=	yes
22
MODULENAME=	${PORTNAME}20
23
24
PORTDOCS=	README test.pl
25
26
post-patch:
27
	@${REINPLACE_CMD} -e "s|/bin/mail|/usr/bin/mail|g" \
28
		${WRKSRC}/mod_evasive.c ${WRKSRC}/mod_evasive20.c \
29
		${WRKSRC}/mod_evasiveNSAPI.c
30
31
post-install:
32
	@${MKDIR} ${STAGEDIR}${DOCSDIR}
33
	@${INSTALL_DATA} ${PORTDOCS:S|^|${WRKSRC}/|} ${STAGEDIR}${DOCSDIR}
34
35
.include <bsd.port.mk>
(-)mod_evasive/mod_evasive/distinfo (+2 lines)
Line 0 Link Here
1
SHA256 (apache2/mod_evasive_1.10.1.tar.gz) = 07c45139aa313899484a900f0fc162b3e17eb4f60fe474d7f3dd6c9941e95667
2
SIZE (apache2/mod_evasive_1.10.1.tar.gz) = 20454
(-)mod_evasive/mod_evasive/pkg-descr (+30 lines)
Line 0 Link Here
1
mod_dosevasive is an evasive maneuvers module for Apache to provide evasive 
2
action in the event of an HTTP DoS or DDoS attack or brute force attack.
3
It is also designed to be a detection and network management tool, and can be
4
easily configured to talk to ipchains, firewalls, routers, and etcetera.
5
mod_dosevasive presently reports abuses via email and syslog facilities.
6
7
Detection is performed by creating an internal dynamic hash table of IP
8
Addresses and URIs, and denying any single IP address from any of the
9
following:
10
11
    * Requesting the same page more than a few times per second
12
    * Making more than 50 concurrent requests on the same child per second
13
    * Making any requests while temporarily blacklisted (on a blocking list) 
14
15
This method has worked well in both single-server script attacks as well as
16
distributed attacks, but just like other evasive tools, is only as useful to
17
the point of bandwidth and processor consumption (e.g. the amount of bandwidth
18
and processor required to receive/process/respond to invalid requests), which
19
is why it's a good idea to integrate this with your firewalls and routers for
20
maximum protection.
21
22
This module instantiates for each listener individually, and therefore has a
23
built-in cleanup mechanism and scaling capabilities. Because of this per-child
24
design, legitimate requests are never compromised (even from proxies and NAT
25
addresses) but only scripted attacks. Even a user repeatedly clicking on
26
'reload' should not be affected unless they do it maliciously. mod_dosevasive
27
is fully tweakable through the Apache configuration file, easy to incorporate
28
into your web server, and easy to use. 
29
30
WWW: http://www.zdziarski.com/blog/?page_id=442
(-)mod_evasive/work/mod_evasive/.cvsignore (+5 lines)
Line 0 Link Here
1
.libs
2
*.la
3
*.slo
4
*.o
5
*.so
(-)mod_evasive/work/mod_evasive/CHANGELOG (+72 lines)
Line 0 Link Here
1
2
Version 1.10.1
3
--------------
4
5
[20051008] jonz: Fixed IP Whitelisting in Apache 1.3 Version
6
7
[20051008] jonz: Corrected initialization to prevent dumping IP database
8
9
[20051008] jonz: Documentation and code cleaned up, renamed mod_evasive
10
11
Version 1.10.0
12
--------------
13
14
[20050117] jonz: Security fix: Tempdir configuration directive (race condition)
15
16
Version 1.9.0
17
-------------
18
19
[20031030] jonz: Added NSAPI/SunONE Support
20
21
[20031030] jonz: Added TEMP_HOME definition to change temporary file locations
22
23
Version 1.8.0
24
-------------
25
26
[20030901] jonz: Added support for IP Whitelisting
27
28
Version 1.7.1
29
-------------
30
31
[20030826] jonz: Minor bugfixes to Apache 2.0 module
32
33
[20030826] jonz: Corrections to object creation and cleanup
34
35
Version 1.7.0
36
-------------
37
38
[20030822] jonz: Support for Apache 2.0
39
40
Version 1.6.1
41
-------------
42
43
[20030811] jonz: Bugfix: free() of another static variable
44
 
45
Version 1.6.0
46
-------------
47
48
[20030806] jonz: Added syslog support
49
50
Version 1.5.1
51
-------------
52
53
[20030516] jonz: Bugfix: free() of a static variable
54
55
Version 1.5.0
56
-------------
57
58
[20030425] jonz: Added automated email notification 
59
60
[20030425] jonz: Added configurable system command
61
62
Version 1.4.0
63
-------------
64
65
[20021031] jonz: Added support fror httpd.conf directives
66
67
[20021031] jonz: Added --add-module support
68
69
Version 1.0.0
70
-------------
71
72
[20021015] jonz: Initial Release
(-)mod_evasive/work/mod_evasive/LICENSE (+146 lines)
Line 0 Link Here
1
GNU GENERAL PUBLIC LICENSE
2
Version 2, June 1991 
3
4
Copyright (C) 1989, 1991 Free Software Foundation, Inc.  
5
59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
6
7
Everyone is permitted to copy and distribute verbatim copies
8
of this license document, but changing it is not allowed.
9
10
Preamble
11
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. 
12
13
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. 
14
15
To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. 
16
17
For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. 
18
19
We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. 
20
21
Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. 
22
23
Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. 
24
25
The precise terms and conditions for copying, distribution and modification follow. 
26
27
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
28
0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". 
29
30
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 
31
32
1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. 
33
34
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 
35
36
2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: 
37
38
39
a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. 
40
41
b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. 
42
43
c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) 
44
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. 
45
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. 
46
47
In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 
48
49
3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: 
50
51
a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, 
52
53
b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, 
54
55
c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) 
56
The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. 
57
If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 
58
59
4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 
60
61
5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 
62
63
6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 
64
65
7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. 
66
67
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. 
68
69
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. 
70
71
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 
72
73
8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 
74
75
9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. 
76
77
Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 
78
79
10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. 
80
81
NO WARRANTY
82
83
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 
84
85
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 
86
87
88
END OF TERMS AND CONDITIONS
89
How to Apply These Terms to Your New Programs
90
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. 
91
92
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. 
93
94
one line to give the program's name and an idea of what it does.
95
Copyright (C) yyyy  name of author
96
97
This program is free software; you can redistribute it and/or
98
modify it under the terms of the GNU General Public License
99
as published by the Free Software Foundation; either version 2
100
of the License, or (at your option) any later version.
101
102
This program is distributed in the hope that it will be useful,
103
but WITHOUT ANY WARRANTY; without even the implied warranty of
104
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
105
GNU General Public License for more details.
106
107
You should have received a copy of the GNU General Public License
108
along with this program; if not, write to the Free Software
109
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
110
111
Also add information on how to contact you by electronic and paper mail. 
112
113
If the program is interactive, make it output a short notice like this when it starts in an interactive mode: 
114
115
Gnomovision version 69, Copyright (C) year name of author
116
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
117
type `show w'.  This is free software, and you are welcome
118
to redistribute it under certain conditions; type `show c' 
119
for details.
120
121
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. 
122
123
You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: 
124
125
Yoyodyne, Inc., hereby disclaims all copyright
126
interest in the program `Gnomovision'
127
(which makes passes at compilers) written 
128
by James Hacker.
129
130
signature of Ty Coon, 1 April 1989
131
Ty Coon, President of Vice
132
133
This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. 
134
135
136
--------------------------------------------------------------------------------
137
Return to GNU's home page. 
138
FSF & GNU inquiries & questions to gnu@gnu.org. Other ways to contact the FSF. 
139
140
Comments on these web pages to webmasters@www.gnu.org, send other questions to gnu@gnu.org. 
141
142
Copyright notice above.
143
Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA 
144
145
Updated: Last modified: Sun Jul 15 13:13:30 CEST 2001 
146
(-)mod_evasive/work/mod_evasive/Makefile.tmpl (+15 lines)
Line 0 Link Here
1
2
#Dependencies
3
4
$(OBJS) $(OBJS_PIC): Makefile
5
6
# DO NOT REMOVE
7
mod_evasive.o: mod_evasive.c $(INCDIR)/httpd.h \
8
 $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
9
 $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
10
 $(INCDIR)/ap_ctype.h $(INCDIR)/hsregex.h \
11
 $(INCDIR)/ap_alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \
12
 $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \
13
 $(INCDIR)/http_core.h $(INCDIR)/http_log.h \
14
 $(INCDIR)/http_main.h $(INCDIR)/http_protocol.h \
15
 $(INCDIR)/util_script.h
(-)mod_evasive/work/mod_evasive/README (+378 lines)
Line 0 Link Here
1
Apache Evasive Maneuvers Module
2
For Apache 1.3 and 2.0
3
Copyright (c) Deep Logic, Inc.
4
Version 1.10 [2005.0117]
5
6
LICENSE
7
8
This program is free software; you can redistribute it and/or
9
modify it under the terms of the GNU General Public License
10
as published by the Free Software Foundation; version 2
11
of the License.
12
13
This program is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
GNU General Public License for more details.
17
18
You should have received a copy of the GNU General Public License
19
along with this program; if not, write to the Free Software
20
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21
22
WHAT IS MOD_EVASIVE ?
23
24
mod_evasive is an evasive maneuvers module for Apache to provide evasive
25
action in the event of an HTTP DoS or DDoS attack or brute force attack.  It 
26
is also designed to be a detection tool, and can be easily configured to talk 
27
to ipchains, firewalls, routers, and etcetera.  
28
29
Detection is performed by creating an internal dynamic hash table of IP 
30
Addresses and URIs, and denying any single IP address from any of the following:
31
32
- Requesting the same page more than a few times per second
33
- Making more than 50 concurrent requests on the same child per second
34
- Making any requests while temporarily blacklisted (on a blocking list)
35
36
This method has worked well in both single-server script attacks as well 
37
as distributed attacks, but just like other evasive tools, is only as 
38
useful to the point of bandwidth and processor consumption (e.g. the
39
amount of bandwidth and processor required to receive/process/respond
40
to invalid requests), which is why it's a good idea to integrate this
41
with your firewalls and routers.
42
43
This module instantiates for each listener individually, and therefore has
44
a built-in cleanup mechanism and scaling capabilities.  Because of this,
45
legitimate requests are rarely ever compromised, only legitimate attacks.  Even
46
a user repeatedly clicking on 'reload' should not be affected unless they do
47
it maliciously.
48
49
Three different module sources have been provided:
50
51
Apache v1.3 API:	mod_evasive.c
52
Apache v2.0 API:	mod_evasive20.c
53
NSAPI (iPlanet):	mod_evasiveNSAPI.c *
54
55
NOTE: mod_evasiveNSAPI is a port submitted by Reine Persson <reiper@rsv.se>
56
      and is not officially supported as part of the mod_evasive project.
57
58
HOW IT WORKS
59
60
A web hit request comes in. The following steps take place:
61
62
- The IP address of the requestor is looked up on the temporary blacklist
63
- The IP address of the requestor and the URI are both hashed into a "key".  
64
  A lookup is performed in the listener's internal hash table to determine 
65
  if the same host has requested this page more than once within the past 
66
  1 second.  
67
- The IP address of the requestor is hashed into a "key".
68
  A lookup is performed in the listerner's internal hash table to determine
69
  if the same host has requested more than 50 objects within the past
70
  second (from the same child).
71
72
If any of the above are true, a 403 response is sent.  This conserves
73
bandwidth and system resources in the event of a DoS attack.  Additionally,
74
a system command and/or an email notification can also be triggered to block
75
all the originating addresses of a DDoS attack. 
76
77
Once a single 403 incident occurs, mod_evasive now blocks the entire IP 
78
address for a period of 10 seconds (configurable).  If the host requests a 
79
page within this period, it is forced to wait even longer.  Since this is 
80
triggered from requesting the same URL multiple times per second, this 
81
again does not affect legitimate users.
82
83
The blacklist can/should be configured to talk to your network's firewalls 
84
and/or routers to push the attack out to the front lines, but this is not 
85
required.
86
87
mod_evasive also performs syslog reporting using daemon.alert.  Messages
88
will look like this:
89
90
Aug  6 17:41:49 elijah mod_evasive[23184]: [ID 801097 daemon.alert] Blacklisting address x.x.x.x: possible attack.
91
92
WHAT IS THIS TOOL USEFUL FOR?
93
94
This tool is *excellent* at fending off request-based DoS attacks or scripted
95
attacks, and brute force attacks. When integrated with firewalls or IP filters,
96
mod_evasive can stand up to even large attacks. Its features will prevent you 
97
from wasting bandwidth or having a few thousand CGI scripts running as a 
98
result of an attack.  
99
100
If you do not have an infrastructure capable of fending off any other types
101
of DoS attacks, chances are this tool will only help you to the point of
102
your total bandwidth or server capacity for sending 403's.  Without a solid
103
infrastructure and address filtering tool in place, a heavy distributed DoS 
104
will most likely still take you offline.  
105
106
HOW TO INSTALL
107
108
APACHE v1.3
109
-----------
110
111
Without DSO Support:
112
113
1. Extract this archive into src/modules in the Apache source tree
114
115
2. Run ./configure --add-module=src/modules/evasive/mod_evasive.c
116
117
3. make, install
118
119
4. Restart Apache 
120
121
With DSO Support, Ensim, or CPanel:
122
123
1. $APACHE_ROOT/bin/apxs -iac mod_evasive.c
124
125
2. Restart Apache
126
127
APACHE v2.0
128
-----------
129
130
1. Extract this archive
131
132
2. Run $APACHE_ROOT/bin/apxs -i -a -c mod_evasive20.c
133
134
3. The module will be built and installed into $APACHE_ROOT/modules, and loaded into your httpd.conf
135
136
4. Restart Apache
137
138
NSAPI
139
SunONE (iPlanet,netscape) Installation
140
--------------------------------------
141
142
Tested on:
143
iPlanet 4.1sp12
144
iPlanet 6.0sp5
145
146
Edit compile script for your environment and compile mod_evasiveNSAPI.c
147
to a shared library.
148
149
CONFIGURATION
150
151
mod_evasive has default options configured, but you may also add the
152
following block to your httpd.conf:
153
154
APACHE v1.3
155
-----------
156
157
<IfModule mod_evasive.c>
158
    DOSHashTableSize    3097
159
    DOSPageCount        2
160
    DOSSiteCount        50
161
    DOSPageInterval     1
162
    DOSSiteInterval     1
163
    DOSBlockingPeriod   10
164
</IfModule>
165
166
APACHE v2.0
167
-----------
168
<IfModule mod_evasive20.c>
169
    DOSHashTableSize    3097
170
    DOSPageCount        2
171
    DOSSiteCount        50
172
    DOSPageInterval     1
173
    DOSSiteInterval     1
174
    DOSBlockingPeriod   10
175
</IfModule>
176
177
Optionally you can also add the following directives:
178
179
    DOSEmailNotify	you@yourdomain.com
180
    DOSSystemCommand	"su - someuser -c '/sbin/... %s ...'"
181
    DOSLogDir		"/var/lock/mod_evasive"
182
183
You will also need to add this line if you are building with dynamic support:
184
185
APACHE v1.3
186
-----------
187
188
AddModule	mod_evasive.c
189
190
APACHE v2.0
191
-----------
192
193
LoadModule evasive20_module modules/mod_evasive20.so
194
195
(This line is already added to your configuration by apxs)
196
197
NSAPI
198
SunONE (iPlanet,Netscape) Configuration
199
--------------------------------------
200
                                                                                
201
Configure iPlanet 4.1
202
---------------------
203
204
Edit obj.conf:
205
                                                                                
206
Init fn="load-modules" funcs="mod_evasive_init,mod_evasive_check" shlib="/opt/ns-4.1/plugins/lib/mod_evasive.sl"
207
                                                                                
208
Init fn="mod_evasive_init" DOSPageCount=2 DOSSiteCount=50 DOSPageInterval=1 DOSSiteInterval=1 DOSBlockingPeriod=10 DOSWhitelist="10.60.0.7,10.65.0.10"
209
                                                                                
210
In the default object:
211
PathCheck fn=mod_evasive_check
212
                                                                                
213
Or an own object
214
<Object name="evasive" ppath="/DoSProtectedArea*">
215
NameTrans fn=mod_evasive_check
216
</Object>
217
                                                                                
218
                                                                                
219
Configure iPlanet 6.0
220
---------------------
221
                                                                                
222
Edit magnus.conf:
223
                                                                                
224
Init fn="load-modules" funcs="mod_evasive_init,mod_evasive_check" shlib="/opt/iplanet-6.0/plugins/lib/mod_evasive.sl"
225
                                                                                
226
Init fn="mod_evasive_init" DOSWhitelist="10.60.0.7,10.65.0.10"
227
                                                                                
228
Edit obj.conf:
229
In the default object:
230
PathCheck fn=mod_evasive_check
231
                                                                                
232
Or an own object
233
<Object name="evasive" ppath="/DoSProtectedArea*">
234
NameTrans fn=mod_evasive_check
235
</Object>
236
237
DOSHashTableSize
238
----------------
239
240
The hash table size defines the number of top-level nodes for each child's 
241
hash table.  Increasing this number will provide faster performance by 
242
decreasing the number of iterations required to get to the record, but 
243
consume more memory for table space.  You should increase this if you have
244
a busy web server.  The value you specify will automatically be tiered up to 
245
the next prime number in the primes list (see mod_evasive.c for a list 
246
of primes used).
247
248
DOSPageCount
249
------------
250
251
This is the threshhold for the number of requests for the same page (or URI)
252
per page interval.  Once the threshhold for that interval has been exceeded,
253
the IP address of the client will be added to the blocking list.
254
 
255
DOSSiteCount
256
------------
257
258
This is the threshhold for the total number of requests for any object by
259
the same client on the same listener per site interval.  Once the threshhold 
260
for that interval has been exceeded, the IP address of the client will be added
261
to the blocking list.
262
263
DOSPageInterval
264
---------------
265
266
The interval for the page count threshhold; defaults to 1 second intervals.
267
268
DOSSiteInterval
269
---------------
270
271
The interval for the site count threshhold; defaults to 1 second intervals.
272
273
DOSBlockingPeriod
274
-----------------
275
276
The blocking period is the amount of time (in seconds) that a client will be
277
blocked for if they are added to the blocking list.  During this time, all
278
subsequent requests from the client will result in a 403 (Forbidden) and
279
the timer being reset (e.g. another 10 seconds).  Since the timer is reset
280
for every subsequent request, it is not necessary to have a long blocking
281
period; in the event of a DoS attack, this timer will keep getting reset. 
282
283
DOSEmailNotify
284
--------------
285
286
If this value is set, an email will be sent to the address specified
287
whenever an IP address becomes blacklisted.  A locking mechanism using /tmp
288
prevents continuous emails from being sent.
289
290
NOTE: Be sure MAILER is set correctly in mod_evasive.c 
291
      (or mod_evasive20.c).  The default is "/bin/mail -t %s" where %s is 
292
      used to denote the destination email address set in the configuration.  
293
      If you are running on linux or some other operating system with a 
294
      different type of mailer, you'll need to change this.
295
296
DOSSystemCommand
297
----------------
298
299
If this value is set, the system command specified will be executed
300
whenever an IP address becomes blacklisted.  This is designed to enable
301
system calls to ip filter or other tools.  A locking mechanism using /tmp
302
prevents continuous system calls.  Use %s to denote the IP address of the
303
blacklisted IP.
304
305
DOSLogDir
306
---------
307
308
Choose an alternative temp directory
309
310
By default "/tmp" will be used for locking mechanism, which opens some 
311
security issues if your system is open to shell users.
312
313
  	http://security.lss.hr/index.php?page=details&ID=LSS-2005-01-01
314
315
In the event you have nonprivileged shell users, you'll want to create a
316
directory writable only to the user Apache is running as (usually root),
317
then set this in your httpd.conf.
318
319
WHITELISTING IP ADDRESSES
320
321
IP addresses of trusted clients can be whitelisted to insure they are never 
322
denied.  The purpose of whitelisting is to protect software, scripts, local 
323
searchbots, or other automated tools from being denied for requesting large 
324
amounts of data from the server.  Whitelisting should *not* be used to add 
325
customer lists or anything of the sort, as this will open the server to abuse.
326
This module is very difficult to trigger without performing some type of 
327
malicious attack, and for that reason it is more appropriate to allow the 
328
module to decide on its own whether or not an individual customer should be 
329
blocked.
330
331
To whitelist an address (or range) add an entry to the Apache configuration 
332
in the following fashion:
333
334
DOSWhitelist	127.0.0.1
335
DOSWhitelist	127.0.0.*
336
337
Wildcards can be used on up to the last 3 octets if necessary.  Multiple
338
DOSWhitelist commands may be used in the configuration.
339
340
TWEAKING APACHE
341
342
The keep-alive settings for your children should be reasonable enough to 
343
keep each child up long enough to resist a DOS attack (or at least part of 
344
one).  Remember, it is the child processes that maintain their own internal
345
IP address tables, and so when one exits, so does all of the IP information it
346
had. For every child that exits, another 5-10 copies of the page may get 
347
through before putting the attacker back into '403 Land'.  With this said, 
348
you should have a very high MaxRequestsPerChild, but not unlimited as this
349
will prevent cleanup.
350
351
You'll want to have a MaxRequestsPerChild set to a non-zero value, as
352
DosEvasive cleans up its internal hashes only on exit.  The default
353
MaxRequestsPerChild is usually 10000.  This should suffice in only allowing
354
a few requests per 10000 per child through in the event of an attack (although
355
if you use DOSSystemCommand to firewall the IP address, a hole will no
356
longer be open in between child cycles).
357
358
TESTING
359
360
Want to make sure it's working? Run test.pl, and view the response codes.
361
It's best to run it several times on the same machine as the web server until
362
you get 403 Forbidden messages. Some larger servers with high child counts 
363
may require more of a beating than smaller servers before blacklisting
364
addresses. 
365
366
Please don't use this script to DoS others without their permission.
367
368
KNOWN BUGS
369
370
- This module appears to conflict with the Microsoft Frontpage Extensions.
371
  Frontpage sucks anyway, so if you're using Frontpage I assume you're asking
372
  for problems, and not really interested in conserving server resources anyway.
373
374
FEEDBACK 
375
376
Please email me with questions, constructive comments, or feedback:
377
  jonathan@nuclearelephant.com
378
(-)mod_evasive/work/mod_evasive/mod_evasive.c (+709 lines)
Line 0 Link Here
1
/* $Id: mod_evasive.c,v 1.3 2005/10/08 19:17:14 jonz Exp $ */
2
3
/*
4
mod_evasive for Apache 1.3
5
Copyright (c) by Jonathan A. Zdziarski
6
7
LICENSE
8
9
This program is free software; you can redistribute it and/or
10
modify it under the terms of the GNU General Public License
11
as published by the Free Software Foundation; version 2
12
of the License.
13
14
This program is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
GNU General Public License for more details.
18
19
You should have received a copy of the GNU General Public License
20
along with this program; if not, write to the Free Software
21
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
                                                                                
23
*/
24
25
#include <sys/types.h>
26
#include <sys/socket.h>
27
#include <sys/stat.h>
28
#include <netinet/in.h>
29
#include <arpa/inet.h>
30
#include <string.h>
31
#include <stdlib.h>
32
#include <sys/types.h>
33
#include <time.h>
34
#include <syslog.h>
35
#include <errno.h>
36
37
#include "httpd.h"
38
#include "http_core.h"
39
#include "http_config.h"
40
#include "http_log.h"
41
#include "http_request.h"
42
43
module MODULE_VAR_EXPORT evasive_module;
44
45
/* BEGIN DoS Evasive Maneuvers Definitions */
46
47
#define MAILER	"/bin/mail -t %s"
48
#define  LOG( A, ... ) { openlog("mod_evasive", LOG_PID, LOG_DAEMON); syslog( A, __VA_ARGS__ ); closelog(); }
49
50
#define DEFAULT_HASH_TBL_SIZE   3079ul  // Default hash table size
51
#define DEFAULT_PAGE_COUNT      2       // Default max page hit count/interval
52
#define DEFAULT_SITE_COUNT      50      // Default max site hit count/interval
53
#define DEFAULT_PAGE_INTERVAL   1       // Default 1 second page interval
54
#define DEFAULT_SITE_INTERVAL   1       // Default 1 second site interval
55
#define DEFAULT_BLOCKING_PERIOD 10      // Default block time (Seconds)
56
#define DEFAULT_LOG_DIR		"/tmp"
57
58
/* END DoS Evasive Maneuvers Definitions */
59
60
/* BEGIN NTT (Named Timestamp Tree) Headers */
61
62
enum { ntt_num_primes = 28 };
63
64
/* ntt root tree */
65
struct ntt {
66
    long size;
67
    long items;
68
    struct ntt_node **tbl;
69
};
70
71
/* ntt node (entry in the ntt root tree) */
72
struct ntt_node {
73
    char *key;
74
    time_t timestamp;
75
    long count;
76
    struct ntt_node *next;
77
};
78
79
/* ntt cursor */
80
struct ntt_c {
81
  long iter_index;
82
  struct ntt_node *iter_next;
83
};
84
85
struct ntt *ntt_create(long size);
86
struct ntt_node	*ntt_find(struct ntt *ntt, const char *key);
87
struct ntt_node	*ntt_insert(struct ntt *ntt, const char *key, time_t timestamp);
88
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c);
89
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c);
90
int ntt_destroy(struct ntt *ntt);
91
int ntt_delete(struct ntt *ntt, const char *key);
92
long ntt_hashcode(struct ntt *ntt, const char *key);
93
94
/* END NTT (Named Timestamp Tree) Headers */
95
96
97
/* BEGIN DoS Evasive Maneuvers Globals */
98
99
struct ntt *hit_list;	// Our dynamic hash table
100
struct ntt *white_list = NULL; // White list table
101
102
static unsigned long hash_table_size = DEFAULT_HASH_TBL_SIZE;
103
static int page_count      = DEFAULT_PAGE_COUNT;
104
static int page_interval   = DEFAULT_PAGE_INTERVAL;
105
static int site_count      = DEFAULT_SITE_COUNT;
106
static int site_interval   = DEFAULT_SITE_INTERVAL;
107
static int blocking_period = DEFAULT_BLOCKING_PERIOD;
108
static char *log_dir       = NULL;
109
static char *email_notify  = NULL;
110
static char *sys_command   = NULL;
111
int is_whitelisted(const char *ip);
112
static const char *whitelist(cmd_parms *cmd, void *mconfig, char *ip);
113
114
/* END DoS Evasive Maneuvers Globals */
115
116
static void evasive_child_init(server_rec *s, pool *p)
117
{
118
    hit_list   = ntt_create(hash_table_size);
119
}
120
121
static int check_access(request_rec *r) 
122
{
123
    int ret = OK;
124
125
    /* BEGIN Evasive Maneuvers Code */
126
127
    if (r->prev == NULL && r->main == NULL && hit_list != NULL) {
128
      unsigned long address = r->connection->remote_addr.sin_addr.s_addr;
129
      char *text_add = inet_ntoa(r->connection->remote_addr.sin_addr);
130
      char hash_key[2048];
131
      struct ntt_node *n;
132
      time_t t = time(NULL);
133
134
      /* Check whitelist */
135
       
136
      if (is_whitelisted(text_add))
137
        return OK;
138
139
      /* First see if the IP itself is on "hold" */
140
      snprintf(hash_key, 2048, "%ld", address);
141
      n = ntt_find(hit_list, hash_key);
142
143
      if (n != NULL && t-n->timestamp<blocking_period) {
144
 
145
        /* If the IP is on "hold", make it wait longer in 403 land */
146
        ret = FORBIDDEN;
147
        n->timestamp = time(NULL);
148
149
      /* Not on hold, check hit stats */
150
      } else {
151
152
        /* Has URI been hit too much? */
153
        snprintf(hash_key, 2048, "%ld_%s", address, r->uri);
154
        n = ntt_find(hit_list, hash_key);
155
        if (n != NULL) {
156
157
          /* If URI is being hit too much, add to "hold" list and 403 */
158
          if (t-n->timestamp<page_interval && n->count>=page_count) {
159
            ret = FORBIDDEN;
160
            snprintf(hash_key, 2048, "%ld", address);
161
            ntt_insert(hit_list, hash_key, time(NULL));
162
          } else {
163
164
            /* Reset our hit count list as necessary */
165
            if (t-n->timestamp>=page_interval) {
166
              n->count=0;
167
            }
168
          }
169
          n->timestamp = t;
170
          n->count++;
171
        } else {
172
          ntt_insert(hit_list, hash_key, t);
173
        }
174
175
        /* Has site been hit too much? */
176
        snprintf(hash_key, 2048, "%ld_SITE", address);
177
        n = ntt_find(hit_list, hash_key);
178
        if (n != NULL) {
179
180
          /* If site is being hit too much, add to "hold" list and 403 */
181
          if (t-n->timestamp<site_interval && n->count>=site_count) {
182
            ret = FORBIDDEN;
183
            snprintf(hash_key, 2048, "%ld", address);
184
            ntt_insert(hit_list, hash_key, time(NULL));
185
          } else {
186
187
            /* Reset our hit count list as necessary */
188
            if (t-n->timestamp>=site_interval) {
189
              n->count=0;
190
            }
191
          }
192
          n->timestamp = t;
193
          n->count++;
194
        } else {
195
          ntt_insert(hit_list, hash_key, t);
196
        }
197
      }
198
199
      /* Perform email notification and system functions */
200
      if (ret == FORBIDDEN) {
201
        char filename[1024];
202
        struct stat s;
203
        FILE *file;
204
205
        snprintf(filename, sizeof(filename), "%s/dos-%s", log_dir != NULL ? log_dir : DEFAULT_LOG_DIR, text_add);
206
        if (stat(filename, &s)) {
207
          file = fopen(filename, "w");
208
          if (file != NULL) {
209
            fprintf(file, "%ld\n", getpid());
210
            fclose(file);
211
212
            LOG(LOG_ALERT, "Blacklisting address %s: possible attack.", text_add)
213
            if (email_notify != NULL) {
214
              snprintf(filename, sizeof(filename), MAILER, email_notify);
215
              file = popen(filename, "w");
216
              if (file != NULL) {
217
                fprintf(file, "To: %s\n", email_notify);
218
                fprintf(file, "Subject: HTTP BLACKLIST %s\n\n", text_add);
219
                fprintf(file, "mod_evasive HTTP Blacklisted %s\n", text_add);
220
                pclose(file);
221
              }
222
            }
223
224
            if (sys_command != NULL) {
225
              snprintf(filename, sizeof(filename), sys_command, text_add);
226
              system(filename);
227
            }
228
 
229
          } else {
230
		LOG(LOG_ALERT, "Couldn't open logfile %s: %s",filename, strerror(errno));
231
	  }
232
233
        } /* if (temp file does not exist) */
234
235
      } /* if (ret == FORBIDDEN) */
236
237
    } /* if (r->prev == NULL && r->main == NULL && hit_list != NULL) */
238
239
    /* END Evasive Maneuvers Code */
240
241
    if (ret == FORBIDDEN
242
	&& (ap_satisfies(r) != SATISFY_ANY || !ap_some_auth_required(r))) {
243
	ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
244
		  "client denied by server configuration: %s",
245
		  r->filename);
246
    }
247
248
    return ret;
249
}
250
251
static void evasive_child_exit(server_rec *s, pool *p) 
252
{
253
    ntt_destroy(hit_list);
254
    free(email_notify);
255
    free(sys_command);
256
}
257
258
259
/* BEGIN NTT (Named Timestamp Tree) Functions */
260
261
static unsigned long ntt_prime_list[ntt_num_primes] = 
262
{
263
    53ul,         97ul,         193ul,       389ul,       769ul,
264
    1543ul,       3079ul,       6151ul,      12289ul,     24593ul,
265
    49157ul,      98317ul,      196613ul,    393241ul,    786433ul,
266
    1572869ul,    3145739ul,    6291469ul,   12582917ul,  25165843ul,
267
    50331653ul,   100663319ul,  201326611ul, 402653189ul, 805306457ul,
268
    1610612741ul, 3221225473ul, 4294967291ul
269
};
270
271
272
/* Find the numeric position in the hash table based on key and modulus */
273
274
long ntt_hashcode(struct ntt *ntt, const char *key) {
275
    unsigned long val = 0;
276
    for (; *key; ++key) val = 5 * val + *key;
277
    return(val % ntt->size);
278
}
279
280
/* Creates a single node in the tree */
281
282
struct ntt_node *ntt_node_create(const char *key) {
283
    char *node_key;
284
    struct ntt_node* node;
285
286
    node = (struct ntt_node *) malloc(sizeof(struct ntt_node));
287
    if (node == NULL) {
288
	return NULL;
289
    }
290
    if ((node_key = strdup(key)) == NULL) {
291
        free(node);
292
	return NULL;
293
    }
294
    node->key = node_key;
295
    node->timestamp = time(NULL);
296
    node->next = NULL;
297
    return(node);
298
}
299
300
/* Tree initializer */
301
302
struct ntt *ntt_create(long size) {
303
    long i = 0;
304
    struct ntt *ntt = (struct ntt *) malloc(sizeof(struct ntt));
305
306
    if (ntt == NULL)
307
        return NULL;
308
    while (ntt_prime_list[i] < size) { i++; }
309
    ntt->size  = ntt_prime_list[i];
310
    ntt->items = 0;
311
    ntt->tbl   = (struct ntt_node **) calloc(ntt->size, sizeof(struct ntt_node *));
312
    if (ntt->tbl == NULL) {
313
        free(ntt);
314
        return NULL;
315
    }
316
    return(ntt);
317
}
318
319
/* Find an object in the tree */
320
321
struct ntt_node *ntt_find(struct ntt *ntt, const char *key) {
322
    long hash_code;
323
    struct ntt_node *node;
324
325
    if (ntt == NULL) return NULL;
326
327
    hash_code = ntt_hashcode(ntt, key);
328
    node = ntt->tbl[hash_code];
329
330
    while (node) {
331
        if (!strcmp(key, node->key)) {
332
            return(node);
333
        }
334
        node = node->next;
335
    }
336
    return((struct ntt_node *)NULL);
337
}
338
339
/* Insert a node into the tree */
340
341
struct ntt_node *ntt_insert(struct ntt *ntt, const char *key, time_t timestamp) {
342
    long hash_code;
343
    struct ntt_node *parent;
344
    struct ntt_node *node;
345
    struct ntt_node *new_node = NULL;
346
347
    if (ntt == NULL) return NULL;
348
349
    hash_code = ntt_hashcode(ntt, key);
350
    parent	= NULL;
351
    node	= ntt->tbl[hash_code];
352
353
    while (node != NULL) {
354
        if (strcmp(key, node->key) == 0) { 
355
            new_node = node;
356
            node = NULL;
357
        }
358
359
	if (new_node == NULL) {
360
          parent = node;
361
          node = node->next;
362
        }
363
    }
364
365
    if (new_node != NULL) {
366
        new_node->timestamp = timestamp;
367
        new_node->count = 0;
368
        return new_node; 
369
    }
370
371
    /* Create a new node */
372
    new_node = ntt_node_create(key);
373
    new_node->timestamp = timestamp;
374
    new_node->timestamp = 0;
375
376
    ntt->items++;
377
378
    /* Insert */
379
    if (parent) {  /* Existing parent */
380
	parent->next = new_node;
381
        return new_node;  /* Return the locked node */
382
    }
383
384
    /* No existing parent; add directly to hash table */
385
    ntt->tbl[hash_code] = new_node;
386
    return new_node;
387
}
388
389
/* Tree destructor */
390
391
int ntt_destroy(struct ntt *ntt) {
392
    struct ntt_node *node, *next;
393
    struct ntt_c c;
394
395
    if (ntt == NULL) return -1;
396
397
    node = c_ntt_first(ntt, &c);
398
    while(node != NULL) {
399
        next = c_ntt_next(ntt, &c);
400
        ntt_delete(ntt, node->key);
401
        node = next;
402
    }
403
404
    free(ntt->tbl);
405
    free(ntt);
406
    ntt = (struct ntt *) NULL;
407
408
    return 0;
409
}
410
411
/* Delete a single node in the tree */
412
413
int ntt_delete(struct ntt *ntt, const char *key) {
414
    long hash_code;
415
    struct ntt_node *parent = NULL;
416
    struct ntt_node *node;
417
    struct ntt_node *del_node = NULL;
418
419
    if (ntt == NULL) return -1;
420
421
    hash_code = ntt_hashcode(ntt, key);
422
    node        = ntt->tbl[hash_code];
423
424
    while (node != NULL) {
425
        if (strcmp(key, node->key) == 0) {
426
            del_node = node;
427
            node = NULL;
428
        }
429
430
        if (del_node == NULL) {
431
          parent = node;
432
          node = node->next;
433
        }
434
    }
435
436
    if (del_node != NULL) {
437
438
        if (parent) {
439
            parent->next = del_node->next;
440
        } else {
441
            ntt->tbl[hash_code] = del_node->next;
442
        }
443
444
        free(del_node->key);
445
        free(del_node);
446
        ntt->items--;
447
448
        return 0;
449
    }
450
451
    return -5;
452
}
453
454
/* Point cursor to first item in tree */
455
456
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c) {
457
458
    c->iter_index = 0;
459
    c->iter_next = (struct ntt_node *)NULL;
460
    return(c_ntt_next(ntt, c));
461
}
462
463
/* Point cursor to next iteration in tree */
464
465
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c) {
466
    long index;
467
    struct ntt_node *node = c->iter_next;
468
469
    if (ntt == NULL) return NULL;
470
471
    if (node) {
472
        if (node != NULL) {
473
            c->iter_next = node->next;
474
            return (node);
475
        }
476
    }
477
478
    if (! node) {
479
        while (c->iter_index < ntt->size) {
480
            index = c->iter_index++;
481
482
            if (ntt->tbl[index]) {
483
                c->iter_next = ntt->tbl[index]->next;
484
                return(ntt->tbl[index]);
485
            }
486
        }
487
    }
488
    return((struct ntt_node *)NULL);
489
}
490
491
/* END NTT (Named Pointer Tree) Functions */
492
493
/* BEGIN Configuration Functions */
494
495
static const char *
496
get_hash_tbl_size(cmd_parms *cmd, void *dconfig, char *value) {
497
    long n = strtol(value, NULL, 0);
498
499
    if (n<=0) 
500
        hash_table_size = DEFAULT_HASH_TBL_SIZE;
501
    else 
502
        hash_table_size = n;
503
504
    return NULL;
505
}
506
507
static const char *
508
get_page_count(cmd_parms *cmd, void *dconfig, char *value) {
509
    long n = strtol(value, NULL, 0);
510
    if (n<=0) 
511
        page_count = DEFAULT_PAGE_COUNT;
512
    else
513
        page_count = n;
514
515
    return NULL;
516
}
517
518
static const char *
519
get_site_count(cmd_parms *cmd, void *dconfig, char *value) {
520
    long n = strtol(value, NULL, 0);
521
    if (n<=0) 
522
        site_count = DEFAULT_SITE_COUNT;
523
    else
524
        site_count = n;
525
526
    return NULL;
527
}
528
529
static const char *
530
get_page_interval(cmd_parms *cmd, void *dconfig, char *value) {
531
    long n = strtol(value, NULL, 0);
532
    if (n<=0) 
533
        page_interval = DEFAULT_PAGE_INTERVAL;
534
    else 
535
        page_interval = n;
536
537
    return NULL;
538
}
539
540
static const char *
541
get_site_interval(cmd_parms *cmd, void *dconfig, char *value) {
542
    long n = strtol(value, NULL, 0);
543
    if (n<=0) 
544
        site_interval = DEFAULT_SITE_INTERVAL;
545
    else
546
        site_interval = n;
547
548
  return NULL;
549
}
550
551
static const char *
552
get_blocking_period(cmd_parms *cmd, void *dconfig, char *value) {
553
    long n = strtol(value, NULL, 0);
554
    if (n<=0) 
555
        blocking_period = DEFAULT_BLOCKING_PERIOD;
556
    else 
557
        blocking_period = n;
558
559
    return NULL;
560
}
561
562
static const char *
563
get_log_dir(cmd_parms *cmd, void *dconfig, char *value) {
564
    if (value != NULL && value[0] != 0) {
565
        if (log_dir != NULL)
566
            free(log_dir);
567
        log_dir = strdup(value);
568
    }
569
570
    return NULL;
571
}
572
573
static const char *
574
get_email_notify(cmd_parms *cmd, void *dconfig, char *value) {
575
    if (value != NULL && value[0] != 0) {
576
        if (email_notify != NULL)
577
            free(email_notify);
578
        email_notify = strdup(value);
579
    }
580
581
    return NULL;
582
}
583
584
static const char *
585
get_sys_command(cmd_parms *cmd, void *dconfig, char *value) {
586
    if (value != NULL && value[0] != 0) {
587
        if (sys_command != NULL)
588
            free(sys_command);
589
        sys_command = strdup(value);
590
    }
591
 
592
    return NULL;
593
} 
594
595
static const char *whitelist(cmd_parms *cmd, void *mconfig, char *ip) {
596
    char entry[128];
597
598
    if (white_list == NULL) 
599
        white_list = ntt_create(53ul);
600
    snprintf(entry, sizeof(entry), "%s", ip);
601
    ntt_insert(white_list, entry, time(NULL));
602
603
    return NULL;
604
}
605
606
/* END Configuration Functions */
607
608
int is_whitelisted(const char *ip) {
609
    char hashkey[128];
610
    char octet[4][4];
611
    char *dip;
612
    char *oct;
613
    int i = 0;
614
                                                                                
615
    memset(octet, 0, 16);
616
    dip = strdup(ip);
617
    if (dip == NULL)
618
        return 0;
619
                                                                                
620
    oct = strtok(dip, ".");
621
    while(oct != NULL && i<4) {
622
        if (strlen(oct)<=3)
623
          strcpy(octet[i], oct);
624
        i++;
625
        oct = strtok(NULL, ".");
626
    }
627
    free(dip);
628
                                                                                
629
    /* Exact Match */
630
    snprintf(hashkey, sizeof(hashkey), "%s", ip);
631
    if (ntt_find(white_list, hashkey)!=NULL)
632
        return 1;
633
                                                                                
634
    /* IPv4 Wildcards */
635
    snprintf(hashkey, sizeof(hashkey), "%s.*.*.*", octet[0]);
636
    if (ntt_find(white_list, hashkey)!=NULL)
637
        return 1;
638
                                                                                
639
    snprintf(hashkey, sizeof(hashkey), "%s.%s.*.*", 
640
             octet[0], octet[1]);
641
    if (ntt_find(white_list, hashkey)!=NULL)
642
        return 1;
643
644
    snprintf(hashkey, sizeof(hashkey), "%s.%s.%s.*", 
645
             octet[0], octet[1], octet[2]);
646
    if (ntt_find(white_list, hashkey)!=NULL)
647
        return 1;
648
649
    /* No match */
650
    return 0;
651
}
652
653
static command_rec command_table[] = {
654
655
        { "DOSWhitelist", whitelist, NULL, RSRC_CONF, ITERATE,
656
        "Whitelist an IP or Wildcard. "},
657
658
	{ "DOSHashTableSize", get_hash_tbl_size, NULL, RSRC_CONF, TAKE1,
659
	"Set size of hash table. " },
660
661
	{ "DOSPageCount", get_page_count, NULL, RSRC_CONF, TAKE1,
662
	"Set maximum page hit count per interval. " },
663
664
	{ "DOSSiteCount", get_site_count, NULL, RSRC_CONF, TAKE1,
665
	"Set maximum site hit count per interval. " },
666
667
	{ "DOSPageInterval", get_page_interval, NULL, RSRC_CONF, TAKE1,
668
	"Set page interval. " },
669
670
	{ "DOSSiteInterval", get_site_interval, NULL, RSRC_CONF, TAKE1,
671
	"Set site interval. " }, 
672
673
	{ "DOSLogDir", get_log_dir, NULL, RSRC_CONF, TAKE1,
674
        "Set log dir. "},
675
676
	{ "DOSEmailNotify", get_email_notify, NULL, RSRC_CONF, TAKE1,
677
        "Set email notification. "},
678
679
	{ "DOSSystemCommand", get_sys_command, NULL, RSRC_CONF, TAKE1,
680
        "Set system command. "},
681
682
        { "DOSBlockingPeriod", get_blocking_period, NULL, RSRC_CONF, TAKE1,
683
        "Set blocking period for detected DoS IPs. "},
684
685
	{ NULL }
686
};
687
688
module MODULE_VAR_EXPORT evasive_module = {
689
    STANDARD_MODULE_STUFF,
690
    NULL,                              /* initializer */
691
    NULL,                              /* dir config creator */
692
    NULL,                              /* dir config merger */
693
    NULL,                              /* server config creator */
694
    NULL,                              /* server config merger */
695
    command_table,                     /* command table */
696
    NULL,                              /* handlers */
697
    NULL,                              /* filename translation */
698
    NULL,                              /* check_user_id */
699
    NULL,                              /* check auth */
700
    check_access,                      /* check access */
701
    NULL,                              /* type_checker */
702
    NULL,                              /* fixups */
703
    NULL,                              /* logger */
704
    NULL,                              /* header parser */
705
    evasive_child_init,                /* child_init */
706
    evasive_child_exit,                /* child_exit */
707
    NULL                               /* post read-request */
708
};
709
(-)mod_evasive/work/mod_evasive/mod_evasive20.c (+699 lines)
Line 0 Link Here
1
/*
2
mod_evasive for Apache 2
3
Copyright (c) by Jonathan A. Zdziarski
4
5
LICENSE
6
                                                                                
7
This program is free software; you can redistribute it and/or
8
modify it under the terms of the GNU General Public License
9
as published by the Free Software Foundation; either version 2
10
of the License, or (at your option) any later version.
11
                                                                                
12
This program is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
GNU General Public License for more details.
16
                                                                                
17
You should have received a copy of the GNU General Public License
18
along with this program; if not, write to the Free Software
19
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20
21
*/
22
23
#include <sys/types.h>
24
#include <sys/socket.h>
25
#include <sys/stat.h>
26
#include <netinet/in.h>
27
#include <arpa/inet.h>
28
#include <string.h>
29
#include <stdlib.h>
30
#include <sys/types.h>
31
#include <time.h>
32
#include <syslog.h>
33
#include <errno.h>
34
35
#include "httpd.h"
36
#include "http_core.h"
37
#include "http_config.h"
38
#include "http_log.h"
39
#include "http_request.h"
40
41
module AP_MODULE_DECLARE_DATA evasive20_module;
42
43
/* BEGIN DoS Evasive Maneuvers Definitions */
44
45
#define MAILER	"/bin/mail %s"
46
#define  LOG( A, ... ) { openlog("mod_evasive", LOG_PID, LOG_DAEMON); syslog( A, __VA_ARGS__ ); closelog(); }
47
48
#define DEFAULT_HASH_TBL_SIZE   3097ul  // Default hash table size
49
#define DEFAULT_PAGE_COUNT      2       // Default maximum page hit count per interval
50
#define DEFAULT_SITE_COUNT      50      // Default maximum site hit count per interval
51
#define DEFAULT_PAGE_INTERVAL   1       // Default 1 Second page interval
52
#define DEFAULT_SITE_INTERVAL   1       // Default 1 Second site interval
53
#define DEFAULT_BLOCKING_PERIOD 10      // Default for Detected IPs; blocked for 10 seconds
54
#define DEFAULT_LOG_DIR		"/tmp"  // Default temp directory
55
56
/* END DoS Evasive Maneuvers Definitions */
57
58
/* BEGIN NTT (Named Timestamp Tree) Headers */
59
60
enum { ntt_num_primes = 28 };
61
62
/* ntt root tree */
63
struct ntt {
64
    long size;
65
    long items;
66
    struct ntt_node **tbl;
67
};
68
69
/* ntt node (entry in the ntt root tree) */
70
struct ntt_node {
71
    char *key;
72
    time_t timestamp;
73
    long count;
74
    struct ntt_node *next;
75
};
76
77
/* ntt cursor */
78
struct ntt_c {
79
  long iter_index;
80
  struct ntt_node *iter_next;
81
};
82
83
struct ntt *ntt_create(long size);
84
int ntt_destroy(struct ntt *ntt);
85
struct ntt_node	*ntt_find(struct ntt *ntt, const char *key);
86
struct ntt_node	*ntt_insert(struct ntt *ntt, const char *key, time_t timestamp);
87
int ntt_delete(struct ntt *ntt, const char *key);
88
long ntt_hashcode(struct ntt *ntt, const char *key);	
89
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c);
90
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c);
91
92
/* END NTT (Named Timestamp Tree) Headers */
93
94
95
/* BEGIN DoS Evasive Maneuvers Globals */
96
97
struct ntt *hit_list;	// Our dynamic hash table
98
99
static unsigned long hash_table_size = DEFAULT_HASH_TBL_SIZE;
100
static int page_count = DEFAULT_PAGE_COUNT;
101
static int page_interval = DEFAULT_PAGE_INTERVAL;
102
static int site_count = DEFAULT_SITE_COUNT;
103
static int site_interval = DEFAULT_SITE_INTERVAL;
104
static int blocking_period = DEFAULT_BLOCKING_PERIOD;
105
static char *email_notify = NULL;
106
static char *log_dir = NULL;
107
static char *system_command = NULL;
108
static const char *whitelist(cmd_parms *cmd, void *dconfig, const char *ip);
109
int is_whitelisted(const char *ip);
110
111
/* END DoS Evasive Maneuvers Globals */
112
113
static void * create_hit_list(apr_pool_t *p, server_rec *s) 
114
{
115
    /* Create a new hit list for this listener */
116
117
    hit_list = ntt_create(hash_table_size);
118
}
119
120
static const char *whitelist(cmd_parms *cmd, void *dconfig, const char *ip)
121
{
122
  char entry[128];
123
  snprintf(entry, sizeof(entry), "WHITELIST_%s", ip);
124
  ntt_insert(hit_list, entry, time(NULL));
125
  
126
  return NULL;
127
}
128
129
130
static int access_checker(request_rec *r) 
131
{
132
    int ret = OK;
133
134
    /* BEGIN DoS Evasive Maneuvers Code */
135
136
    if (r->prev == NULL && r->main == NULL && hit_list != NULL) {
137
      char hash_key[2048];
138
      struct ntt_node *n;
139
      time_t t = time(NULL);
140
141
      /* Check whitelist */
142
      if (is_whitelisted(r->connection->remote_ip)) 
143
        return OK;
144
145
      /* First see if the IP itself is on "hold" */
146
      n = ntt_find(hit_list, r->connection->remote_ip);
147
148
      if (n != NULL && t-n->timestamp<blocking_period) {
149
 
150
        /* If the IP is on "hold", make it wait longer in 403 land */
151
        ret = HTTP_FORBIDDEN;
152
        n->timestamp = time(NULL);
153
154
      /* Not on hold, check hit stats */
155
      } else {
156
157
        /* Has URI been hit too much? */
158
        snprintf(hash_key, 2048, "%s_%s", r->connection->remote_ip, r->uri);
159
        n = ntt_find(hit_list, hash_key);
160
        if (n != NULL) {
161
162
          /* If URI is being hit too much, add to "hold" list and 403 */
163
          if (t-n->timestamp<page_interval && n->count>=page_count) {
164
            ret = HTTP_FORBIDDEN;
165
            ntt_insert(hit_list, r->connection->remote_ip, time(NULL));
166
          } else {
167
168
            /* Reset our hit count list as necessary */
169
            if (t-n->timestamp>=page_interval) {
170
              n->count=0;
171
            }
172
          }
173
          n->timestamp = t;
174
          n->count++;
175
        } else {
176
          ntt_insert(hit_list, hash_key, t);
177
        }
178
179
        /* Has site been hit too much? */
180
        snprintf(hash_key, 2048, "%s_SITE", r->connection->remote_ip);
181
        n = ntt_find(hit_list, hash_key);
182
        if (n != NULL) {
183
184
          /* If site is being hit too much, add to "hold" list and 403 */
185
          if (t-n->timestamp<site_interval && n->count>=site_count) {
186
            ret = HTTP_FORBIDDEN;
187
            ntt_insert(hit_list, r->connection->remote_ip, time(NULL));
188
          } else {
189
190
            /* Reset our hit count list as necessary */
191
            if (t-n->timestamp>=site_interval) {
192
              n->count=0;
193
            }
194
          }
195
          n->timestamp = t;
196
          n->count++;
197
        } else {
198
          ntt_insert(hit_list, hash_key, t);
199
        }
200
      }
201
202
      /* Perform email notification and system functions */
203
      if (ret == HTTP_FORBIDDEN) {
204
        char filename[1024];
205
        struct stat s;
206
        FILE *file;
207
208
        snprintf(filename, sizeof(filename), "%s/dos-%s", log_dir != NULL ? log_dir : DEFAULT_LOG_DIR, r->connection->remote_ip);
209
        if (stat(filename, &s)) {
210
          file = fopen(filename, "w");
211
          if (file != NULL) {
212
            fprintf(file, "%ld\n", getpid());
213
            fclose(file);
214
215
            LOG(LOG_ALERT, "Blacklisting address %s: possible DoS attack.", r->connection->remote_ip);
216
            if (email_notify != NULL) {
217
              snprintf(filename, sizeof(filename), MAILER, email_notify);
218
              file = popen(filename, "w");
219
              if (file != NULL) {
220
                fprintf(file, "To: %s\n", email_notify);
221
                fprintf(file, "Subject: HTTP BLACKLIST %s\n\n", r->connection->remote_ip);
222
                fprintf(file, "mod_evasive HTTP Blacklisted %s\n", r->connection->remote_ip);
223
                pclose(file);
224
              }
225
            }
226
227
            if (system_command != NULL) {
228
              snprintf(filename, sizeof(filename), system_command, r->connection->remote_ip);
229
              system(filename);
230
            }
231
 
232
          } else {
233
            LOG(LOG_ALERT, "Couldn't open logfile %s: %s",filename, strerror(errno));
234
	  }
235
236
        } /* if (temp file does not exist) */
237
238
      } /* if (ret == HTTP_FORBIDDEN) */
239
240
    } /* if (r->prev == NULL && r->main == NULL && hit_list != NULL) */
241
242
    /* END DoS Evasive Maneuvers Code */
243
244
    if (ret == HTTP_FORBIDDEN
245
	&& (ap_satisfies(r) != SATISFY_ANY || !ap_some_auth_required(r))) {
246
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
247
            "client denied by server configuration: %s",
248
            r->filename);
249
    }
250
251
    return ret;
252
}
253
254
int is_whitelisted(const char *ip) {
255
  char hashkey[128];
256
  char octet[4][4];
257
  char *dip;
258
  char *oct;
259
  int i = 0;
260
261
  memset(octet, 0, 16);
262
  dip = strdup(ip);
263
  if (dip == NULL)
264
    return 0;
265
266
  oct = strtok(dip, ".");
267
  while(oct != NULL && i<4) {
268
    if (strlen(oct)<=3) 
269
      strcpy(octet[i], oct);
270
    i++;
271
    oct = strtok(NULL, ".");
272
  }
273
  free(dip);
274
275
  /* Exact Match */
276
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s", ip); 
277
  if (ntt_find(hit_list, hashkey)!=NULL)
278
    return 1;
279
280
  /* IPv4 Wildcards */ 
281
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.*.*.*", octet[0]);
282
  if (ntt_find(hit_list, hashkey)!=NULL)
283
    return 1;
284
285
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.%s.*.*", octet[0], octet[1]);
286
  if (ntt_find(hit_list, hashkey)!=NULL)
287
    return 1;
288
289
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.%s.%s.*", octet[0], octet[1], octet[2]);
290
  if (ntt_find(hit_list, hashkey)!=NULL)
291
    return 1;
292
293
  /* No match */
294
  return 0;
295
}
296
297
static apr_status_t destroy_hit_list(void *not_used) {
298
  ntt_destroy(hit_list);
299
  free(email_notify);
300
  free(system_command);
301
}
302
303
304
/* BEGIN NTT (Named Timestamp Tree) Functions */
305
306
static unsigned long ntt_prime_list[ntt_num_primes] = 
307
{
308
    53ul,         97ul,         193ul,       389ul,       769ul,
309
    1543ul,       3079ul,       6151ul,      12289ul,     24593ul,
310
    49157ul,      98317ul,      196613ul,    393241ul,    786433ul,
311
    1572869ul,    3145739ul,    6291469ul,   12582917ul,  25165843ul,
312
    50331653ul,   100663319ul,  201326611ul, 402653189ul, 805306457ul,
313
    1610612741ul, 3221225473ul, 4294967291ul
314
};
315
316
317
/* Find the numeric position in the hash table based on key and modulus */
318
319
long ntt_hashcode(struct ntt *ntt, const char *key) {
320
    unsigned long val = 0;
321
    for (; *key; ++key) val = 5 * val + *key;
322
    return(val % ntt->size);
323
}
324
325
/* Creates a single node in the tree */
326
327
struct ntt_node *ntt_node_create(const char *key) {
328
    char *node_key;
329
    struct ntt_node* node;
330
331
    node = (struct ntt_node *) malloc(sizeof(struct ntt_node));
332
    if (node == NULL) {
333
	return NULL;
334
    }
335
    if ((node_key = strdup(key)) == NULL) {
336
        free(node);
337
	return NULL;
338
    }
339
    node->key = node_key;
340
    node->timestamp = time(NULL);
341
    node->next = NULL;
342
    return(node);
343
}
344
345
/* Tree initializer */
346
347
struct ntt *ntt_create(long size) {
348
    long i = 0;
349
    struct ntt *ntt = (struct ntt *) malloc(sizeof(struct ntt));
350
351
    if (ntt == NULL)
352
        return NULL;
353
    while (ntt_prime_list[i] < size) { i++; }
354
    ntt->size  = ntt_prime_list[i];
355
    ntt->items = 0;
356
    ntt->tbl   = (struct ntt_node **) calloc(ntt->size, sizeof(struct ntt_node *));
357
    if (ntt->tbl == NULL) {
358
        free(ntt);
359
        return NULL;
360
    }
361
    return(ntt);
362
}
363
364
/* Find an object in the tree */
365
366
struct ntt_node *ntt_find(struct ntt *ntt, const char *key) {
367
    long hash_code;
368
    struct ntt_node *node;
369
370
    if (ntt == NULL) return NULL;
371
372
    hash_code = ntt_hashcode(ntt, key);
373
    node = ntt->tbl[hash_code];
374
375
    while (node) {
376
        if (!strcmp(key, node->key)) {
377
            return(node);
378
        }
379
        node = node->next;
380
    }
381
    return((struct ntt_node *)NULL);
382
}
383
384
/* Insert a node into the tree */
385
386
struct ntt_node *ntt_insert(struct ntt *ntt, const char *key, time_t timestamp) {
387
    long hash_code;
388
    struct ntt_node *parent;
389
    struct ntt_node *node;
390
    struct ntt_node *new_node = NULL;
391
392
    if (ntt == NULL) return NULL;
393
394
    hash_code = ntt_hashcode(ntt, key);
395
    parent	= NULL;
396
    node	= ntt->tbl[hash_code];
397
398
    while (node != NULL) {
399
        if (strcmp(key, node->key) == 0) { 
400
            new_node = node;
401
            node = NULL;
402
        }
403
404
	if (new_node == NULL) {
405
          parent = node;
406
          node = node->next;
407
        }
408
    }
409
410
    if (new_node != NULL) {
411
        new_node->timestamp = timestamp;
412
        new_node->count = 0;
413
        return new_node; 
414
    }
415
416
    /* Create a new node */
417
    new_node = ntt_node_create(key);
418
    new_node->timestamp = timestamp;
419
    new_node->timestamp = 0;
420
421
    ntt->items++;
422
423
    /* Insert */
424
    if (parent) {  /* Existing parent */
425
	parent->next = new_node;
426
        return new_node;  /* Return the locked node */
427
    }
428
429
    /* No existing parent; add directly to hash table */
430
    ntt->tbl[hash_code] = new_node;
431
    return new_node;
432
}
433
434
/* Tree destructor */
435
436
int ntt_destroy(struct ntt *ntt) {
437
    struct ntt_node *node, *next;
438
    struct ntt_c c;
439
440
    if (ntt == NULL) return -1;
441
442
    node = c_ntt_first(ntt, &c);
443
    while(node != NULL) {
444
        next = c_ntt_next(ntt, &c);
445
        ntt_delete(ntt, node->key);
446
        node = next;
447
    }
448
449
    free(ntt->tbl);
450
    free(ntt);
451
    ntt = (struct ntt *) NULL;
452
453
    return 0;
454
}
455
456
/* Delete a single node in the tree */
457
458
int ntt_delete(struct ntt *ntt, const char *key) {
459
    long hash_code;
460
    struct ntt_node *parent = NULL;
461
    struct ntt_node *node;
462
    struct ntt_node *del_node = NULL;
463
464
    if (ntt == NULL) return -1;
465
466
    hash_code = ntt_hashcode(ntt, key);
467
    node        = ntt->tbl[hash_code];
468
469
    while (node != NULL) {
470
        if (strcmp(key, node->key) == 0) {
471
            del_node = node;
472
            node = NULL;
473
        }
474
475
        if (del_node == NULL) {
476
          parent = node;
477
          node = node->next;
478
        }
479
    }
480
481
    if (del_node != NULL) {
482
483
        if (parent) {
484
            parent->next = del_node->next;
485
        } else {
486
            ntt->tbl[hash_code] = del_node->next;
487
        }
488
489
        free(del_node->key);
490
        free(del_node);
491
        ntt->items--;
492
493
        return 0;
494
    }
495
496
    return -5;
497
}
498
499
/* Point cursor to first item in tree */
500
501
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c) {
502
503
    c->iter_index = 0;
504
    c->iter_next = (struct ntt_node *)NULL;
505
    return(c_ntt_next(ntt, c));
506
}
507
508
/* Point cursor to next iteration in tree */
509
510
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c) {
511
    long index;
512
    struct ntt_node *node = c->iter_next;
513
514
    if (ntt == NULL) return NULL;
515
516
    if (node) {
517
        if (node != NULL) {
518
            c->iter_next = node->next;
519
            return (node);
520
        }
521
    }
522
523
    if (! node) {
524
        while (c->iter_index < ntt->size) {
525
            index = c->iter_index++;
526
527
            if (ntt->tbl[index]) {
528
                c->iter_next = ntt->tbl[index]->next;
529
                return(ntt->tbl[index]);
530
            }
531
        }
532
    }
533
    return((struct ntt_node *)NULL);
534
}
535
536
/* END NTT (Named Pointer Tree) Functions */
537
538
539
/* BEGIN Configuration Functions */
540
541
static const char *
542
get_hash_tbl_size(cmd_parms *cmd, void *dconfig, const char *value) {
543
  long n = strtol(value, NULL, 0);
544
545
  if (n<=0) {
546
    hash_table_size = DEFAULT_HASH_TBL_SIZE;
547
  } else  {
548
    hash_table_size = n;
549
  }
550
551
  return NULL;
552
}
553
554
static const char *
555
get_page_count(cmd_parms *cmd, void *dconfig, const char *value) {
556
  long n = strtol(value, NULL, 0);
557
  if (n<=0) {
558
    page_count = DEFAULT_PAGE_COUNT;
559
  } else {
560
    page_count = n;
561
  }
562
563
  return NULL;
564
}
565
566
static const char *
567
get_site_count(cmd_parms *cmd, void *dconfig, const char *value) {
568
  long n = strtol(value, NULL, 0);
569
  if (n<=0) {
570
    site_count = DEFAULT_SITE_COUNT;
571
  } else {
572
    site_count = n;
573
  }
574
575
  return NULL;
576
}
577
578
static const char *
579
get_page_interval(cmd_parms *cmd, void *dconfig, const char *value) {
580
  long n = strtol(value, NULL, 0);
581
  if (n<=0) {
582
    page_interval = DEFAULT_PAGE_INTERVAL;
583
  } else {
584
    page_interval = n;
585
  }
586
587
  return NULL;
588
}
589
590
static const char *
591
get_site_interval(cmd_parms *cmd, void *dconfig, const char *value) {
592
  long n = strtol(value, NULL, 0);
593
  if (n<=0) {
594
    site_interval = DEFAULT_SITE_INTERVAL;
595
  } else {
596
    site_interval = n;
597
  }
598
599
  return NULL;
600
}
601
602
static const char *
603
get_blocking_period(cmd_parms *cmd, void *dconfig, const char *value) {
604
  long n = strtol(value, NULL, 0);
605
  if (n<=0) {
606
    blocking_period = DEFAULT_BLOCKING_PERIOD;
607
  } else {
608
    blocking_period = n;
609
  }
610
611
  return NULL;
612
}
613
614
static const char *
615
get_log_dir(cmd_parms *cmd, void *dconfig, const char *value) {
616
  if (value != NULL && value[0] != 0) {
617
    if (log_dir != NULL)
618
      free(log_dir);
619
    log_dir = strdup(value);
620
  }
621
622
  return NULL;
623
}
624
625
static const char *
626
get_email_notify(cmd_parms *cmd, void *dconfig, const char *value) {
627
  if (value != NULL && value[0] != 0) {
628
    if (email_notify != NULL)
629
      free(email_notify);
630
    email_notify = strdup(value);
631
  }
632
633
  return NULL;
634
}
635
636
static const char *
637
get_system_command(cmd_parms *cmd, void *dconfig, const char *value) {
638
  if (value != NULL && value[0] != 0) {
639
    if (system_command != NULL)
640
      free(system_command);
641
    system_command = strdup(value);
642
  }
643
 
644
  return NULL;
645
} 
646
647
/* END Configuration Functions */
648
649
static const command_rec access_cmds[] =
650
{
651
	AP_INIT_TAKE1("DOSHashTableSize", get_hash_tbl_size, NULL, RSRC_CONF, 
652
		"Set size of hash table"),
653
654
        AP_INIT_TAKE1("DOSPageCount", get_page_count, NULL, RSRC_CONF,
655
		"Set maximum page hit count per interval"),
656
657
        AP_INIT_TAKE1("DOSSiteCount", get_site_count, NULL, RSRC_CONF,
658
		"Set maximum site hit count per interval"),
659
660
        AP_INIT_TAKE1("DOSPageInterval", get_page_interval, NULL, RSRC_CONF,
661
		"Set page interval"),
662
663
	AP_INIT_TAKE1("DOSSiteInterval", get_site_interval, NULL, RSRC_CONF,
664
		"Set site interval"),
665
666
        AP_INIT_TAKE1("DOSBlockingPeriod", get_blocking_period, NULL, RSRC_CONF,
667
		"Set blocking period for detected DoS IPs"),
668
669
	AP_INIT_TAKE1("DOSEmailNotify", get_email_notify, NULL, RSRC_CONF,
670
		"Set email notification"),
671
672
	AP_INIT_TAKE1("DOSLogDir", get_log_dir, NULL, RSRC_CONF,
673
		"Set log dir"),
674
675
	AP_INIT_TAKE1("DOSSystemCommand", get_system_command, NULL, RSRC_CONF,
676
		"Set system command on DoS"),
677
678
        AP_INIT_ITERATE("DOSWhitelist", whitelist, NULL, RSRC_CONF,
679
                "IP-addresses wildcards to whitelist"),
680
681
	{ NULL }
682
};
683
684
static void register_hooks(apr_pool_t *p) {
685
  ap_hook_access_checker(access_checker, NULL, NULL, APR_HOOK_MIDDLE);
686
  apr_pool_cleanup_register(p, NULL, apr_pool_cleanup_null, destroy_hit_list);
687
};
688
689
module AP_MODULE_DECLARE_DATA evasive20_module =
690
{
691
    STANDARD20_MODULE_STUFF,
692
    NULL,
693
    NULL,
694
    create_hit_list,
695
    NULL,
696
    access_cmds,
697
    register_hooks
698
};
699
(-)mod_evasive/work/mod_evasive/mod_evasiveNSAPI.c (+608 lines)
Line 0 Link Here
1
/*
2
mod_dosevasive/1.8 for NSAPI
3
 
4
Copyright 2002 by Jonathan A. Zdziarski.  All rights reserved.
5
 
6
LICENSE
7
-------
8
 
9
This distribution may be freely distributed in its original form.  
10
License is granted to make modifications to the source for internal,
11
private use only, provided you retain this notice, disclaimers, author's
12
copyright, and credits.
13
 
14
 
15
DISCLAIMER
16
----------
17
 
18
THIS SOFTWARE IS PROVIDE "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
19
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO WAY SHALL THE
21
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
27
OF THE POSSIBILITY OF SUCH DAMAGE.
28
 
29
*/
30
31
/* This is a port to NSAPI from mod_dosevasive/1.8 for Apache 2.0 
32
   2003-10-29 Reine Persson
33
*/
34
35
#include <sys/types.h>
36
#include <sys/socket.h>
37
#include <sys/stat.h>
38
#include <netinet/in.h>
39
#include <arpa/inet.h>
40
#include <string.h>
41
#include <stdlib.h>
42
#include <sys/types.h>
43
#include <time.h>
44
#include <syslog.h>
45
#include <errno.h>
46
47
#include <nsapi.h>
48
 
49
50
/* BEGIN DoS Evasive Maneuvers Definitions */
51
52
#define DEFAULT_LOG_DIR "/tmp"
53
#define MAILER	"/bin/mail %s"
54
#define  LOG( A, ... ) { openlog("mod_dosevasive", LOG_PID, LOG_DAEMON); syslog( A, __VA_ARGS__ ); closelog(); }
55
56
#define DEFAULT_HASH_TBL_SIZE   3097ul  // Default hash table size
57
#define DEFAULT_PAGE_COUNT      2       // Default maximum page hit count per interval
58
#define DEFAULT_SITE_COUNT      50      // Default maximum site hit count per interval
59
#define DEFAULT_PAGE_INTERVAL   1       // Default 1 Second page interval
60
#define DEFAULT_SITE_INTERVAL   1       // Default 1 Second site interval
61
#define DEFAULT_BLOCKING_PERIOD 10      // Default for Detected IPs; blocked for 10 seconds
62
63
/* END DoS Evasive Maneuvers Definitions */
64
65
static CRITICAL mod_dosevasive_crit;
66
67
/* BEGIN NTT (Named Timestamp Tree) Headers */
68
69
enum { ntt_num_primes = 28 };
70
71
/* ntt root tree */
72
struct ntt {
73
    long size;
74
    long items;
75
    struct ntt_node **tbl;
76
};
77
78
/* ntt node (entry in the ntt root tree) */
79
struct ntt_node {
80
    char *key;
81
    time_t timestamp;
82
    long count;
83
    struct ntt_node *next;
84
};
85
86
/* ntt cursor */
87
struct ntt_c {
88
  long iter_index;
89
  struct ntt_node *iter_next;
90
};
91
92
struct ntt *ntt_create(long size);
93
int ntt_destroy(struct ntt *ntt);
94
struct ntt_node	*ntt_find(struct ntt *ntt, const char *key);
95
struct ntt_node	*ntt_insert(struct ntt *ntt, const char *key, time_t timestamp);
96
int ntt_delete(struct ntt *ntt, const char *key);
97
long ntt_hashcode(struct ntt *ntt, const char *key);	
98
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c);
99
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c);
100
101
/* END NTT (Named Timestamp Tree) Headers */
102
103
104
/* BEGIN DoS Evasive Maneuvers Globals */
105
106
struct ntt *hit_list;	// Our dynamic hash table
107
108
static unsigned long hash_table_size = DEFAULT_HASH_TBL_SIZE;
109
static int page_count = DEFAULT_PAGE_COUNT;
110
static int page_interval = DEFAULT_PAGE_INTERVAL;
111
static int site_count = DEFAULT_SITE_COUNT;
112
static int site_interval = DEFAULT_SITE_INTERVAL;
113
static int blocking_period = DEFAULT_BLOCKING_PERIOD;
114
static char *log_dir = NULL;
115
static char *email_notify = NULL;
116
static char *system_command = NULL;
117
118
static const char * whitelist(const char *ip);
119
int is_whitelisted(const char *ip);
120
static int destroy_hit_list(void *not_used);
121
122
/* END DoS Evasive Maneuvers Globals */
123
124
static char *
125
itemize(char *str,char delim)
126
{
127
    static char *nextitem = NULL;
128
    char *result;
129
 
130
    if(str)
131
        nextitem=str;
132
    if(!nextitem)
133
        return(NULL);
134
    result=nextitem;
135
    while(*nextitem && *nextitem!=delim) 
136
        ++nextitem;
137
    if(*nextitem) 
138
        *nextitem++='\0';
139
    else 
140
        nextitem=NULL;
141
    return(result);
142
}
143
144
 
145
NSAPI_PUBLIC int
146
mod_dosevasive_init(pblock *pb, Session *sn, Request *rq)
147
{
148
  char *ip,*stmp,*white_list=NULL;
149
  int itmp;
150
151
  mod_dosevasive_crit = crit_init();
152
  if ((itmp=atoi(pblock_findval("DOSHashTableSize", pb))) != 0 )
153
    hash_table_size=itmp;
154
  if ((itmp=atoi(pblock_findval("DOSPageCount", pb))) != 0 )
155
    page_count=itmp;
156
  if ((itmp=atoi(pblock_findval("DOSSiteCount", pb))) != 0 )
157
    site_count=itmp;
158
  if ((itmp=atoi(pblock_findval("DOSPageInterval", pb))) != 0 )
159
    page_interval=itmp;
160
  if ((itmp=atoi(pblock_findval("DOSSiteInterval", pb))) != 0 )
161
    site_interval=itmp;
162
  if ((itmp=atoi(pblock_findval("DOSBlockingPeriod", pb))) != 0 )
163
    blocking_period=itmp;
164
  if ((stmp=pblock_findval("DOSLogDir", pb)) != NULL )
165
    log_dir=stmp;
166
  if ((stmp=pblock_findval("DOSEmailNotify", pb)) != NULL )
167
    email_notify=stmp;
168
  if ((stmp=pblock_findval("DOSSystemCommand", pb)) != NULL )
169
    system_command=stmp;
170
171
  white_list=pblock_findval("DOSWhitelist", pb);
172
173
  hit_list = ntt_create(hash_table_size);
174
175
  if ( white_list != NULL ) {
176
    ip=itemize(white_list,',');
177
    while( ip != NULL ) {
178
      whitelist(ip);
179
      ip=itemize(NULL,',');
180
    }
181
  }
182
  return REQ_PROCEED;
183
}
184
 
185
NSAPI_PUBLIC int
186
mod_dosevasive_check(pblock *pb, Session *sn, Request *rq)
187
{
188
  int ret = REQ_PROCEED;
189
190
  /* BEGIN DoS Evasive Maneuvers Code */
191
  
192
  if (pblock_findval("NS_original_uri",rq->vars) == NULL && pblock_findval("referer",rq->headers) == NULL && hit_list != NULL) {
193
    char hash_key[2048];
194
    struct ntt_node *n;
195
    time_t t = time(NULL);
196
197
    /* Check whitelist */
198
    if (is_whitelisted(pblock_findval("ip",sn->client))) 
199
      return REQ_PROCEED;
200
    
201
    /* First see if the IP itself is on "hold" */
202
    n = ntt_find(hit_list, pblock_findval("ip",sn->client));
203
    
204
    if (n != NULL && t-n->timestamp<blocking_period) {
205
      
206
      /* If the IP is on "hold", make it wait longer in 403 land */
207
      ret = REQ_ABORTED;
208
      n->timestamp = time(NULL);
209
      
210
      /* Not on hold, check hit stats */
211
    } else {
212
      
213
      /* Has URI been hit too much? */
214
      snprintf(hash_key, 2048, "%s_%s", pblock_findval("ip",sn->client), pblock_findval("uri",rq->reqpb));
215
      n = ntt_find(hit_list, hash_key);
216
      if (n != NULL) {
217
	
218
	/* If URI is being hit too much, add to "hold" list and 403 */
219
	if (t-n->timestamp<page_interval && n->count>=page_count) {
220
	  ret = REQ_ABORTED;
221
	  ntt_insert(hit_list, pblock_findval("ip",sn->client), time(NULL));
222
	} else {
223
	  
224
	  /* Reset our hit count list as necessary */
225
	  if (t-n->timestamp>=page_interval) {
226
	    n->count=0;
227
	  }
228
	}
229
	n->timestamp = t;
230
	n->count++;
231
      } else {
232
	
233
	ntt_insert(hit_list, hash_key, t);
234
      }
235
      
236
      /* Has site been hit too much? */
237
      snprintf(hash_key, 2048, "%s_SITE", pblock_findval("ip",sn->client));
238
      n = ntt_find(hit_list, hash_key);
239
      if (n != NULL) {
240
	
241
	/* If site is being hit too much, add to "hold" list and 403 */
242
	if (t-n->timestamp<site_interval && n->count>=site_count) {
243
	  ret = REQ_ABORTED;
244
	  ntt_insert(hit_list, pblock_findval("ip",sn->client), time(NULL));
245
	} else {
246
	  
247
	  /* Reset our hit count list as necessary */
248
	  if (t-n->timestamp>=site_interval) {
249
	    n->count=0;
250
	  }
251
	}
252
	n->timestamp = t;
253
	n->count++;
254
      } else {
255
	ntt_insert(hit_list, hash_key, t);
256
      }
257
    }
258
    
259
    /* Perform email notification and system functions */
260
    if (ret == REQ_ABORTED) {
261
      char filename[1024];
262
      struct stat s;
263
      FILE *file;
264
      
265
      snprintf(filename, sizeof(filename), "%s/dos-%s", log_dir != NULL ? log_dir : DEFAULT_LOG_DIR, pblock_findval("ip",sn->client));
266
      if (stat(filename, &s)) {
267
	file = fopen(filename, "w");
268
	if (file != NULL) {
269
	  fprintf(file, "%ld\n", getpid());
270
	  fclose(file);
271
	  
272
	  LOG(LOG_ALERT, "Blacklisting address %s: possible DoS attack.",pblock_findval("ip",sn->client));
273
	  if (email_notify != NULL) {
274
	    snprintf(filename, sizeof(filename), MAILER, email_notify);
275
	    file = popen(filename, "w");
276
	    if (file != NULL) {
277
	      fprintf(file, "To: %s\n", email_notify);
278
	      fprintf(file, "Subject: HTTP BLACKLIST %s\n\n", pblock_findval("ip",sn->client));
279
	      fprintf(file, "mod_dosevasive HTTP Blacklisted %s\n", pblock_findval("ip",sn->client));
280
	      pclose(file);
281
	    } else {
282
	      LOG(LOG_ALERT, "Couldn't open logfile %s: %s",filename, strerror(errno));
283
	    }
284
	  }
285
	  
286
	  if (system_command != NULL) {
287
	    snprintf(filename, sizeof(filename), system_command, pblock_findval("ip",sn->client));
288
	    system(filename);
289
	  }
290
	  
291
	}
292
	
293
      } /* if (temp file does not exist) */
294
      
295
    } /* if (ret == REQ_ABORTED) */
296
    
297
  } /* if (vars->NS_Original_uri == NULL && headers->referer == NULL && hit_list != NULL) */
298
  
299
  /* END DoS Evasive Maneuvers Code */
300
  
301
  if (ret == REQ_ABORTED ) {
302
    log_error(LOG_SECURITY,"mod_dosevasive_check",sn,rq,"client denied by server configuration: %s",pblock_findval("uri",rq->reqpb));
303
    protocol_status(sn, rq, PROTOCOL_FORBIDDEN, NULL);
304
  }
305
  return ret;
306
}
307
308
309
static const char *whitelist(const char *ip)
310
{
311
  char entry[128];
312
  snprintf(entry, sizeof(entry), "WHITELIST_%s", ip);
313
  ntt_insert(hit_list, entry, time(NULL));
314
  
315
  return NULL;
316
}
317
318
319
int is_whitelisted(const char *ip) {
320
  char hashkey[128];
321
  char octet[4][4];
322
  char *dip;
323
  char *oct;
324
  int i = 0;
325
  
326
  memset(octet, 0, 16);
327
  dip = strdup(ip);
328
  if (dip == NULL)
329
    return 0;
330
  
331
  oct = strtok(dip, ".");
332
  while(oct != NULL && i<4) {
333
    if (strlen(oct)<=3) 
334
      strcpy(octet[i], oct);
335
    i++;
336
    oct = strtok(NULL, ".");
337
  }
338
  free(dip);
339
  
340
  /* Exact Match */
341
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s", ip); 
342
  if (ntt_find(hit_list, hashkey)!=NULL)
343
    return 1;
344
  
345
  /* IPv4 Wildcards */ 
346
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.*.*.*", octet[0]);
347
  if (ntt_find(hit_list, hashkey)!=NULL)
348
    return 1;
349
350
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.%s.*.*", octet[0], octet[1]);
351
  if (ntt_find(hit_list, hashkey)!=NULL)
352
    return 1;
353
  
354
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.%s.%s.*", octet[0], octet[1], octet[2]);
355
  if (ntt_find(hit_list, hashkey)!=NULL)
356
    return 1;
357
  
358
  /* No match */
359
  return 0;
360
}
361
362
static int destroy_hit_list(void *not_used) {
363
  ntt_destroy(hit_list);
364
  free(log_dir);
365
  free(email_notify);
366
  free(system_command);
367
  return 0;
368
}
369
370
371
/* BEGIN NTT (Named Timestamp Tree) Functions */
372
373
static unsigned long ntt_prime_list[ntt_num_primes] = 
374
{
375
  53ul,         97ul,         193ul,       389ul,       769ul,
376
  1543ul,       3079ul,       6151ul,      12289ul,     24593ul,
377
  49157ul,      98317ul,      196613ul,    393241ul,    786433ul,
378
  1572869ul,    3145739ul,    6291469ul,   12582917ul,  25165843ul,
379
  50331653ul,   100663319ul,  201326611ul, 402653189ul, 805306457ul,
380
  1610612741ul, 3221225473ul, 4294967291ul
381
};
382
383
384
/* Find the numeric position in the hash table based on key and modulus */
385
386
long ntt_hashcode(struct ntt *ntt, const char *key) {
387
  unsigned long val = 0;
388
  for (; *key; ++key) val = 5 * val + *key;
389
  return(val % ntt->size);
390
}
391
392
/* Creates a single node in the tree */
393
394
struct ntt_node *ntt_node_create(const char *key) {
395
  char *node_key;
396
  struct ntt_node* node;
397
  
398
  node = (struct ntt_node *) malloc(sizeof(struct ntt_node));
399
  if (node == NULL) {
400
    return NULL;
401
  }
402
  if ((node_key = strdup(key)) == NULL) {
403
    free(node);
404
    return NULL;
405
  }
406
  node->key = node_key;
407
  node->timestamp = time(NULL);
408
  node->next = NULL;
409
  return(node);
410
}
411
412
/* Tree initializer */
413
414
struct ntt *ntt_create(long size) {
415
  long i = 0;
416
  struct ntt *ntt = (struct ntt *) malloc(sizeof(struct ntt));
417
  
418
  if (ntt == NULL)
419
    return NULL;
420
  while (ntt_prime_list[i] < size) { i++; }
421
  ntt->size  = ntt_prime_list[i];
422
  ntt->items = 0;
423
  ntt->tbl   = (struct ntt_node **) calloc(ntt->size, sizeof(struct ntt_node *));
424
  if (ntt->tbl == NULL) {
425
        free(ntt);
426
        return NULL;
427
  }
428
  return(ntt);
429
}
430
431
/* Find an object in the tree */
432
433
struct ntt_node *ntt_find(struct ntt *ntt, const char *key) {
434
  long hash_code;
435
  struct ntt_node *node;
436
  
437
  if (ntt == NULL) return NULL;
438
  
439
  hash_code = ntt_hashcode(ntt, key);
440
  node = ntt->tbl[hash_code];
441
  
442
  while (node) {
443
    if (!strcmp(key, node->key)) {
444
      return(node);
445
    }
446
    node = node->next;
447
  }
448
  return((struct ntt_node *)NULL);
449
}
450
451
/* Insert a node into the tree */
452
453
struct ntt_node *ntt_insert(struct ntt *ntt, const char *key, time_t timestamp) {
454
  long hash_code;
455
  struct ntt_node *parent;
456
  struct ntt_node *node;
457
  struct ntt_node *new_node = NULL;
458
  
459
  if (ntt == NULL) return NULL;
460
  
461
  hash_code = ntt_hashcode(ntt, key);
462
  parent	= NULL;
463
  node	= ntt->tbl[hash_code];
464
  
465
  crit_enter(mod_dosevasive_crit); /*Lock*/
466
467
  while (node != NULL) {
468
    if (strcmp(key, node->key) == 0) { 
469
      new_node = node;
470
      node = NULL;
471
    }
472
    
473
    if (new_node == NULL) {
474
      parent = node;
475
      node = node->next;
476
    }
477
  }
478
  
479
  if (new_node != NULL) {
480
    new_node->timestamp = timestamp;
481
    new_node->count = 0;
482
    crit_exit(mod_dosevasive_crit); /*Unlock*/
483
    return new_node; 
484
  }
485
  
486
  /* Create a new node */
487
  new_node = ntt_node_create(key);
488
  new_node->timestamp = timestamp;
489
  new_node->timestamp = 0;
490
  
491
  ntt->items++;
492
  
493
  /* Insert */
494
  if (parent) {  /* Existing parent */
495
    parent->next = new_node;
496
    crit_exit(mod_dosevasive_crit); /*Unlock*/
497
    return new_node;  /* Return the locked node */
498
  }
499
  
500
  /* No existing parent; add directly to hash table */
501
  ntt->tbl[hash_code] = new_node;
502
  crit_exit(mod_dosevasive_crit); /*Unlock*/
503
  return new_node;
504
}
505
506
/* Tree destructor */
507
508
int ntt_destroy(struct ntt *ntt) {
509
  struct ntt_node *node, *next;
510
  struct ntt_c c;
511
  
512
  if (ntt == NULL) return -1;
513
  
514
  node = c_ntt_first(ntt, &c);
515
  while(node != NULL) {
516
    next = c_ntt_next(ntt, &c);
517
    ntt_delete(ntt, node->key);
518
    node = next;
519
  }
520
  
521
  free(ntt->tbl);
522
  free(ntt);
523
  ntt = (struct ntt *) NULL;
524
  
525
  return 0;
526
}
527
528
/* Delete a single node in the tree */
529
530
int ntt_delete(struct ntt *ntt, const char *key) {
531
  long hash_code;
532
  struct ntt_node *parent = NULL;
533
  struct ntt_node *node;
534
  struct ntt_node *del_node = NULL;
535
  
536
  if (ntt == NULL) return -1;
537
  
538
  hash_code = ntt_hashcode(ntt, key);
539
  node        = ntt->tbl[hash_code];
540
  
541
  while (node != NULL) {
542
    if (strcmp(key, node->key) == 0) {
543
      del_node = node;
544
      node = NULL;
545
    }
546
    
547
    if (del_node == NULL) {
548
      parent = node;
549
      node = node->next;
550
    }
551
  }
552
  
553
  if (del_node != NULL) {
554
    
555
    if (parent) {
556
      parent->next = del_node->next;
557
    } else {
558
      ntt->tbl[hash_code] = del_node->next;
559
    }
560
    
561
    free(del_node->key);
562
    free(del_node);
563
    ntt->items--;
564
    
565
    return 0;
566
  }
567
  
568
  return -5;
569
}
570
571
/* Point cursor to first item in tree */
572
573
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c) {
574
  
575
  c->iter_index = 0;
576
  c->iter_next = (struct ntt_node *)NULL;
577
  return(c_ntt_next(ntt, c));
578
}
579
580
/* Point cursor to next iteration in tree */
581
582
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c) {
583
  long index;
584
  struct ntt_node *node = c->iter_next;
585
  
586
  if (ntt == NULL) return NULL;
587
  
588
  if (node) {
589
    if (node != NULL) {
590
      c->iter_next = node->next;
591
      return (node);
592
    }
593
  }
594
  
595
  if (! node) {
596
    while (c->iter_index < ntt->size) {
597
      index = c->iter_index++;
598
      
599
      if (ntt->tbl[index]) {
600
	c->iter_next = ntt->tbl[index]->next;
601
	return(ntt->tbl[index]);
602
      }
603
    }
604
  }
605
  return((struct ntt_node *)NULL);
606
}
607
608
/* END NTT (Named Pointer Tree) Functions */
(-)mod_evasive/work/mod_evasive/test.pl (+18 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# test.pl: small script to test mod_dosevasive's effectiveness
4
5
use IO::Socket;
6
use strict;
7
8
for(0..100) {
9
  my($response);
10
  my($SOCKET) = new IO::Socket::INET( Proto   => "tcp",
11
                                      PeerAddr=> "127.0.0.1:80");
12
  if (! defined $SOCKET) { die $!; }
13
  print $SOCKET "GET /?$_ HTTP/1.0\n\n";
14
  $response = <$SOCKET>;
15
  print $response;
16
  close($SOCKET);
17
}
18
(-)mod_evasive/work/mod_evasive/.cvsignore (+5 lines)
Line 0 Link Here
1
.libs
2
*.la
3
*.slo
4
*.o
5
*.so
(-)mod_evasive/work/mod_evasive/CHANGELOG (+72 lines)
Line 0 Link Here
1
2
Version 1.10.1
3
--------------
4
5
[20051008] jonz: Fixed IP Whitelisting in Apache 1.3 Version
6
7
[20051008] jonz: Corrected initialization to prevent dumping IP database
8
9
[20051008] jonz: Documentation and code cleaned up, renamed mod_evasive
10
11
Version 1.10.0
12
--------------
13
14
[20050117] jonz: Security fix: Tempdir configuration directive (race condition)
15
16
Version 1.9.0
17
-------------
18
19
[20031030] jonz: Added NSAPI/SunONE Support
20
21
[20031030] jonz: Added TEMP_HOME definition to change temporary file locations
22
23
Version 1.8.0
24
-------------
25
26
[20030901] jonz: Added support for IP Whitelisting
27
28
Version 1.7.1
29
-------------
30
31
[20030826] jonz: Minor bugfixes to Apache 2.0 module
32
33
[20030826] jonz: Corrections to object creation and cleanup
34
35
Version 1.7.0
36
-------------
37
38
[20030822] jonz: Support for Apache 2.0
39
40
Version 1.6.1
41
-------------
42
43
[20030811] jonz: Bugfix: free() of another static variable
44
 
45
Version 1.6.0
46
-------------
47
48
[20030806] jonz: Added syslog support
49
50
Version 1.5.1
51
-------------
52
53
[20030516] jonz: Bugfix: free() of a static variable
54
55
Version 1.5.0
56
-------------
57
58
[20030425] jonz: Added automated email notification 
59
60
[20030425] jonz: Added configurable system command
61
62
Version 1.4.0
63
-------------
64
65
[20021031] jonz: Added support fror httpd.conf directives
66
67
[20021031] jonz: Added --add-module support
68
69
Version 1.0.0
70
-------------
71
72
[20021015] jonz: Initial Release
(-)mod_evasive/work/mod_evasive/LICENSE (+146 lines)
Line 0 Link Here
1
GNU GENERAL PUBLIC LICENSE
2
Version 2, June 1991 
3
4
Copyright (C) 1989, 1991 Free Software Foundation, Inc.  
5
59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
6
7
Everyone is permitted to copy and distribute verbatim copies
8
of this license document, but changing it is not allowed.
9
10
Preamble
11
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. 
12
13
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. 
14
15
To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. 
16
17
For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. 
18
19
We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. 
20
21
Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. 
22
23
Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. 
24
25
The precise terms and conditions for copying, distribution and modification follow. 
26
27
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
28
0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". 
29
30
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 
31
32
1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. 
33
34
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 
35
36
2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: 
37
38
39
a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. 
40
41
b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. 
42
43
c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) 
44
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. 
45
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. 
46
47
In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 
48
49
3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: 
50
51
a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, 
52
53
b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, 
54
55
c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) 
56
The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. 
57
If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 
58
59
4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 
60
61
5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 
62
63
6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 
64
65
7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. 
66
67
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. 
68
69
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. 
70
71
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 
72
73
8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 
74
75
9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. 
76
77
Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 
78
79
10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. 
80
81
NO WARRANTY
82
83
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 
84
85
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 
86
87
88
END OF TERMS AND CONDITIONS
89
How to Apply These Terms to Your New Programs
90
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. 
91
92
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. 
93
94
one line to give the program's name and an idea of what it does.
95
Copyright (C) yyyy  name of author
96
97
This program is free software; you can redistribute it and/or
98
modify it under the terms of the GNU General Public License
99
as published by the Free Software Foundation; either version 2
100
of the License, or (at your option) any later version.
101
102
This program is distributed in the hope that it will be useful,
103
but WITHOUT ANY WARRANTY; without even the implied warranty of
104
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
105
GNU General Public License for more details.
106
107
You should have received a copy of the GNU General Public License
108
along with this program; if not, write to the Free Software
109
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
110
111
Also add information on how to contact you by electronic and paper mail. 
112
113
If the program is interactive, make it output a short notice like this when it starts in an interactive mode: 
114
115
Gnomovision version 69, Copyright (C) year name of author
116
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
117
type `show w'.  This is free software, and you are welcome
118
to redistribute it under certain conditions; type `show c' 
119
for details.
120
121
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. 
122
123
You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: 
124
125
Yoyodyne, Inc., hereby disclaims all copyright
126
interest in the program `Gnomovision'
127
(which makes passes at compilers) written 
128
by James Hacker.
129
130
signature of Ty Coon, 1 April 1989
131
Ty Coon, President of Vice
132
133
This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. 
134
135
136
--------------------------------------------------------------------------------
137
Return to GNU's home page. 
138
FSF & GNU inquiries & questions to gnu@gnu.org. Other ways to contact the FSF. 
139
140
Comments on these web pages to webmasters@www.gnu.org, send other questions to gnu@gnu.org. 
141
142
Copyright notice above.
143
Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA 
144
145
Updated: Last modified: Sun Jul 15 13:13:30 CEST 2001 
146
(-)mod_evasive/work/mod_evasive/Makefile.tmpl (+15 lines)
Line 0 Link Here
1
2
#Dependencies
3
4
$(OBJS) $(OBJS_PIC): Makefile
5
6
# DO NOT REMOVE
7
mod_evasive.o: mod_evasive.c $(INCDIR)/httpd.h \
8
 $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
9
 $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
10
 $(INCDIR)/ap_ctype.h $(INCDIR)/hsregex.h \
11
 $(INCDIR)/ap_alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \
12
 $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \
13
 $(INCDIR)/http_core.h $(INCDIR)/http_log.h \
14
 $(INCDIR)/http_main.h $(INCDIR)/http_protocol.h \
15
 $(INCDIR)/util_script.h
(-)mod_evasive/work/mod_evasive/README (+378 lines)
Line 0 Link Here
1
Apache Evasive Maneuvers Module
2
For Apache 1.3 and 2.0
3
Copyright (c) Deep Logic, Inc.
4
Version 1.10 [2005.0117]
5
6
LICENSE
7
8
This program is free software; you can redistribute it and/or
9
modify it under the terms of the GNU General Public License
10
as published by the Free Software Foundation; version 2
11
of the License.
12
13
This program is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
GNU General Public License for more details.
17
18
You should have received a copy of the GNU General Public License
19
along with this program; if not, write to the Free Software
20
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21
22
WHAT IS MOD_EVASIVE ?
23
24
mod_evasive is an evasive maneuvers module for Apache to provide evasive
25
action in the event of an HTTP DoS or DDoS attack or brute force attack.  It 
26
is also designed to be a detection tool, and can be easily configured to talk 
27
to ipchains, firewalls, routers, and etcetera.  
28
29
Detection is performed by creating an internal dynamic hash table of IP 
30
Addresses and URIs, and denying any single IP address from any of the following:
31
32
- Requesting the same page more than a few times per second
33
- Making more than 50 concurrent requests on the same child per second
34
- Making any requests while temporarily blacklisted (on a blocking list)
35
36
This method has worked well in both single-server script attacks as well 
37
as distributed attacks, but just like other evasive tools, is only as 
38
useful to the point of bandwidth and processor consumption (e.g. the
39
amount of bandwidth and processor required to receive/process/respond
40
to invalid requests), which is why it's a good idea to integrate this
41
with your firewalls and routers.
42
43
This module instantiates for each listener individually, and therefore has
44
a built-in cleanup mechanism and scaling capabilities.  Because of this,
45
legitimate requests are rarely ever compromised, only legitimate attacks.  Even
46
a user repeatedly clicking on 'reload' should not be affected unless they do
47
it maliciously.
48
49
Three different module sources have been provided:
50
51
Apache v1.3 API:	mod_evasive.c
52
Apache v2.0 API:	mod_evasive20.c
53
NSAPI (iPlanet):	mod_evasiveNSAPI.c *
54
55
NOTE: mod_evasiveNSAPI is a port submitted by Reine Persson <reiper@rsv.se>
56
      and is not officially supported as part of the mod_evasive project.
57
58
HOW IT WORKS
59
60
A web hit request comes in. The following steps take place:
61
62
- The IP address of the requestor is looked up on the temporary blacklist
63
- The IP address of the requestor and the URI are both hashed into a "key".  
64
  A lookup is performed in the listener's internal hash table to determine 
65
  if the same host has requested this page more than once within the past 
66
  1 second.  
67
- The IP address of the requestor is hashed into a "key".
68
  A lookup is performed in the listerner's internal hash table to determine
69
  if the same host has requested more than 50 objects within the past
70
  second (from the same child).
71
72
If any of the above are true, a 403 response is sent.  This conserves
73
bandwidth and system resources in the event of a DoS attack.  Additionally,
74
a system command and/or an email notification can also be triggered to block
75
all the originating addresses of a DDoS attack. 
76
77
Once a single 403 incident occurs, mod_evasive now blocks the entire IP 
78
address for a period of 10 seconds (configurable).  If the host requests a 
79
page within this period, it is forced to wait even longer.  Since this is 
80
triggered from requesting the same URL multiple times per second, this 
81
again does not affect legitimate users.
82
83
The blacklist can/should be configured to talk to your network's firewalls 
84
and/or routers to push the attack out to the front lines, but this is not 
85
required.
86
87
mod_evasive also performs syslog reporting using daemon.alert.  Messages
88
will look like this:
89
90
Aug  6 17:41:49 elijah mod_evasive[23184]: [ID 801097 daemon.alert] Blacklisting address x.x.x.x: possible attack.
91
92
WHAT IS THIS TOOL USEFUL FOR?
93
94
This tool is *excellent* at fending off request-based DoS attacks or scripted
95
attacks, and brute force attacks. When integrated with firewalls or IP filters,
96
mod_evasive can stand up to even large attacks. Its features will prevent you 
97
from wasting bandwidth or having a few thousand CGI scripts running as a 
98
result of an attack.  
99
100
If you do not have an infrastructure capable of fending off any other types
101
of DoS attacks, chances are this tool will only help you to the point of
102
your total bandwidth or server capacity for sending 403's.  Without a solid
103
infrastructure and address filtering tool in place, a heavy distributed DoS 
104
will most likely still take you offline.  
105
106
HOW TO INSTALL
107
108
APACHE v1.3
109
-----------
110
111
Without DSO Support:
112
113
1. Extract this archive into src/modules in the Apache source tree
114
115
2. Run ./configure --add-module=src/modules/evasive/mod_evasive.c
116
117
3. make, install
118
119
4. Restart Apache 
120
121
With DSO Support, Ensim, or CPanel:
122
123
1. $APACHE_ROOT/bin/apxs -iac mod_evasive.c
124
125
2. Restart Apache
126
127
APACHE v2.0
128
-----------
129
130
1. Extract this archive
131
132
2. Run $APACHE_ROOT/bin/apxs -i -a -c mod_evasive20.c
133
134
3. The module will be built and installed into $APACHE_ROOT/modules, and loaded into your httpd.conf
135
136
4. Restart Apache
137
138
NSAPI
139
SunONE (iPlanet,netscape) Installation
140
--------------------------------------
141
142
Tested on:
143
iPlanet 4.1sp12
144
iPlanet 6.0sp5
145
146
Edit compile script for your environment and compile mod_evasiveNSAPI.c
147
to a shared library.
148
149
CONFIGURATION
150
151
mod_evasive has default options configured, but you may also add the
152
following block to your httpd.conf:
153
154
APACHE v1.3
155
-----------
156
157
<IfModule mod_evasive.c>
158
    DOSHashTableSize    3097
159
    DOSPageCount        2
160
    DOSSiteCount        50
161
    DOSPageInterval     1
162
    DOSSiteInterval     1
163
    DOSBlockingPeriod   10
164
</IfModule>
165
166
APACHE v2.0
167
-----------
168
<IfModule mod_evasive20.c>
169
    DOSHashTableSize    3097
170
    DOSPageCount        2
171
    DOSSiteCount        50
172
    DOSPageInterval     1
173
    DOSSiteInterval     1
174
    DOSBlockingPeriod   10
175
</IfModule>
176
177
Optionally you can also add the following directives:
178
179
    DOSEmailNotify	you@yourdomain.com
180
    DOSSystemCommand	"su - someuser -c '/sbin/... %s ...'"
181
    DOSLogDir		"/var/lock/mod_evasive"
182
183
You will also need to add this line if you are building with dynamic support:
184
185
APACHE v1.3
186
-----------
187
188
AddModule	mod_evasive.c
189
190
APACHE v2.0
191
-----------
192
193
LoadModule evasive20_module modules/mod_evasive20.so
194
195
(This line is already added to your configuration by apxs)
196
197
NSAPI
198
SunONE (iPlanet,Netscape) Configuration
199
--------------------------------------
200
                                                                                
201
Configure iPlanet 4.1
202
---------------------
203
204
Edit obj.conf:
205
                                                                                
206
Init fn="load-modules" funcs="mod_evasive_init,mod_evasive_check" shlib="/opt/ns-4.1/plugins/lib/mod_evasive.sl"
207
                                                                                
208
Init fn="mod_evasive_init" DOSPageCount=2 DOSSiteCount=50 DOSPageInterval=1 DOSSiteInterval=1 DOSBlockingPeriod=10 DOSWhitelist="10.60.0.7,10.65.0.10"
209
                                                                                
210
In the default object:
211
PathCheck fn=mod_evasive_check
212
                                                                                
213
Or an own object
214
<Object name="evasive" ppath="/DoSProtectedArea*">
215
NameTrans fn=mod_evasive_check
216
</Object>
217
                                                                                
218
                                                                                
219
Configure iPlanet 6.0
220
---------------------
221
                                                                                
222
Edit magnus.conf:
223
                                                                                
224
Init fn="load-modules" funcs="mod_evasive_init,mod_evasive_check" shlib="/opt/iplanet-6.0/plugins/lib/mod_evasive.sl"
225
                                                                                
226
Init fn="mod_evasive_init" DOSWhitelist="10.60.0.7,10.65.0.10"
227
                                                                                
228
Edit obj.conf:
229
In the default object:
230
PathCheck fn=mod_evasive_check
231
                                                                                
232
Or an own object
233
<Object name="evasive" ppath="/DoSProtectedArea*">
234
NameTrans fn=mod_evasive_check
235
</Object>
236
237
DOSHashTableSize
238
----------------
239
240
The hash table size defines the number of top-level nodes for each child's 
241
hash table.  Increasing this number will provide faster performance by 
242
decreasing the number of iterations required to get to the record, but 
243
consume more memory for table space.  You should increase this if you have
244
a busy web server.  The value you specify will automatically be tiered up to 
245
the next prime number in the primes list (see mod_evasive.c for a list 
246
of primes used).
247
248
DOSPageCount
249
------------
250
251
This is the threshhold for the number of requests for the same page (or URI)
252
per page interval.  Once the threshhold for that interval has been exceeded,
253
the IP address of the client will be added to the blocking list.
254
 
255
DOSSiteCount
256
------------
257
258
This is the threshhold for the total number of requests for any object by
259
the same client on the same listener per site interval.  Once the threshhold 
260
for that interval has been exceeded, the IP address of the client will be added
261
to the blocking list.
262
263
DOSPageInterval
264
---------------
265
266
The interval for the page count threshhold; defaults to 1 second intervals.
267
268
DOSSiteInterval
269
---------------
270
271
The interval for the site count threshhold; defaults to 1 second intervals.
272
273
DOSBlockingPeriod
274
-----------------
275
276
The blocking period is the amount of time (in seconds) that a client will be
277
blocked for if they are added to the blocking list.  During this time, all
278
subsequent requests from the client will result in a 403 (Forbidden) and
279
the timer being reset (e.g. another 10 seconds).  Since the timer is reset
280
for every subsequent request, it is not necessary to have a long blocking
281
period; in the event of a DoS attack, this timer will keep getting reset. 
282
283
DOSEmailNotify
284
--------------
285
286
If this value is set, an email will be sent to the address specified
287
whenever an IP address becomes blacklisted.  A locking mechanism using /tmp
288
prevents continuous emails from being sent.
289
290
NOTE: Be sure MAILER is set correctly in mod_evasive.c 
291
      (or mod_evasive20.c).  The default is "/bin/mail -t %s" where %s is 
292
      used to denote the destination email address set in the configuration.  
293
      If you are running on linux or some other operating system with a 
294
      different type of mailer, you'll need to change this.
295
296
DOSSystemCommand
297
----------------
298
299
If this value is set, the system command specified will be executed
300
whenever an IP address becomes blacklisted.  This is designed to enable
301
system calls to ip filter or other tools.  A locking mechanism using /tmp
302
prevents continuous system calls.  Use %s to denote the IP address of the
303
blacklisted IP.
304
305
DOSLogDir
306
---------
307
308
Choose an alternative temp directory
309
310
By default "/tmp" will be used for locking mechanism, which opens some 
311
security issues if your system is open to shell users.
312
313
  	http://security.lss.hr/index.php?page=details&ID=LSS-2005-01-01
314
315
In the event you have nonprivileged shell users, you'll want to create a
316
directory writable only to the user Apache is running as (usually root),
317
then set this in your httpd.conf.
318
319
WHITELISTING IP ADDRESSES
320
321
IP addresses of trusted clients can be whitelisted to insure they are never 
322
denied.  The purpose of whitelisting is to protect software, scripts, local 
323
searchbots, or other automated tools from being denied for requesting large 
324
amounts of data from the server.  Whitelisting should *not* be used to add 
325
customer lists or anything of the sort, as this will open the server to abuse.
326
This module is very difficult to trigger without performing some type of 
327
malicious attack, and for that reason it is more appropriate to allow the 
328
module to decide on its own whether or not an individual customer should be 
329
blocked.
330
331
To whitelist an address (or range) add an entry to the Apache configuration 
332
in the following fashion:
333
334
DOSWhitelist	127.0.0.1
335
DOSWhitelist	127.0.0.*
336
337
Wildcards can be used on up to the last 3 octets if necessary.  Multiple
338
DOSWhitelist commands may be used in the configuration.
339
340
TWEAKING APACHE
341
342
The keep-alive settings for your children should be reasonable enough to 
343
keep each child up long enough to resist a DOS attack (or at least part of 
344
one).  Remember, it is the child processes that maintain their own internal
345
IP address tables, and so when one exits, so does all of the IP information it
346
had. For every child that exits, another 5-10 copies of the page may get 
347
through before putting the attacker back into '403 Land'.  With this said, 
348
you should have a very high MaxRequestsPerChild, but not unlimited as this
349
will prevent cleanup.
350
351
You'll want to have a MaxRequestsPerChild set to a non-zero value, as
352
DosEvasive cleans up its internal hashes only on exit.  The default
353
MaxRequestsPerChild is usually 10000.  This should suffice in only allowing
354
a few requests per 10000 per child through in the event of an attack (although
355
if you use DOSSystemCommand to firewall the IP address, a hole will no
356
longer be open in between child cycles).
357
358
TESTING
359
360
Want to make sure it's working? Run test.pl, and view the response codes.
361
It's best to run it several times on the same machine as the web server until
362
you get 403 Forbidden messages. Some larger servers with high child counts 
363
may require more of a beating than smaller servers before blacklisting
364
addresses. 
365
366
Please don't use this script to DoS others without their permission.
367
368
KNOWN BUGS
369
370
- This module appears to conflict with the Microsoft Frontpage Extensions.
371
  Frontpage sucks anyway, so if you're using Frontpage I assume you're asking
372
  for problems, and not really interested in conserving server resources anyway.
373
374
FEEDBACK 
375
376
Please email me with questions, constructive comments, or feedback:
377
  jonathan@nuclearelephant.com
378
(-)mod_evasive/work/mod_evasive/mod_evasive.c (+709 lines)
Line 0 Link Here
1
/* $Id: mod_evasive.c,v 1.3 2005/10/08 19:17:14 jonz Exp $ */
2
3
/*
4
mod_evasive for Apache 1.3
5
Copyright (c) by Jonathan A. Zdziarski
6
7
LICENSE
8
9
This program is free software; you can redistribute it and/or
10
modify it under the terms of the GNU General Public License
11
as published by the Free Software Foundation; version 2
12
of the License.
13
14
This program is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
GNU General Public License for more details.
18
19
You should have received a copy of the GNU General Public License
20
along with this program; if not, write to the Free Software
21
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
                                                                                
23
*/
24
25
#include <sys/types.h>
26
#include <sys/socket.h>
27
#include <sys/stat.h>
28
#include <netinet/in.h>
29
#include <arpa/inet.h>
30
#include <string.h>
31
#include <stdlib.h>
32
#include <sys/types.h>
33
#include <time.h>
34
#include <syslog.h>
35
#include <errno.h>
36
37
#include "httpd.h"
38
#include "http_core.h"
39
#include "http_config.h"
40
#include "http_log.h"
41
#include "http_request.h"
42
43
module MODULE_VAR_EXPORT evasive_module;
44
45
/* BEGIN DoS Evasive Maneuvers Definitions */
46
47
#define MAILER	"/bin/mail -t %s"
48
#define  LOG( A, ... ) { openlog("mod_evasive", LOG_PID, LOG_DAEMON); syslog( A, __VA_ARGS__ ); closelog(); }
49
50
#define DEFAULT_HASH_TBL_SIZE   3079ul  // Default hash table size
51
#define DEFAULT_PAGE_COUNT      2       // Default max page hit count/interval
52
#define DEFAULT_SITE_COUNT      50      // Default max site hit count/interval
53
#define DEFAULT_PAGE_INTERVAL   1       // Default 1 second page interval
54
#define DEFAULT_SITE_INTERVAL   1       // Default 1 second site interval
55
#define DEFAULT_BLOCKING_PERIOD 10      // Default block time (Seconds)
56
#define DEFAULT_LOG_DIR		"/tmp"
57
58
/* END DoS Evasive Maneuvers Definitions */
59
60
/* BEGIN NTT (Named Timestamp Tree) Headers */
61
62
enum { ntt_num_primes = 28 };
63
64
/* ntt root tree */
65
struct ntt {
66
    long size;
67
    long items;
68
    struct ntt_node **tbl;
69
};
70
71
/* ntt node (entry in the ntt root tree) */
72
struct ntt_node {
73
    char *key;
74
    time_t timestamp;
75
    long count;
76
    struct ntt_node *next;
77
};
78
79
/* ntt cursor */
80
struct ntt_c {
81
  long iter_index;
82
  struct ntt_node *iter_next;
83
};
84
85
struct ntt *ntt_create(long size);
86
struct ntt_node	*ntt_find(struct ntt *ntt, const char *key);
87
struct ntt_node	*ntt_insert(struct ntt *ntt, const char *key, time_t timestamp);
88
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c);
89
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c);
90
int ntt_destroy(struct ntt *ntt);
91
int ntt_delete(struct ntt *ntt, const char *key);
92
long ntt_hashcode(struct ntt *ntt, const char *key);
93
94
/* END NTT (Named Timestamp Tree) Headers */
95
96
97
/* BEGIN DoS Evasive Maneuvers Globals */
98
99
struct ntt *hit_list;	// Our dynamic hash table
100
struct ntt *white_list = NULL; // White list table
101
102
static unsigned long hash_table_size = DEFAULT_HASH_TBL_SIZE;
103
static int page_count      = DEFAULT_PAGE_COUNT;
104
static int page_interval   = DEFAULT_PAGE_INTERVAL;
105
static int site_count      = DEFAULT_SITE_COUNT;
106
static int site_interval   = DEFAULT_SITE_INTERVAL;
107
static int blocking_period = DEFAULT_BLOCKING_PERIOD;
108
static char *log_dir       = NULL;
109
static char *email_notify  = NULL;
110
static char *sys_command   = NULL;
111
int is_whitelisted(const char *ip);
112
static const char *whitelist(cmd_parms *cmd, void *mconfig, char *ip);
113
114
/* END DoS Evasive Maneuvers Globals */
115
116
static void evasive_child_init(server_rec *s, pool *p)
117
{
118
    hit_list   = ntt_create(hash_table_size);
119
}
120
121
static int check_access(request_rec *r) 
122
{
123
    int ret = OK;
124
125
    /* BEGIN Evasive Maneuvers Code */
126
127
    if (r->prev == NULL && r->main == NULL && hit_list != NULL) {
128
      unsigned long address = r->connection->remote_addr.sin_addr.s_addr;
129
      char *text_add = inet_ntoa(r->connection->remote_addr.sin_addr);
130
      char hash_key[2048];
131
      struct ntt_node *n;
132
      time_t t = time(NULL);
133
134
      /* Check whitelist */
135
       
136
      if (is_whitelisted(text_add))
137
        return OK;
138
139
      /* First see if the IP itself is on "hold" */
140
      snprintf(hash_key, 2048, "%ld", address);
141
      n = ntt_find(hit_list, hash_key);
142
143
      if (n != NULL && t-n->timestamp<blocking_period) {
144
 
145
        /* If the IP is on "hold", make it wait longer in 403 land */
146
        ret = FORBIDDEN;
147
        n->timestamp = time(NULL);
148
149
      /* Not on hold, check hit stats */
150
      } else {
151
152
        /* Has URI been hit too much? */
153
        snprintf(hash_key, 2048, "%ld_%s", address, r->uri);
154
        n = ntt_find(hit_list, hash_key);
155
        if (n != NULL) {
156
157
          /* If URI is being hit too much, add to "hold" list and 403 */
158
          if (t-n->timestamp<page_interval && n->count>=page_count) {
159
            ret = FORBIDDEN;
160
            snprintf(hash_key, 2048, "%ld", address);
161
            ntt_insert(hit_list, hash_key, time(NULL));
162
          } else {
163
164
            /* Reset our hit count list as necessary */
165
            if (t-n->timestamp>=page_interval) {
166
              n->count=0;
167
            }
168
          }
169
          n->timestamp = t;
170
          n->count++;
171
        } else {
172
          ntt_insert(hit_list, hash_key, t);
173
        }
174
175
        /* Has site been hit too much? */
176
        snprintf(hash_key, 2048, "%ld_SITE", address);
177
        n = ntt_find(hit_list, hash_key);
178
        if (n != NULL) {
179
180
          /* If site is being hit too much, add to "hold" list and 403 */
181
          if (t-n->timestamp<site_interval && n->count>=site_count) {
182
            ret = FORBIDDEN;
183
            snprintf(hash_key, 2048, "%ld", address);
184
            ntt_insert(hit_list, hash_key, time(NULL));
185
          } else {
186
187
            /* Reset our hit count list as necessary */
188
            if (t-n->timestamp>=site_interval) {
189
              n->count=0;
190
            }
191
          }
192
          n->timestamp = t;
193
          n->count++;
194
        } else {
195
          ntt_insert(hit_list, hash_key, t);
196
        }
197
      }
198
199
      /* Perform email notification and system functions */
200
      if (ret == FORBIDDEN) {
201
        char filename[1024];
202
        struct stat s;
203
        FILE *file;
204
205
        snprintf(filename, sizeof(filename), "%s/dos-%s", log_dir != NULL ? log_dir : DEFAULT_LOG_DIR, text_add);
206
        if (stat(filename, &s)) {
207
          file = fopen(filename, "w");
208
          if (file != NULL) {
209
            fprintf(file, "%ld\n", getpid());
210
            fclose(file);
211
212
            LOG(LOG_ALERT, "Blacklisting address %s: possible attack.", text_add)
213
            if (email_notify != NULL) {
214
              snprintf(filename, sizeof(filename), MAILER, email_notify);
215
              file = popen(filename, "w");
216
              if (file != NULL) {
217
                fprintf(file, "To: %s\n", email_notify);
218
                fprintf(file, "Subject: HTTP BLACKLIST %s\n\n", text_add);
219
                fprintf(file, "mod_evasive HTTP Blacklisted %s\n", text_add);
220
                pclose(file);
221
              }
222
            }
223
224
            if (sys_command != NULL) {
225
              snprintf(filename, sizeof(filename), sys_command, text_add);
226
              system(filename);
227
            }
228
 
229
          } else {
230
		LOG(LOG_ALERT, "Couldn't open logfile %s: %s",filename, strerror(errno));
231
	  }
232
233
        } /* if (temp file does not exist) */
234
235
      } /* if (ret == FORBIDDEN) */
236
237
    } /* if (r->prev == NULL && r->main == NULL && hit_list != NULL) */
238
239
    /* END Evasive Maneuvers Code */
240
241
    if (ret == FORBIDDEN
242
	&& (ap_satisfies(r) != SATISFY_ANY || !ap_some_auth_required(r))) {
243
	ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
244
		  "client denied by server configuration: %s",
245
		  r->filename);
246
    }
247
248
    return ret;
249
}
250
251
static void evasive_child_exit(server_rec *s, pool *p) 
252
{
253
    ntt_destroy(hit_list);
254
    free(email_notify);
255
    free(sys_command);
256
}
257
258
259
/* BEGIN NTT (Named Timestamp Tree) Functions */
260
261
static unsigned long ntt_prime_list[ntt_num_primes] = 
262
{
263
    53ul,         97ul,         193ul,       389ul,       769ul,
264
    1543ul,       3079ul,       6151ul,      12289ul,     24593ul,
265
    49157ul,      98317ul,      196613ul,    393241ul,    786433ul,
266
    1572869ul,    3145739ul,    6291469ul,   12582917ul,  25165843ul,
267
    50331653ul,   100663319ul,  201326611ul, 402653189ul, 805306457ul,
268
    1610612741ul, 3221225473ul, 4294967291ul
269
};
270
271
272
/* Find the numeric position in the hash table based on key and modulus */
273
274
long ntt_hashcode(struct ntt *ntt, const char *key) {
275
    unsigned long val = 0;
276
    for (; *key; ++key) val = 5 * val + *key;
277
    return(val % ntt->size);
278
}
279
280
/* Creates a single node in the tree */
281
282
struct ntt_node *ntt_node_create(const char *key) {
283
    char *node_key;
284
    struct ntt_node* node;
285
286
    node = (struct ntt_node *) malloc(sizeof(struct ntt_node));
287
    if (node == NULL) {
288
	return NULL;
289
    }
290
    if ((node_key = strdup(key)) == NULL) {
291
        free(node);
292
	return NULL;
293
    }
294
    node->key = node_key;
295
    node->timestamp = time(NULL);
296
    node->next = NULL;
297
    return(node);
298
}
299
300
/* Tree initializer */
301
302
struct ntt *ntt_create(long size) {
303
    long i = 0;
304
    struct ntt *ntt = (struct ntt *) malloc(sizeof(struct ntt));
305
306
    if (ntt == NULL)
307
        return NULL;
308
    while (ntt_prime_list[i] < size) { i++; }
309
    ntt->size  = ntt_prime_list[i];
310
    ntt->items = 0;
311
    ntt->tbl   = (struct ntt_node **) calloc(ntt->size, sizeof(struct ntt_node *));
312
    if (ntt->tbl == NULL) {
313
        free(ntt);
314
        return NULL;
315
    }
316
    return(ntt);
317
}
318
319
/* Find an object in the tree */
320
321
struct ntt_node *ntt_find(struct ntt *ntt, const char *key) {
322
    long hash_code;
323
    struct ntt_node *node;
324
325
    if (ntt == NULL) return NULL;
326
327
    hash_code = ntt_hashcode(ntt, key);
328
    node = ntt->tbl[hash_code];
329
330
    while (node) {
331
        if (!strcmp(key, node->key)) {
332
            return(node);
333
        }
334
        node = node->next;
335
    }
336
    return((struct ntt_node *)NULL);
337
}
338
339
/* Insert a node into the tree */
340
341
struct ntt_node *ntt_insert(struct ntt *ntt, const char *key, time_t timestamp) {
342
    long hash_code;
343
    struct ntt_node *parent;
344
    struct ntt_node *node;
345
    struct ntt_node *new_node = NULL;
346
347
    if (ntt == NULL) return NULL;
348
349
    hash_code = ntt_hashcode(ntt, key);
350
    parent	= NULL;
351
    node	= ntt->tbl[hash_code];
352
353
    while (node != NULL) {
354
        if (strcmp(key, node->key) == 0) { 
355
            new_node = node;
356
            node = NULL;
357
        }
358
359
	if (new_node == NULL) {
360
          parent = node;
361
          node = node->next;
362
        }
363
    }
364
365
    if (new_node != NULL) {
366
        new_node->timestamp = timestamp;
367
        new_node->count = 0;
368
        return new_node; 
369
    }
370
371
    /* Create a new node */
372
    new_node = ntt_node_create(key);
373
    new_node->timestamp = timestamp;
374
    new_node->timestamp = 0;
375
376
    ntt->items++;
377
378
    /* Insert */
379
    if (parent) {  /* Existing parent */
380
	parent->next = new_node;
381
        return new_node;  /* Return the locked node */
382
    }
383
384
    /* No existing parent; add directly to hash table */
385
    ntt->tbl[hash_code] = new_node;
386
    return new_node;
387
}
388
389
/* Tree destructor */
390
391
int ntt_destroy(struct ntt *ntt) {
392
    struct ntt_node *node, *next;
393
    struct ntt_c c;
394
395
    if (ntt == NULL) return -1;
396
397
    node = c_ntt_first(ntt, &c);
398
    while(node != NULL) {
399
        next = c_ntt_next(ntt, &c);
400
        ntt_delete(ntt, node->key);
401
        node = next;
402
    }
403
404
    free(ntt->tbl);
405
    free(ntt);
406
    ntt = (struct ntt *) NULL;
407
408
    return 0;
409
}
410
411
/* Delete a single node in the tree */
412
413
int ntt_delete(struct ntt *ntt, const char *key) {
414
    long hash_code;
415
    struct ntt_node *parent = NULL;
416
    struct ntt_node *node;
417
    struct ntt_node *del_node = NULL;
418
419
    if (ntt == NULL) return -1;
420
421
    hash_code = ntt_hashcode(ntt, key);
422
    node        = ntt->tbl[hash_code];
423
424
    while (node != NULL) {
425
        if (strcmp(key, node->key) == 0) {
426
            del_node = node;
427
            node = NULL;
428
        }
429
430
        if (del_node == NULL) {
431
          parent = node;
432
          node = node->next;
433
        }
434
    }
435
436
    if (del_node != NULL) {
437
438
        if (parent) {
439
            parent->next = del_node->next;
440
        } else {
441
            ntt->tbl[hash_code] = del_node->next;
442
        }
443
444
        free(del_node->key);
445
        free(del_node);
446
        ntt->items--;
447
448
        return 0;
449
    }
450
451
    return -5;
452
}
453
454
/* Point cursor to first item in tree */
455
456
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c) {
457
458
    c->iter_index = 0;
459
    c->iter_next = (struct ntt_node *)NULL;
460
    return(c_ntt_next(ntt, c));
461
}
462
463
/* Point cursor to next iteration in tree */
464
465
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c) {
466
    long index;
467
    struct ntt_node *node = c->iter_next;
468
469
    if (ntt == NULL) return NULL;
470
471
    if (node) {
472
        if (node != NULL) {
473
            c->iter_next = node->next;
474
            return (node);
475
        }
476
    }
477
478
    if (! node) {
479
        while (c->iter_index < ntt->size) {
480
            index = c->iter_index++;
481
482
            if (ntt->tbl[index]) {
483
                c->iter_next = ntt->tbl[index]->next;
484
                return(ntt->tbl[index]);
485
            }
486
        }
487
    }
488
    return((struct ntt_node *)NULL);
489
}
490
491
/* END NTT (Named Pointer Tree) Functions */
492
493
/* BEGIN Configuration Functions */
494
495
static const char *
496
get_hash_tbl_size(cmd_parms *cmd, void *dconfig, char *value) {
497
    long n = strtol(value, NULL, 0);
498
499
    if (n<=0) 
500
        hash_table_size = DEFAULT_HASH_TBL_SIZE;
501
    else 
502
        hash_table_size = n;
503
504
    return NULL;
505
}
506
507
static const char *
508
get_page_count(cmd_parms *cmd, void *dconfig, char *value) {
509
    long n = strtol(value, NULL, 0);
510
    if (n<=0) 
511
        page_count = DEFAULT_PAGE_COUNT;
512
    else
513
        page_count = n;
514
515
    return NULL;
516
}
517
518
static const char *
519
get_site_count(cmd_parms *cmd, void *dconfig, char *value) {
520
    long n = strtol(value, NULL, 0);
521
    if (n<=0) 
522
        site_count = DEFAULT_SITE_COUNT;
523
    else
524
        site_count = n;
525
526
    return NULL;
527
}
528
529
static const char *
530
get_page_interval(cmd_parms *cmd, void *dconfig, char *value) {
531
    long n = strtol(value, NULL, 0);
532
    if (n<=0) 
533
        page_interval = DEFAULT_PAGE_INTERVAL;
534
    else 
535
        page_interval = n;
536
537
    return NULL;
538
}
539
540
static const char *
541
get_site_interval(cmd_parms *cmd, void *dconfig, char *value) {
542
    long n = strtol(value, NULL, 0);
543
    if (n<=0) 
544
        site_interval = DEFAULT_SITE_INTERVAL;
545
    else
546
        site_interval = n;
547
548
  return NULL;
549
}
550
551
static const char *
552
get_blocking_period(cmd_parms *cmd, void *dconfig, char *value) {
553
    long n = strtol(value, NULL, 0);
554
    if (n<=0) 
555
        blocking_period = DEFAULT_BLOCKING_PERIOD;
556
    else 
557
        blocking_period = n;
558
559
    return NULL;
560
}
561
562
static const char *
563
get_log_dir(cmd_parms *cmd, void *dconfig, char *value) {
564
    if (value != NULL && value[0] != 0) {
565
        if (log_dir != NULL)
566
            free(log_dir);
567
        log_dir = strdup(value);
568
    }
569
570
    return NULL;
571
}
572
573
static const char *
574
get_email_notify(cmd_parms *cmd, void *dconfig, char *value) {
575
    if (value != NULL && value[0] != 0) {
576
        if (email_notify != NULL)
577
            free(email_notify);
578
        email_notify = strdup(value);
579
    }
580
581
    return NULL;
582
}
583
584
static const char *
585
get_sys_command(cmd_parms *cmd, void *dconfig, char *value) {
586
    if (value != NULL && value[0] != 0) {
587
        if (sys_command != NULL)
588
            free(sys_command);
589
        sys_command = strdup(value);
590
    }
591
 
592
    return NULL;
593
} 
594
595
static const char *whitelist(cmd_parms *cmd, void *mconfig, char *ip) {
596
    char entry[128];
597
598
    if (white_list == NULL) 
599
        white_list = ntt_create(53ul);
600
    snprintf(entry, sizeof(entry), "%s", ip);
601
    ntt_insert(white_list, entry, time(NULL));
602
603
    return NULL;
604
}
605
606
/* END Configuration Functions */
607
608
int is_whitelisted(const char *ip) {
609
    char hashkey[128];
610
    char octet[4][4];
611
    char *dip;
612
    char *oct;
613
    int i = 0;
614
                                                                                
615
    memset(octet, 0, 16);
616
    dip = strdup(ip);
617
    if (dip == NULL)
618
        return 0;
619
                                                                                
620
    oct = strtok(dip, ".");
621
    while(oct != NULL && i<4) {
622
        if (strlen(oct)<=3)
623
          strcpy(octet[i], oct);
624
        i++;
625
        oct = strtok(NULL, ".");
626
    }
627
    free(dip);
628
                                                                                
629
    /* Exact Match */
630
    snprintf(hashkey, sizeof(hashkey), "%s", ip);
631
    if (ntt_find(white_list, hashkey)!=NULL)
632
        return 1;
633
                                                                                
634
    /* IPv4 Wildcards */
635
    snprintf(hashkey, sizeof(hashkey), "%s.*.*.*", octet[0]);
636
    if (ntt_find(white_list, hashkey)!=NULL)
637
        return 1;
638
                                                                                
639
    snprintf(hashkey, sizeof(hashkey), "%s.%s.*.*", 
640
             octet[0], octet[1]);
641
    if (ntt_find(white_list, hashkey)!=NULL)
642
        return 1;
643
644
    snprintf(hashkey, sizeof(hashkey), "%s.%s.%s.*", 
645
             octet[0], octet[1], octet[2]);
646
    if (ntt_find(white_list, hashkey)!=NULL)
647
        return 1;
648
649
    /* No match */
650
    return 0;
651
}
652
653
static command_rec command_table[] = {
654
655
        { "DOSWhitelist", whitelist, NULL, RSRC_CONF, ITERATE,
656
        "Whitelist an IP or Wildcard. "},
657
658
	{ "DOSHashTableSize", get_hash_tbl_size, NULL, RSRC_CONF, TAKE1,
659
	"Set size of hash table. " },
660
661
	{ "DOSPageCount", get_page_count, NULL, RSRC_CONF, TAKE1,
662
	"Set maximum page hit count per interval. " },
663
664
	{ "DOSSiteCount", get_site_count, NULL, RSRC_CONF, TAKE1,
665
	"Set maximum site hit count per interval. " },
666
667
	{ "DOSPageInterval", get_page_interval, NULL, RSRC_CONF, TAKE1,
668
	"Set page interval. " },
669
670
	{ "DOSSiteInterval", get_site_interval, NULL, RSRC_CONF, TAKE1,
671
	"Set site interval. " }, 
672
673
	{ "DOSLogDir", get_log_dir, NULL, RSRC_CONF, TAKE1,
674
        "Set log dir. "},
675
676
	{ "DOSEmailNotify", get_email_notify, NULL, RSRC_CONF, TAKE1,
677
        "Set email notification. "},
678
679
	{ "DOSSystemCommand", get_sys_command, NULL, RSRC_CONF, TAKE1,
680
        "Set system command. "},
681
682
        { "DOSBlockingPeriod", get_blocking_period, NULL, RSRC_CONF, TAKE1,
683
        "Set blocking period for detected DoS IPs. "},
684
685
	{ NULL }
686
};
687
688
module MODULE_VAR_EXPORT evasive_module = {
689
    STANDARD_MODULE_STUFF,
690
    NULL,                              /* initializer */
691
    NULL,                              /* dir config creator */
692
    NULL,                              /* dir config merger */
693
    NULL,                              /* server config creator */
694
    NULL,                              /* server config merger */
695
    command_table,                     /* command table */
696
    NULL,                              /* handlers */
697
    NULL,                              /* filename translation */
698
    NULL,                              /* check_user_id */
699
    NULL,                              /* check auth */
700
    check_access,                      /* check access */
701
    NULL,                              /* type_checker */
702
    NULL,                              /* fixups */
703
    NULL,                              /* logger */
704
    NULL,                              /* header parser */
705
    evasive_child_init,                /* child_init */
706
    evasive_child_exit,                /* child_exit */
707
    NULL                               /* post read-request */
708
};
709
(-)mod_evasive/work/mod_evasive/mod_evasive20.c (+699 lines)
Line 0 Link Here
1
/*
2
mod_evasive for Apache 2
3
Copyright (c) by Jonathan A. Zdziarski
4
5
LICENSE
6
                                                                                
7
This program is free software; you can redistribute it and/or
8
modify it under the terms of the GNU General Public License
9
as published by the Free Software Foundation; either version 2
10
of the License, or (at your option) any later version.
11
                                                                                
12
This program is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
GNU General Public License for more details.
16
                                                                                
17
You should have received a copy of the GNU General Public License
18
along with this program; if not, write to the Free Software
19
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20
21
*/
22
23
#include <sys/types.h>
24
#include <sys/socket.h>
25
#include <sys/stat.h>
26
#include <netinet/in.h>
27
#include <arpa/inet.h>
28
#include <string.h>
29
#include <stdlib.h>
30
#include <sys/types.h>
31
#include <time.h>
32
#include <syslog.h>
33
#include <errno.h>
34
35
#include "httpd.h"
36
#include "http_core.h"
37
#include "http_config.h"
38
#include "http_log.h"
39
#include "http_request.h"
40
41
module AP_MODULE_DECLARE_DATA evasive20_module;
42
43
/* BEGIN DoS Evasive Maneuvers Definitions */
44
45
#define MAILER	"/bin/mail %s"
46
#define  LOG( A, ... ) { openlog("mod_evasive", LOG_PID, LOG_DAEMON); syslog( A, __VA_ARGS__ ); closelog(); }
47
48
#define DEFAULT_HASH_TBL_SIZE   3097ul  // Default hash table size
49
#define DEFAULT_PAGE_COUNT      2       // Default maximum page hit count per interval
50
#define DEFAULT_SITE_COUNT      50      // Default maximum site hit count per interval
51
#define DEFAULT_PAGE_INTERVAL   1       // Default 1 Second page interval
52
#define DEFAULT_SITE_INTERVAL   1       // Default 1 Second site interval
53
#define DEFAULT_BLOCKING_PERIOD 10      // Default for Detected IPs; blocked for 10 seconds
54
#define DEFAULT_LOG_DIR		"/tmp"  // Default temp directory
55
56
/* END DoS Evasive Maneuvers Definitions */
57
58
/* BEGIN NTT (Named Timestamp Tree) Headers */
59
60
enum { ntt_num_primes = 28 };
61
62
/* ntt root tree */
63
struct ntt {
64
    long size;
65
    long items;
66
    struct ntt_node **tbl;
67
};
68
69
/* ntt node (entry in the ntt root tree) */
70
struct ntt_node {
71
    char *key;
72
    time_t timestamp;
73
    long count;
74
    struct ntt_node *next;
75
};
76
77
/* ntt cursor */
78
struct ntt_c {
79
  long iter_index;
80
  struct ntt_node *iter_next;
81
};
82
83
struct ntt *ntt_create(long size);
84
int ntt_destroy(struct ntt *ntt);
85
struct ntt_node	*ntt_find(struct ntt *ntt, const char *key);
86
struct ntt_node	*ntt_insert(struct ntt *ntt, const char *key, time_t timestamp);
87
int ntt_delete(struct ntt *ntt, const char *key);
88
long ntt_hashcode(struct ntt *ntt, const char *key);	
89
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c);
90
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c);
91
92
/* END NTT (Named Timestamp Tree) Headers */
93
94
95
/* BEGIN DoS Evasive Maneuvers Globals */
96
97
struct ntt *hit_list;	// Our dynamic hash table
98
99
static unsigned long hash_table_size = DEFAULT_HASH_TBL_SIZE;
100
static int page_count = DEFAULT_PAGE_COUNT;
101
static int page_interval = DEFAULT_PAGE_INTERVAL;
102
static int site_count = DEFAULT_SITE_COUNT;
103
static int site_interval = DEFAULT_SITE_INTERVAL;
104
static int blocking_period = DEFAULT_BLOCKING_PERIOD;
105
static char *email_notify = NULL;
106
static char *log_dir = NULL;
107
static char *system_command = NULL;
108
static const char *whitelist(cmd_parms *cmd, void *dconfig, const char *ip);
109
int is_whitelisted(const char *ip);
110
111
/* END DoS Evasive Maneuvers Globals */
112
113
static void * create_hit_list(apr_pool_t *p, server_rec *s) 
114
{
115
    /* Create a new hit list for this listener */
116
117
    hit_list = ntt_create(hash_table_size);
118
}
119
120
static const char *whitelist(cmd_parms *cmd, void *dconfig, const char *ip)
121
{
122
  char entry[128];
123
  snprintf(entry, sizeof(entry), "WHITELIST_%s", ip);
124
  ntt_insert(hit_list, entry, time(NULL));
125
  
126
  return NULL;
127
}
128
129
130
static int access_checker(request_rec *r) 
131
{
132
    int ret = OK;
133
134
    /* BEGIN DoS Evasive Maneuvers Code */
135
136
    if (r->prev == NULL && r->main == NULL && hit_list != NULL) {
137
      char hash_key[2048];
138
      struct ntt_node *n;
139
      time_t t = time(NULL);
140
141
      /* Check whitelist */
142
      if (is_whitelisted(r->connection->remote_ip)) 
143
        return OK;
144
145
      /* First see if the IP itself is on "hold" */
146
      n = ntt_find(hit_list, r->connection->remote_ip);
147
148
      if (n != NULL && t-n->timestamp<blocking_period) {
149
 
150
        /* If the IP is on "hold", make it wait longer in 403 land */
151
        ret = HTTP_FORBIDDEN;
152
        n->timestamp = time(NULL);
153
154
      /* Not on hold, check hit stats */
155
      } else {
156
157
        /* Has URI been hit too much? */
158
        snprintf(hash_key, 2048, "%s_%s", r->connection->remote_ip, r->uri);
159
        n = ntt_find(hit_list, hash_key);
160
        if (n != NULL) {
161
162
          /* If URI is being hit too much, add to "hold" list and 403 */
163
          if (t-n->timestamp<page_interval && n->count>=page_count) {
164
            ret = HTTP_FORBIDDEN;
165
            ntt_insert(hit_list, r->connection->remote_ip, time(NULL));
166
          } else {
167
168
            /* Reset our hit count list as necessary */
169
            if (t-n->timestamp>=page_interval) {
170
              n->count=0;
171
            }
172
          }
173
          n->timestamp = t;
174
          n->count++;
175
        } else {
176
          ntt_insert(hit_list, hash_key, t);
177
        }
178
179
        /* Has site been hit too much? */
180
        snprintf(hash_key, 2048, "%s_SITE", r->connection->remote_ip);
181
        n = ntt_find(hit_list, hash_key);
182
        if (n != NULL) {
183
184
          /* If site is being hit too much, add to "hold" list and 403 */
185
          if (t-n->timestamp<site_interval && n->count>=site_count) {
186
            ret = HTTP_FORBIDDEN;
187
            ntt_insert(hit_list, r->connection->remote_ip, time(NULL));
188
          } else {
189
190
            /* Reset our hit count list as necessary */
191
            if (t-n->timestamp>=site_interval) {
192
              n->count=0;
193
            }
194
          }
195
          n->timestamp = t;
196
          n->count++;
197
        } else {
198
          ntt_insert(hit_list, hash_key, t);
199
        }
200
      }
201
202
      /* Perform email notification and system functions */
203
      if (ret == HTTP_FORBIDDEN) {
204
        char filename[1024];
205
        struct stat s;
206
        FILE *file;
207
208
        snprintf(filename, sizeof(filename), "%s/dos-%s", log_dir != NULL ? log_dir : DEFAULT_LOG_DIR, r->connection->remote_ip);
209
        if (stat(filename, &s)) {
210
          file = fopen(filename, "w");
211
          if (file != NULL) {
212
            fprintf(file, "%ld\n", getpid());
213
            fclose(file);
214
215
            LOG(LOG_ALERT, "Blacklisting address %s: possible DoS attack.", r->connection->remote_ip);
216
            if (email_notify != NULL) {
217
              snprintf(filename, sizeof(filename), MAILER, email_notify);
218
              file = popen(filename, "w");
219
              if (file != NULL) {
220
                fprintf(file, "To: %s\n", email_notify);
221
                fprintf(file, "Subject: HTTP BLACKLIST %s\n\n", r->connection->remote_ip);
222
                fprintf(file, "mod_evasive HTTP Blacklisted %s\n", r->connection->remote_ip);
223
                pclose(file);
224
              }
225
            }
226
227
            if (system_command != NULL) {
228
              snprintf(filename, sizeof(filename), system_command, r->connection->remote_ip);
229
              system(filename);
230
            }
231
 
232
          } else {
233
            LOG(LOG_ALERT, "Couldn't open logfile %s: %s",filename, strerror(errno));
234
	  }
235
236
        } /* if (temp file does not exist) */
237
238
      } /* if (ret == HTTP_FORBIDDEN) */
239
240
    } /* if (r->prev == NULL && r->main == NULL && hit_list != NULL) */
241
242
    /* END DoS Evasive Maneuvers Code */
243
244
    if (ret == HTTP_FORBIDDEN
245
	&& (ap_satisfies(r) != SATISFY_ANY || !ap_some_auth_required(r))) {
246
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
247
            "client denied by server configuration: %s",
248
            r->filename);
249
    }
250
251
    return ret;
252
}
253
254
int is_whitelisted(const char *ip) {
255
  char hashkey[128];
256
  char octet[4][4];
257
  char *dip;
258
  char *oct;
259
  int i = 0;
260
261
  memset(octet, 0, 16);
262
  dip = strdup(ip);
263
  if (dip == NULL)
264
    return 0;
265
266
  oct = strtok(dip, ".");
267
  while(oct != NULL && i<4) {
268
    if (strlen(oct)<=3) 
269
      strcpy(octet[i], oct);
270
    i++;
271
    oct = strtok(NULL, ".");
272
  }
273
  free(dip);
274
275
  /* Exact Match */
276
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s", ip); 
277
  if (ntt_find(hit_list, hashkey)!=NULL)
278
    return 1;
279
280
  /* IPv4 Wildcards */ 
281
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.*.*.*", octet[0]);
282
  if (ntt_find(hit_list, hashkey)!=NULL)
283
    return 1;
284
285
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.%s.*.*", octet[0], octet[1]);
286
  if (ntt_find(hit_list, hashkey)!=NULL)
287
    return 1;
288
289
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.%s.%s.*", octet[0], octet[1], octet[2]);
290
  if (ntt_find(hit_list, hashkey)!=NULL)
291
    return 1;
292
293
  /* No match */
294
  return 0;
295
}
296
297
static apr_status_t destroy_hit_list(void *not_used) {
298
  ntt_destroy(hit_list);
299
  free(email_notify);
300
  free(system_command);
301
}
302
303
304
/* BEGIN NTT (Named Timestamp Tree) Functions */
305
306
static unsigned long ntt_prime_list[ntt_num_primes] = 
307
{
308
    53ul,         97ul,         193ul,       389ul,       769ul,
309
    1543ul,       3079ul,       6151ul,      12289ul,     24593ul,
310
    49157ul,      98317ul,      196613ul,    393241ul,    786433ul,
311
    1572869ul,    3145739ul,    6291469ul,   12582917ul,  25165843ul,
312
    50331653ul,   100663319ul,  201326611ul, 402653189ul, 805306457ul,
313
    1610612741ul, 3221225473ul, 4294967291ul
314
};
315
316
317
/* Find the numeric position in the hash table based on key and modulus */
318
319
long ntt_hashcode(struct ntt *ntt, const char *key) {
320
    unsigned long val = 0;
321
    for (; *key; ++key) val = 5 * val + *key;
322
    return(val % ntt->size);
323
}
324
325
/* Creates a single node in the tree */
326
327
struct ntt_node *ntt_node_create(const char *key) {
328
    char *node_key;
329
    struct ntt_node* node;
330
331
    node = (struct ntt_node *) malloc(sizeof(struct ntt_node));
332
    if (node == NULL) {
333
	return NULL;
334
    }
335
    if ((node_key = strdup(key)) == NULL) {
336
        free(node);
337
	return NULL;
338
    }
339
    node->key = node_key;
340
    node->timestamp = time(NULL);
341
    node->next = NULL;
342
    return(node);
343
}
344
345
/* Tree initializer */
346
347
struct ntt *ntt_create(long size) {
348
    long i = 0;
349
    struct ntt *ntt = (struct ntt *) malloc(sizeof(struct ntt));
350
351
    if (ntt == NULL)
352
        return NULL;
353
    while (ntt_prime_list[i] < size) { i++; }
354
    ntt->size  = ntt_prime_list[i];
355
    ntt->items = 0;
356
    ntt->tbl   = (struct ntt_node **) calloc(ntt->size, sizeof(struct ntt_node *));
357
    if (ntt->tbl == NULL) {
358
        free(ntt);
359
        return NULL;
360
    }
361
    return(ntt);
362
}
363
364
/* Find an object in the tree */
365
366
struct ntt_node *ntt_find(struct ntt *ntt, const char *key) {
367
    long hash_code;
368
    struct ntt_node *node;
369
370
    if (ntt == NULL) return NULL;
371
372
    hash_code = ntt_hashcode(ntt, key);
373
    node = ntt->tbl[hash_code];
374
375
    while (node) {
376
        if (!strcmp(key, node->key)) {
377
            return(node);
378
        }
379
        node = node->next;
380
    }
381
    return((struct ntt_node *)NULL);
382
}
383
384
/* Insert a node into the tree */
385
386
struct ntt_node *ntt_insert(struct ntt *ntt, const char *key, time_t timestamp) {
387
    long hash_code;
388
    struct ntt_node *parent;
389
    struct ntt_node *node;
390
    struct ntt_node *new_node = NULL;
391
392
    if (ntt == NULL) return NULL;
393
394
    hash_code = ntt_hashcode(ntt, key);
395
    parent	= NULL;
396
    node	= ntt->tbl[hash_code];
397
398
    while (node != NULL) {
399
        if (strcmp(key, node->key) == 0) { 
400
            new_node = node;
401
            node = NULL;
402
        }
403
404
	if (new_node == NULL) {
405
          parent = node;
406
          node = node->next;
407
        }
408
    }
409
410
    if (new_node != NULL) {
411
        new_node->timestamp = timestamp;
412
        new_node->count = 0;
413
        return new_node; 
414
    }
415
416
    /* Create a new node */
417
    new_node = ntt_node_create(key);
418
    new_node->timestamp = timestamp;
419
    new_node->timestamp = 0;
420
421
    ntt->items++;
422
423
    /* Insert */
424
    if (parent) {  /* Existing parent */
425
	parent->next = new_node;
426
        return new_node;  /* Return the locked node */
427
    }
428
429
    /* No existing parent; add directly to hash table */
430
    ntt->tbl[hash_code] = new_node;
431
    return new_node;
432
}
433
434
/* Tree destructor */
435
436
int ntt_destroy(struct ntt *ntt) {
437
    struct ntt_node *node, *next;
438
    struct ntt_c c;
439
440
    if (ntt == NULL) return -1;
441
442
    node = c_ntt_first(ntt, &c);
443
    while(node != NULL) {
444
        next = c_ntt_next(ntt, &c);
445
        ntt_delete(ntt, node->key);
446
        node = next;
447
    }
448
449
    free(ntt->tbl);
450
    free(ntt);
451
    ntt = (struct ntt *) NULL;
452
453
    return 0;
454
}
455
456
/* Delete a single node in the tree */
457
458
int ntt_delete(struct ntt *ntt, const char *key) {
459
    long hash_code;
460
    struct ntt_node *parent = NULL;
461
    struct ntt_node *node;
462
    struct ntt_node *del_node = NULL;
463
464
    if (ntt == NULL) return -1;
465
466
    hash_code = ntt_hashcode(ntt, key);
467
    node        = ntt->tbl[hash_code];
468
469
    while (node != NULL) {
470
        if (strcmp(key, node->key) == 0) {
471
            del_node = node;
472
            node = NULL;
473
        }
474
475
        if (del_node == NULL) {
476
          parent = node;
477
          node = node->next;
478
        }
479
    }
480
481
    if (del_node != NULL) {
482
483
        if (parent) {
484
            parent->next = del_node->next;
485
        } else {
486
            ntt->tbl[hash_code] = del_node->next;
487
        }
488
489
        free(del_node->key);
490
        free(del_node);
491
        ntt->items--;
492
493
        return 0;
494
    }
495
496
    return -5;
497
}
498
499
/* Point cursor to first item in tree */
500
501
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c) {
502
503
    c->iter_index = 0;
504
    c->iter_next = (struct ntt_node *)NULL;
505
    return(c_ntt_next(ntt, c));
506
}
507
508
/* Point cursor to next iteration in tree */
509
510
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c) {
511
    long index;
512
    struct ntt_node *node = c->iter_next;
513
514
    if (ntt == NULL) return NULL;
515
516
    if (node) {
517
        if (node != NULL) {
518
            c->iter_next = node->next;
519
            return (node);
520
        }
521
    }
522
523
    if (! node) {
524
        while (c->iter_index < ntt->size) {
525
            index = c->iter_index++;
526
527
            if (ntt->tbl[index]) {
528
                c->iter_next = ntt->tbl[index]->next;
529
                return(ntt->tbl[index]);
530
            }
531
        }
532
    }
533
    return((struct ntt_node *)NULL);
534
}
535
536
/* END NTT (Named Pointer Tree) Functions */
537
538
539
/* BEGIN Configuration Functions */
540
541
static const char *
542
get_hash_tbl_size(cmd_parms *cmd, void *dconfig, const char *value) {
543
  long n = strtol(value, NULL, 0);
544
545
  if (n<=0) {
546
    hash_table_size = DEFAULT_HASH_TBL_SIZE;
547
  } else  {
548
    hash_table_size = n;
549
  }
550
551
  return NULL;
552
}
553
554
static const char *
555
get_page_count(cmd_parms *cmd, void *dconfig, const char *value) {
556
  long n = strtol(value, NULL, 0);
557
  if (n<=0) {
558
    page_count = DEFAULT_PAGE_COUNT;
559
  } else {
560
    page_count = n;
561
  }
562
563
  return NULL;
564
}
565
566
static const char *
567
get_site_count(cmd_parms *cmd, void *dconfig, const char *value) {
568
  long n = strtol(value, NULL, 0);
569
  if (n<=0) {
570
    site_count = DEFAULT_SITE_COUNT;
571
  } else {
572
    site_count = n;
573
  }
574
575
  return NULL;
576
}
577
578
static const char *
579
get_page_interval(cmd_parms *cmd, void *dconfig, const char *value) {
580
  long n = strtol(value, NULL, 0);
581
  if (n<=0) {
582
    page_interval = DEFAULT_PAGE_INTERVAL;
583
  } else {
584
    page_interval = n;
585
  }
586
587
  return NULL;
588
}
589
590
static const char *
591
get_site_interval(cmd_parms *cmd, void *dconfig, const char *value) {
592
  long n = strtol(value, NULL, 0);
593
  if (n<=0) {
594
    site_interval = DEFAULT_SITE_INTERVAL;
595
  } else {
596
    site_interval = n;
597
  }
598
599
  return NULL;
600
}
601
602
static const char *
603
get_blocking_period(cmd_parms *cmd, void *dconfig, const char *value) {
604
  long n = strtol(value, NULL, 0);
605
  if (n<=0) {
606
    blocking_period = DEFAULT_BLOCKING_PERIOD;
607
  } else {
608
    blocking_period = n;
609
  }
610
611
  return NULL;
612
}
613
614
static const char *
615
get_log_dir(cmd_parms *cmd, void *dconfig, const char *value) {
616
  if (value != NULL && value[0] != 0) {
617
    if (log_dir != NULL)
618
      free(log_dir);
619
    log_dir = strdup(value);
620
  }
621
622
  return NULL;
623
}
624
625
static const char *
626
get_email_notify(cmd_parms *cmd, void *dconfig, const char *value) {
627
  if (value != NULL && value[0] != 0) {
628
    if (email_notify != NULL)
629
      free(email_notify);
630
    email_notify = strdup(value);
631
  }
632
633
  return NULL;
634
}
635
636
static const char *
637
get_system_command(cmd_parms *cmd, void *dconfig, const char *value) {
638
  if (value != NULL && value[0] != 0) {
639
    if (system_command != NULL)
640
      free(system_command);
641
    system_command = strdup(value);
642
  }
643
 
644
  return NULL;
645
} 
646
647
/* END Configuration Functions */
648
649
static const command_rec access_cmds[] =
650
{
651
	AP_INIT_TAKE1("DOSHashTableSize", get_hash_tbl_size, NULL, RSRC_CONF, 
652
		"Set size of hash table"),
653
654
        AP_INIT_TAKE1("DOSPageCount", get_page_count, NULL, RSRC_CONF,
655
		"Set maximum page hit count per interval"),
656
657
        AP_INIT_TAKE1("DOSSiteCount", get_site_count, NULL, RSRC_CONF,
658
		"Set maximum site hit count per interval"),
659
660
        AP_INIT_TAKE1("DOSPageInterval", get_page_interval, NULL, RSRC_CONF,
661
		"Set page interval"),
662
663
	AP_INIT_TAKE1("DOSSiteInterval", get_site_interval, NULL, RSRC_CONF,
664
		"Set site interval"),
665
666
        AP_INIT_TAKE1("DOSBlockingPeriod", get_blocking_period, NULL, RSRC_CONF,
667
		"Set blocking period for detected DoS IPs"),
668
669
	AP_INIT_TAKE1("DOSEmailNotify", get_email_notify, NULL, RSRC_CONF,
670
		"Set email notification"),
671
672
	AP_INIT_TAKE1("DOSLogDir", get_log_dir, NULL, RSRC_CONF,
673
		"Set log dir"),
674
675
	AP_INIT_TAKE1("DOSSystemCommand", get_system_command, NULL, RSRC_CONF,
676
		"Set system command on DoS"),
677
678
        AP_INIT_ITERATE("DOSWhitelist", whitelist, NULL, RSRC_CONF,
679
                "IP-addresses wildcards to whitelist"),
680
681
	{ NULL }
682
};
683
684
static void register_hooks(apr_pool_t *p) {
685
  ap_hook_access_checker(access_checker, NULL, NULL, APR_HOOK_MIDDLE);
686
  apr_pool_cleanup_register(p, NULL, apr_pool_cleanup_null, destroy_hit_list);
687
};
688
689
module AP_MODULE_DECLARE_DATA evasive20_module =
690
{
691
    STANDARD20_MODULE_STUFF,
692
    NULL,
693
    NULL,
694
    create_hit_list,
695
    NULL,
696
    access_cmds,
697
    register_hooks
698
};
699
(-)mod_evasive/work/mod_evasive/mod_evasiveNSAPI.c (+608 lines)
Line 0 Link Here
1
/*
2
mod_dosevasive/1.8 for NSAPI
3
 
4
Copyright 2002 by Jonathan A. Zdziarski.  All rights reserved.
5
 
6
LICENSE
7
-------
8
 
9
This distribution may be freely distributed in its original form.  
10
License is granted to make modifications to the source for internal,
11
private use only, provided you retain this notice, disclaimers, author's
12
copyright, and credits.
13
 
14
 
15
DISCLAIMER
16
----------
17
 
18
THIS SOFTWARE IS PROVIDE "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
19
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO WAY SHALL THE
21
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
27
OF THE POSSIBILITY OF SUCH DAMAGE.
28
 
29
*/
30
31
/* This is a port to NSAPI from mod_dosevasive/1.8 for Apache 2.0 
32
   2003-10-29 Reine Persson
33
*/
34
35
#include <sys/types.h>
36
#include <sys/socket.h>
37
#include <sys/stat.h>
38
#include <netinet/in.h>
39
#include <arpa/inet.h>
40
#include <string.h>
41
#include <stdlib.h>
42
#include <sys/types.h>
43
#include <time.h>
44
#include <syslog.h>
45
#include <errno.h>
46
47
#include <nsapi.h>
48
 
49
50
/* BEGIN DoS Evasive Maneuvers Definitions */
51
52
#define DEFAULT_LOG_DIR "/tmp"
53
#define MAILER	"/bin/mail %s"
54
#define  LOG( A, ... ) { openlog("mod_dosevasive", LOG_PID, LOG_DAEMON); syslog( A, __VA_ARGS__ ); closelog(); }
55
56
#define DEFAULT_HASH_TBL_SIZE   3097ul  // Default hash table size
57
#define DEFAULT_PAGE_COUNT      2       // Default maximum page hit count per interval
58
#define DEFAULT_SITE_COUNT      50      // Default maximum site hit count per interval
59
#define DEFAULT_PAGE_INTERVAL   1       // Default 1 Second page interval
60
#define DEFAULT_SITE_INTERVAL   1       // Default 1 Second site interval
61
#define DEFAULT_BLOCKING_PERIOD 10      // Default for Detected IPs; blocked for 10 seconds
62
63
/* END DoS Evasive Maneuvers Definitions */
64
65
static CRITICAL mod_dosevasive_crit;
66
67
/* BEGIN NTT (Named Timestamp Tree) Headers */
68
69
enum { ntt_num_primes = 28 };
70
71
/* ntt root tree */
72
struct ntt {
73
    long size;
74
    long items;
75
    struct ntt_node **tbl;
76
};
77
78
/* ntt node (entry in the ntt root tree) */
79
struct ntt_node {
80
    char *key;
81
    time_t timestamp;
82
    long count;
83
    struct ntt_node *next;
84
};
85
86
/* ntt cursor */
87
struct ntt_c {
88
  long iter_index;
89
  struct ntt_node *iter_next;
90
};
91
92
struct ntt *ntt_create(long size);
93
int ntt_destroy(struct ntt *ntt);
94
struct ntt_node	*ntt_find(struct ntt *ntt, const char *key);
95
struct ntt_node	*ntt_insert(struct ntt *ntt, const char *key, time_t timestamp);
96
int ntt_delete(struct ntt *ntt, const char *key);
97
long ntt_hashcode(struct ntt *ntt, const char *key);	
98
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c);
99
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c);
100
101
/* END NTT (Named Timestamp Tree) Headers */
102
103
104
/* BEGIN DoS Evasive Maneuvers Globals */
105
106
struct ntt *hit_list;	// Our dynamic hash table
107
108
static unsigned long hash_table_size = DEFAULT_HASH_TBL_SIZE;
109
static int page_count = DEFAULT_PAGE_COUNT;
110
static int page_interval = DEFAULT_PAGE_INTERVAL;
111
static int site_count = DEFAULT_SITE_COUNT;
112
static int site_interval = DEFAULT_SITE_INTERVAL;
113
static int blocking_period = DEFAULT_BLOCKING_PERIOD;
114
static char *log_dir = NULL;
115
static char *email_notify = NULL;
116
static char *system_command = NULL;
117
118
static const char * whitelist(const char *ip);
119
int is_whitelisted(const char *ip);
120
static int destroy_hit_list(void *not_used);
121
122
/* END DoS Evasive Maneuvers Globals */
123
124
static char *
125
itemize(char *str,char delim)
126
{
127
    static char *nextitem = NULL;
128
    char *result;
129
 
130
    if(str)
131
        nextitem=str;
132
    if(!nextitem)
133
        return(NULL);
134
    result=nextitem;
135
    while(*nextitem && *nextitem!=delim) 
136
        ++nextitem;
137
    if(*nextitem) 
138
        *nextitem++='\0';
139
    else 
140
        nextitem=NULL;
141
    return(result);
142
}
143
144
 
145
NSAPI_PUBLIC int
146
mod_dosevasive_init(pblock *pb, Session *sn, Request *rq)
147
{
148
  char *ip,*stmp,*white_list=NULL;
149
  int itmp;
150
151
  mod_dosevasive_crit = crit_init();
152
  if ((itmp=atoi(pblock_findval("DOSHashTableSize", pb))) != 0 )
153
    hash_table_size=itmp;
154
  if ((itmp=atoi(pblock_findval("DOSPageCount", pb))) != 0 )
155
    page_count=itmp;
156
  if ((itmp=atoi(pblock_findval("DOSSiteCount", pb))) != 0 )
157
    site_count=itmp;
158
  if ((itmp=atoi(pblock_findval("DOSPageInterval", pb))) != 0 )
159
    page_interval=itmp;
160
  if ((itmp=atoi(pblock_findval("DOSSiteInterval", pb))) != 0 )
161
    site_interval=itmp;
162
  if ((itmp=atoi(pblock_findval("DOSBlockingPeriod", pb))) != 0 )
163
    blocking_period=itmp;
164
  if ((stmp=pblock_findval("DOSLogDir", pb)) != NULL )
165
    log_dir=stmp;
166
  if ((stmp=pblock_findval("DOSEmailNotify", pb)) != NULL )
167
    email_notify=stmp;
168
  if ((stmp=pblock_findval("DOSSystemCommand", pb)) != NULL )
169
    system_command=stmp;
170
171
  white_list=pblock_findval("DOSWhitelist", pb);
172
173
  hit_list = ntt_create(hash_table_size);
174
175
  if ( white_list != NULL ) {
176
    ip=itemize(white_list,',');
177
    while( ip != NULL ) {
178
      whitelist(ip);
179
      ip=itemize(NULL,',');
180
    }
181
  }
182
  return REQ_PROCEED;
183
}
184
 
185
NSAPI_PUBLIC int
186
mod_dosevasive_check(pblock *pb, Session *sn, Request *rq)
187
{
188
  int ret = REQ_PROCEED;
189
190
  /* BEGIN DoS Evasive Maneuvers Code */
191
  
192
  if (pblock_findval("NS_original_uri",rq->vars) == NULL && pblock_findval("referer",rq->headers) == NULL && hit_list != NULL) {
193
    char hash_key[2048];
194
    struct ntt_node *n;
195
    time_t t = time(NULL);
196
197
    /* Check whitelist */
198
    if (is_whitelisted(pblock_findval("ip",sn->client))) 
199
      return REQ_PROCEED;
200
    
201
    /* First see if the IP itself is on "hold" */
202
    n = ntt_find(hit_list, pblock_findval("ip",sn->client));
203
    
204
    if (n != NULL && t-n->timestamp<blocking_period) {
205
      
206
      /* If the IP is on "hold", make it wait longer in 403 land */
207
      ret = REQ_ABORTED;
208
      n->timestamp = time(NULL);
209
      
210
      /* Not on hold, check hit stats */
211
    } else {
212
      
213
      /* Has URI been hit too much? */
214
      snprintf(hash_key, 2048, "%s_%s", pblock_findval("ip",sn->client), pblock_findval("uri",rq->reqpb));
215
      n = ntt_find(hit_list, hash_key);
216
      if (n != NULL) {
217
	
218
	/* If URI is being hit too much, add to "hold" list and 403 */
219
	if (t-n->timestamp<page_interval && n->count>=page_count) {
220
	  ret = REQ_ABORTED;
221
	  ntt_insert(hit_list, pblock_findval("ip",sn->client), time(NULL));
222
	} else {
223
	  
224
	  /* Reset our hit count list as necessary */
225
	  if (t-n->timestamp>=page_interval) {
226
	    n->count=0;
227
	  }
228
	}
229
	n->timestamp = t;
230
	n->count++;
231
      } else {
232
	
233
	ntt_insert(hit_list, hash_key, t);
234
      }
235
      
236
      /* Has site been hit too much? */
237
      snprintf(hash_key, 2048, "%s_SITE", pblock_findval("ip",sn->client));
238
      n = ntt_find(hit_list, hash_key);
239
      if (n != NULL) {
240
	
241
	/* If site is being hit too much, add to "hold" list and 403 */
242
	if (t-n->timestamp<site_interval && n->count>=site_count) {
243
	  ret = REQ_ABORTED;
244
	  ntt_insert(hit_list, pblock_findval("ip",sn->client), time(NULL));
245
	} else {
246
	  
247
	  /* Reset our hit count list as necessary */
248
	  if (t-n->timestamp>=site_interval) {
249
	    n->count=0;
250
	  }
251
	}
252
	n->timestamp = t;
253
	n->count++;
254
      } else {
255
	ntt_insert(hit_list, hash_key, t);
256
      }
257
    }
258
    
259
    /* Perform email notification and system functions */
260
    if (ret == REQ_ABORTED) {
261
      char filename[1024];
262
      struct stat s;
263
      FILE *file;
264
      
265
      snprintf(filename, sizeof(filename), "%s/dos-%s", log_dir != NULL ? log_dir : DEFAULT_LOG_DIR, pblock_findval("ip",sn->client));
266
      if (stat(filename, &s)) {
267
	file = fopen(filename, "w");
268
	if (file != NULL) {
269
	  fprintf(file, "%ld\n", getpid());
270
	  fclose(file);
271
	  
272
	  LOG(LOG_ALERT, "Blacklisting address %s: possible DoS attack.",pblock_findval("ip",sn->client));
273
	  if (email_notify != NULL) {
274
	    snprintf(filename, sizeof(filename), MAILER, email_notify);
275
	    file = popen(filename, "w");
276
	    if (file != NULL) {
277
	      fprintf(file, "To: %s\n", email_notify);
278
	      fprintf(file, "Subject: HTTP BLACKLIST %s\n\n", pblock_findval("ip",sn->client));
279
	      fprintf(file, "mod_dosevasive HTTP Blacklisted %s\n", pblock_findval("ip",sn->client));
280
	      pclose(file);
281
	    } else {
282
	      LOG(LOG_ALERT, "Couldn't open logfile %s: %s",filename, strerror(errno));
283
	    }
284
	  }
285
	  
286
	  if (system_command != NULL) {
287
	    snprintf(filename, sizeof(filename), system_command, pblock_findval("ip",sn->client));
288
	    system(filename);
289
	  }
290
	  
291
	}
292
	
293
      } /* if (temp file does not exist) */
294
      
295
    } /* if (ret == REQ_ABORTED) */
296
    
297
  } /* if (vars->NS_Original_uri == NULL && headers->referer == NULL && hit_list != NULL) */
298
  
299
  /* END DoS Evasive Maneuvers Code */
300
  
301
  if (ret == REQ_ABORTED ) {
302
    log_error(LOG_SECURITY,"mod_dosevasive_check",sn,rq,"client denied by server configuration: %s",pblock_findval("uri",rq->reqpb));
303
    protocol_status(sn, rq, PROTOCOL_FORBIDDEN, NULL);
304
  }
305
  return ret;
306
}
307
308
309
static const char *whitelist(const char *ip)
310
{
311
  char entry[128];
312
  snprintf(entry, sizeof(entry), "WHITELIST_%s", ip);
313
  ntt_insert(hit_list, entry, time(NULL));
314
  
315
  return NULL;
316
}
317
318
319
int is_whitelisted(const char *ip) {
320
  char hashkey[128];
321
  char octet[4][4];
322
  char *dip;
323
  char *oct;
324
  int i = 0;
325
  
326
  memset(octet, 0, 16);
327
  dip = strdup(ip);
328
  if (dip == NULL)
329
    return 0;
330
  
331
  oct = strtok(dip, ".");
332
  while(oct != NULL && i<4) {
333
    if (strlen(oct)<=3) 
334
      strcpy(octet[i], oct);
335
    i++;
336
    oct = strtok(NULL, ".");
337
  }
338
  free(dip);
339
  
340
  /* Exact Match */
341
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s", ip); 
342
  if (ntt_find(hit_list, hashkey)!=NULL)
343
    return 1;
344
  
345
  /* IPv4 Wildcards */ 
346
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.*.*.*", octet[0]);
347
  if (ntt_find(hit_list, hashkey)!=NULL)
348
    return 1;
349
350
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.%s.*.*", octet[0], octet[1]);
351
  if (ntt_find(hit_list, hashkey)!=NULL)
352
    return 1;
353
  
354
  snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.%s.%s.*", octet[0], octet[1], octet[2]);
355
  if (ntt_find(hit_list, hashkey)!=NULL)
356
    return 1;
357
  
358
  /* No match */
359
  return 0;
360
}
361
362
static int destroy_hit_list(void *not_used) {
363
  ntt_destroy(hit_list);
364
  free(log_dir);
365
  free(email_notify);
366
  free(system_command);
367
  return 0;
368
}
369
370
371
/* BEGIN NTT (Named Timestamp Tree) Functions */
372
373
static unsigned long ntt_prime_list[ntt_num_primes] = 
374
{
375
  53ul,         97ul,         193ul,       389ul,       769ul,
376
  1543ul,       3079ul,       6151ul,      12289ul,     24593ul,
377
  49157ul,      98317ul,      196613ul,    393241ul,    786433ul,
378
  1572869ul,    3145739ul,    6291469ul,   12582917ul,  25165843ul,
379
  50331653ul,   100663319ul,  201326611ul, 402653189ul, 805306457ul,
380
  1610612741ul, 3221225473ul, 4294967291ul
381
};
382
383
384
/* Find the numeric position in the hash table based on key and modulus */
385
386
long ntt_hashcode(struct ntt *ntt, const char *key) {
387
  unsigned long val = 0;
388
  for (; *key; ++key) val = 5 * val + *key;
389
  return(val % ntt->size);
390
}
391
392
/* Creates a single node in the tree */
393
394
struct ntt_node *ntt_node_create(const char *key) {
395
  char *node_key;
396
  struct ntt_node* node;
397
  
398
  node = (struct ntt_node *) malloc(sizeof(struct ntt_node));
399
  if (node == NULL) {
400
    return NULL;
401
  }
402
  if ((node_key = strdup(key)) == NULL) {
403
    free(node);
404
    return NULL;
405
  }
406
  node->key = node_key;
407
  node->timestamp = time(NULL);
408
  node->next = NULL;
409
  return(node);
410
}
411
412
/* Tree initializer */
413
414
struct ntt *ntt_create(long size) {
415
  long i = 0;
416
  struct ntt *ntt = (struct ntt *) malloc(sizeof(struct ntt));
417
  
418
  if (ntt == NULL)
419
    return NULL;
420
  while (ntt_prime_list[i] < size) { i++; }
421
  ntt->size  = ntt_prime_list[i];
422
  ntt->items = 0;
423
  ntt->tbl   = (struct ntt_node **) calloc(ntt->size, sizeof(struct ntt_node *));
424
  if (ntt->tbl == NULL) {
425
        free(ntt);
426
        return NULL;
427
  }
428
  return(ntt);
429
}
430
431
/* Find an object in the tree */
432
433
struct ntt_node *ntt_find(struct ntt *ntt, const char *key) {
434
  long hash_code;
435
  struct ntt_node *node;
436
  
437
  if (ntt == NULL) return NULL;
438
  
439
  hash_code = ntt_hashcode(ntt, key);
440
  node = ntt->tbl[hash_code];
441
  
442
  while (node) {
443
    if (!strcmp(key, node->key)) {
444
      return(node);
445
    }
446
    node = node->next;
447
  }
448
  return((struct ntt_node *)NULL);
449
}
450
451
/* Insert a node into the tree */
452
453
struct ntt_node *ntt_insert(struct ntt *ntt, const char *key, time_t timestamp) {
454
  long hash_code;
455
  struct ntt_node *parent;
456
  struct ntt_node *node;
457
  struct ntt_node *new_node = NULL;
458
  
459
  if (ntt == NULL) return NULL;
460
  
461
  hash_code = ntt_hashcode(ntt, key);
462
  parent	= NULL;
463
  node	= ntt->tbl[hash_code];
464
  
465
  crit_enter(mod_dosevasive_crit); /*Lock*/
466
467
  while (node != NULL) {
468
    if (strcmp(key, node->key) == 0) { 
469
      new_node = node;
470
      node = NULL;
471
    }
472
    
473
    if (new_node == NULL) {
474
      parent = node;
475
      node = node->next;
476
    }
477
  }
478
  
479
  if (new_node != NULL) {
480
    new_node->timestamp = timestamp;
481
    new_node->count = 0;
482
    crit_exit(mod_dosevasive_crit); /*Unlock*/
483
    return new_node; 
484
  }
485
  
486
  /* Create a new node */
487
  new_node = ntt_node_create(key);
488
  new_node->timestamp = timestamp;
489
  new_node->timestamp = 0;
490
  
491
  ntt->items++;
492
  
493
  /* Insert */
494
  if (parent) {  /* Existing parent */
495
    parent->next = new_node;
496
    crit_exit(mod_dosevasive_crit); /*Unlock*/
497
    return new_node;  /* Return the locked node */
498
  }
499
  
500
  /* No existing parent; add directly to hash table */
501
  ntt->tbl[hash_code] = new_node;
502
  crit_exit(mod_dosevasive_crit); /*Unlock*/
503
  return new_node;
504
}
505
506
/* Tree destructor */
507
508
int ntt_destroy(struct ntt *ntt) {
509
  struct ntt_node *node, *next;
510
  struct ntt_c c;
511
  
512
  if (ntt == NULL) return -1;
513
  
514
  node = c_ntt_first(ntt, &c);
515
  while(node != NULL) {
516
    next = c_ntt_next(ntt, &c);
517
    ntt_delete(ntt, node->key);
518
    node = next;
519
  }
520
  
521
  free(ntt->tbl);
522
  free(ntt);
523
  ntt = (struct ntt *) NULL;
524
  
525
  return 0;
526
}
527
528
/* Delete a single node in the tree */
529
530
int ntt_delete(struct ntt *ntt, const char *key) {
531
  long hash_code;
532
  struct ntt_node *parent = NULL;
533
  struct ntt_node *node;
534
  struct ntt_node *del_node = NULL;
535
  
536
  if (ntt == NULL) return -1;
537
  
538
  hash_code = ntt_hashcode(ntt, key);
539
  node        = ntt->tbl[hash_code];
540
  
541
  while (node != NULL) {
542
    if (strcmp(key, node->key) == 0) {
543
      del_node = node;
544
      node = NULL;
545
    }
546
    
547
    if (del_node == NULL) {
548
      parent = node;
549
      node = node->next;
550
    }
551
  }
552
  
553
  if (del_node != NULL) {
554
    
555
    if (parent) {
556
      parent->next = del_node->next;
557
    } else {
558
      ntt->tbl[hash_code] = del_node->next;
559
    }
560
    
561
    free(del_node->key);
562
    free(del_node);
563
    ntt->items--;
564
    
565
    return 0;
566
  }
567
  
568
  return -5;
569
}
570
571
/* Point cursor to first item in tree */
572
573
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c) {
574
  
575
  c->iter_index = 0;
576
  c->iter_next = (struct ntt_node *)NULL;
577
  return(c_ntt_next(ntt, c));
578
}
579
580
/* Point cursor to next iteration in tree */
581
582
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c) {
583
  long index;
584
  struct ntt_node *node = c->iter_next;
585
  
586
  if (ntt == NULL) return NULL;
587
  
588
  if (node) {
589
    if (node != NULL) {
590
      c->iter_next = node->next;
591
      return (node);
592
    }
593
  }
594
  
595
  if (! node) {
596
    while (c->iter_index < ntt->size) {
597
      index = c->iter_index++;
598
      
599
      if (ntt->tbl[index]) {
600
	c->iter_next = ntt->tbl[index]->next;
601
	return(ntt->tbl[index]);
602
      }
603
    }
604
  }
605
  return((struct ntt_node *)NULL);
606
}
607
608
/* END NTT (Named Pointer Tree) Functions */
(-)mod_evasive/work/mod_evasive/test.pl (+18 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# test.pl: small script to test mod_dosevasive's effectiveness
4
5
use IO::Socket;
6
use strict;
7
8
for(0..100) {
9
  my($response);
10
  my($SOCKET) = new IO::Socket::INET( Proto   => "tcp",
11
                                      PeerAddr=> "127.0.0.1:80");
12
  if (! defined $SOCKET) { die $!; }
13
  print $SOCKET "GET /?$_ HTTP/1.0\n\n";
14
  $response = <$SOCKET>;
15
  print $response;
16
  close($SOCKET);
17
}
18

Return to bug 201306