Skip to content

Instantly share code, notes, and snippets.

@funkadelic
Created February 6, 2013 08:34
Show Gist options
  • Save funkadelic/4721191 to your computer and use it in GitHub Desktop.
Save funkadelic/4721191 to your computer and use it in GitHub Desktop.
modifications to mod_evasive20.c to get it to mail alerts properly in Ubuntu 12.04 LTS. diff this against mod_evasive_1.10.1.tar.gz to see the changes
/*
mod_evasive for Apache 2
Copyright (c) by Jonathan A. Zdziarski
LICENSE
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <time.h>
#include <syslog.h>
#include <errno.h>
#include "httpd.h"
#include "http_core.h"
#include "http_config.h"
#include "http_log.h"
#include "http_request.h"
module AP_MODULE_DECLARE_DATA evasive20_module;
/* BEGIN DoS Evasive Maneuvers Definitions */
#define MAILER "/usr/bin/mail -s \"[mod_evasive] HTTP BLACKLIST %s\" %s"
#define LOG( A, ... ) { openlog("mod_evasive", LOG_PID, LOG_DAEMON); syslog( A, __VA_ARGS__ ); closelog(); }
#define DEFAULT_HASH_TBL_SIZE 3097ul // Default hash table size
#define DEFAULT_PAGE_COUNT 2 // Default maximum page hit count per interval
#define DEFAULT_SITE_COUNT 50 // Default maximum site hit count per interval
#define DEFAULT_PAGE_INTERVAL 1 // Default 1 Second page interval
#define DEFAULT_SITE_INTERVAL 1 // Default 1 Second site interval
#define DEFAULT_BLOCKING_PERIOD 10 // Default for Detected IPs; blocked for 10 seconds
#define DEFAULT_LOG_DIR "/tmp" // Default temp directory
/* END DoS Evasive Maneuvers Definitions */
/* BEGIN NTT (Named Timestamp Tree) Headers */
enum { ntt_num_primes = 28 };
/* ntt root tree */
struct ntt {
long size;
long items;
struct ntt_node **tbl;
};
/* ntt node (entry in the ntt root tree) */
struct ntt_node {
char *key;
time_t timestamp;
long count;
struct ntt_node *next;
};
/* ntt cursor */
struct ntt_c {
long iter_index;
struct ntt_node *iter_next;
};
struct ntt *ntt_create(long size);
int ntt_destroy(struct ntt *ntt);
struct ntt_node *ntt_find(struct ntt *ntt, const char *key);
struct ntt_node *ntt_insert(struct ntt *ntt, const char *key, time_t timestamp);
int ntt_delete(struct ntt *ntt, const char *key);
long ntt_hashcode(struct ntt *ntt, const char *key);
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c);
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c);
/* END NTT (Named Timestamp Tree) Headers */
/* BEGIN DoS Evasive Maneuvers Globals */
struct ntt *hit_list; // Our dynamic hash table
static unsigned long hash_table_size = DEFAULT_HASH_TBL_SIZE;
static int page_count = DEFAULT_PAGE_COUNT;
static int page_interval = DEFAULT_PAGE_INTERVAL;
static int site_count = DEFAULT_SITE_COUNT;
static int site_interval = DEFAULT_SITE_INTERVAL;
static int blocking_period = DEFAULT_BLOCKING_PERIOD;
static char *email_notify = NULL;
static char *log_dir = NULL;
static char *system_command = NULL;
static const char *whitelist(cmd_parms *cmd, void *dconfig, const char *ip);
int is_whitelisted(const char *ip);
/* END DoS Evasive Maneuvers Globals */
static void * create_hit_list(apr_pool_t *p, server_rec *s)
{
/* Create a new hit list for this listener */
hit_list = ntt_create(hash_table_size);
}
static const char *whitelist(cmd_parms *cmd, void *dconfig, const char *ip)
{
char entry[128];
snprintf(entry, sizeof(entry), "WHITELIST_%s", ip);
ntt_insert(hit_list, entry, time(NULL));
return NULL;
}
static int access_checker(request_rec *r)
{
int ret = OK;
/* BEGIN DoS Evasive Maneuvers Code */
if (r->prev == NULL && r->main == NULL && hit_list != NULL) {
char hash_key[2048];
struct ntt_node *n;
time_t t = time(NULL);
/* Check whitelist */
if (is_whitelisted(r->connection->remote_ip))
return OK;
/* First see if the IP itself is on "hold" */
n = ntt_find(hit_list, r->connection->remote_ip);
if (n != NULL && t-n->timestamp<blocking_period) {
/* If the IP is on "hold", make it wait longer in 403 land */
ret = HTTP_FORBIDDEN;
n->timestamp = time(NULL);
/* Not on hold, check hit stats */
} else {
/* Has URI been hit too much? */
snprintf(hash_key, 2048, "%s_%s", r->connection->remote_ip, r->uri);
n = ntt_find(hit_list, hash_key);
if (n != NULL) {
/* If URI is being hit too much, add to "hold" list and 403 */
if (t-n->timestamp<page_interval && n->count>=page_count) {
ret = HTTP_FORBIDDEN;
ntt_insert(hit_list, r->connection->remote_ip, time(NULL));
} else {
/* Reset our hit count list as necessary */
if (t-n->timestamp>=page_interval) {
n->count=0;
}
}
n->timestamp = t;
n->count++;
} else {
ntt_insert(hit_list, hash_key, t);
}
/* Has site been hit too much? */
snprintf(hash_key, 2048, "%s_SITE", r->connection->remote_ip);
n = ntt_find(hit_list, hash_key);
if (n != NULL) {
/* If site is being hit too much, add to "hold" list and 403 */
if (t-n->timestamp<site_interval && n->count>=site_count) {
ret = HTTP_FORBIDDEN;
ntt_insert(hit_list, r->connection->remote_ip, time(NULL));
} else {
/* Reset our hit count list as necessary */
if (t-n->timestamp>=site_interval) {
n->count=0;
}
}
n->timestamp = t;
n->count++;
} else {
ntt_insert(hit_list, hash_key, t);
}
}
/* Perform email notification and system functions */
if (ret == HTTP_FORBIDDEN) {
char filename[1024];
struct stat s;
FILE *file;
snprintf(filename, sizeof(filename), "%s/dos-%s", log_dir != NULL ? log_dir : DEFAULT_LOG_DIR, r->connection->remote_ip);
if (stat(filename, &s)) {
file = fopen(filename, "w");
if (file != NULL) {
fprintf(file, "%ld\n", getpid());
fclose(file);
LOG(LOG_ALERT, "Blacklisting address %s: possible DoS attack.", r->connection->remote_ip);
if (email_notify != NULL) {
snprintf(filename, sizeof(filename), MAILER, r->connection->remote_ip, email_notify);
file = popen(filename, "w");
if (file != NULL) {
fprintf(file, "mod_evasive HTTP Blacklisted %s\n", r->connection->remote_ip);
pclose(file);
}
}
if (system_command != NULL) {
snprintf(filename, sizeof(filename), system_command, r->connection->remote_ip);
system(filename);
}
} else {
LOG(LOG_ALERT, "Couldn't open logfile %s: %s",filename, strerror(errno));
}
} /* if (temp file does not exist) */
} /* if (ret == HTTP_FORBIDDEN) */
} /* if (r->prev == NULL && r->main == NULL && hit_list != NULL) */
/* END DoS Evasive Maneuvers Code */
if (ret == HTTP_FORBIDDEN
&& (ap_satisfies(r) != SATISFY_ANY || !ap_some_auth_required(r))) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"client denied by server configuration: %s",
r->filename);
}
return ret;
}
int is_whitelisted(const char *ip) {
char hashkey[128];
char octet[4][4];
char *dip;
char *oct;
int i = 0;
memset(octet, 0, 16);
dip = strdup(ip);
if (dip == NULL)
return 0;
oct = strtok(dip, ".");
while(oct != NULL && i<4) {
if (strlen(oct)<=3)
strcpy(octet[i], oct);
i++;
oct = strtok(NULL, ".");
}
free(dip);
/* Exact Match */
snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s", ip);
if (ntt_find(hit_list, hashkey)!=NULL)
return 1;
/* IPv4 Wildcards */
snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.*.*.*", octet[0]);
if (ntt_find(hit_list, hashkey)!=NULL)
return 1;
snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.%s.*.*", octet[0], octet[1]);
if (ntt_find(hit_list, hashkey)!=NULL)
return 1;
snprintf(hashkey, sizeof(hashkey), "WHITELIST_%s.%s.%s.*", octet[0], octet[1], octet[2]);
if (ntt_find(hit_list, hashkey)!=NULL)
return 1;
/* No match */
return 0;
}
static apr_status_t destroy_hit_list(void *not_used) {
ntt_destroy(hit_list);
free(email_notify);
free(system_command);
}
/* BEGIN NTT (Named Timestamp Tree) Functions */
static unsigned long ntt_prime_list[ntt_num_primes] =
{
53ul, 97ul, 193ul, 389ul, 769ul,
1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
1610612741ul, 3221225473ul, 4294967291ul
};
/* Find the numeric position in the hash table based on key and modulus */
long ntt_hashcode(struct ntt *ntt, const char *key) {
unsigned long val = 0;
for (; *key; ++key) val = 5 * val + *key;
return(val % ntt->size);
}
/* Creates a single node in the tree */
struct ntt_node *ntt_node_create(const char *key) {
char *node_key;
struct ntt_node* node;
node = (struct ntt_node *) malloc(sizeof(struct ntt_node));
if (node == NULL) {
return NULL;
}
if ((node_key = strdup(key)) == NULL) {
free(node);
return NULL;
}
node->key = node_key;
node->timestamp = time(NULL);
node->next = NULL;
return(node);
}
/* Tree initializer */
struct ntt *ntt_create(long size) {
long i = 0;
struct ntt *ntt = (struct ntt *) malloc(sizeof(struct ntt));
if (ntt == NULL)
return NULL;
while (ntt_prime_list[i] < size) { i++; }
ntt->size = ntt_prime_list[i];
ntt->items = 0;
ntt->tbl = (struct ntt_node **) calloc(ntt->size, sizeof(struct ntt_node *));
if (ntt->tbl == NULL) {
free(ntt);
return NULL;
}
return(ntt);
}
/* Find an object in the tree */
struct ntt_node *ntt_find(struct ntt *ntt, const char *key) {
long hash_code;
struct ntt_node *node;
if (ntt == NULL) return NULL;
hash_code = ntt_hashcode(ntt, key);
node = ntt->tbl[hash_code];
while (node) {
if (!strcmp(key, node->key)) {
return(node);
}
node = node->next;
}
return((struct ntt_node *)NULL);
}
/* Insert a node into the tree */
struct ntt_node *ntt_insert(struct ntt *ntt, const char *key, time_t timestamp) {
long hash_code;
struct ntt_node *parent;
struct ntt_node *node;
struct ntt_node *new_node = NULL;
if (ntt == NULL) return NULL;
hash_code = ntt_hashcode(ntt, key);
parent = NULL;
node = ntt->tbl[hash_code];
while (node != NULL) {
if (strcmp(key, node->key) == 0) {
new_node = node;
node = NULL;
}
if (new_node == NULL) {
parent = node;
node = node->next;
}
}
if (new_node != NULL) {
new_node->timestamp = timestamp;
new_node->count = 0;
return new_node;
}
/* Create a new node */
new_node = ntt_node_create(key);
new_node->timestamp = timestamp;
new_node->timestamp = 0;
ntt->items++;
/* Insert */
if (parent) { /* Existing parent */
parent->next = new_node;
return new_node; /* Return the locked node */
}
/* No existing parent; add directly to hash table */
ntt->tbl[hash_code] = new_node;
return new_node;
}
/* Tree destructor */
int ntt_destroy(struct ntt *ntt) {
struct ntt_node *node, *next;
struct ntt_c c;
if (ntt == NULL) return -1;
node = c_ntt_first(ntt, &c);
while(node != NULL) {
next = c_ntt_next(ntt, &c);
ntt_delete(ntt, node->key);
node = next;
}
free(ntt->tbl);
free(ntt);
ntt = (struct ntt *) NULL;
return 0;
}
/* Delete a single node in the tree */
int ntt_delete(struct ntt *ntt, const char *key) {
long hash_code;
struct ntt_node *parent = NULL;
struct ntt_node *node;
struct ntt_node *del_node = NULL;
if (ntt == NULL) return -1;
hash_code = ntt_hashcode(ntt, key);
node = ntt->tbl[hash_code];
while (node != NULL) {
if (strcmp(key, node->key) == 0) {
del_node = node;
node = NULL;
}
if (del_node == NULL) {
parent = node;
node = node->next;
}
}
if (del_node != NULL) {
if (parent) {
parent->next = del_node->next;
} else {
ntt->tbl[hash_code] = del_node->next;
}
free(del_node->key);
free(del_node);
ntt->items--;
return 0;
}
return -5;
}
/* Point cursor to first item in tree */
struct ntt_node *c_ntt_first(struct ntt *ntt, struct ntt_c *c) {
c->iter_index = 0;
c->iter_next = (struct ntt_node *)NULL;
return(c_ntt_next(ntt, c));
}
/* Point cursor to next iteration in tree */
struct ntt_node *c_ntt_next(struct ntt *ntt, struct ntt_c *c) {
long index;
struct ntt_node *node = c->iter_next;
if (ntt == NULL) return NULL;
if (node) {
if (node != NULL) {
c->iter_next = node->next;
return (node);
}
}
if (! node) {
while (c->iter_index < ntt->size) {
index = c->iter_index++;
if (ntt->tbl[index]) {
c->iter_next = ntt->tbl[index]->next;
return(ntt->tbl[index]);
}
}
}
return((struct ntt_node *)NULL);
}
/* END NTT (Named Pointer Tree) Functions */
/* BEGIN Configuration Functions */
static const char *
get_hash_tbl_size(cmd_parms *cmd, void *dconfig, const char *value) {
long n = strtol(value, NULL, 0);
if (n<=0) {
hash_table_size = DEFAULT_HASH_TBL_SIZE;
} else {
hash_table_size = n;
}
return NULL;
}
static const char *
get_page_count(cmd_parms *cmd, void *dconfig, const char *value) {
long n = strtol(value, NULL, 0);
if (n<=0) {
page_count = DEFAULT_PAGE_COUNT;
} else {
page_count = n;
}
return NULL;
}
static const char *
get_site_count(cmd_parms *cmd, void *dconfig, const char *value) {
long n = strtol(value, NULL, 0);
if (n<=0) {
site_count = DEFAULT_SITE_COUNT;
} else {
site_count = n;
}
return NULL;
}
static const char *
get_page_interval(cmd_parms *cmd, void *dconfig, const char *value) {
long n = strtol(value, NULL, 0);
if (n<=0) {
page_interval = DEFAULT_PAGE_INTERVAL;
} else {
page_interval = n;
}
return NULL;
}
static const char *
get_site_interval(cmd_parms *cmd, void *dconfig, const char *value) {
long n = strtol(value, NULL, 0);
if (n<=0) {
site_interval = DEFAULT_SITE_INTERVAL;
} else {
site_interval = n;
}
return NULL;
}
static const char *
get_blocking_period(cmd_parms *cmd, void *dconfig, const char *value) {
long n = strtol(value, NULL, 0);
if (n<=0) {
blocking_period = DEFAULT_BLOCKING_PERIOD;
} else {
blocking_period = n;
}
return NULL;
}
static const char *
get_log_dir(cmd_parms *cmd, void *dconfig, const char *value) {
if (value != NULL && value[0] != 0) {
if (log_dir != NULL)
free(log_dir);
log_dir = strdup(value);
}
return NULL;
}
static const char *
get_email_notify(cmd_parms *cmd, void *dconfig, const char *value) {
if (value != NULL && value[0] != 0) {
if (email_notify != NULL)
free(email_notify);
email_notify = strdup(value);
}
return NULL;
}
static const char *
get_system_command(cmd_parms *cmd, void *dconfig, const char *value) {
if (value != NULL && value[0] != 0) {
if (system_command != NULL)
free(system_command);
system_command = strdup(value);
}
return NULL;
}
/* END Configuration Functions */
static const command_rec access_cmds[] =
{
AP_INIT_TAKE1("DOSHashTableSize", get_hash_tbl_size, NULL, RSRC_CONF,
"Set size of hash table"),
AP_INIT_TAKE1("DOSPageCount", get_page_count, NULL, RSRC_CONF,
"Set maximum page hit count per interval"),
AP_INIT_TAKE1("DOSSiteCount", get_site_count, NULL, RSRC_CONF,
"Set maximum site hit count per interval"),
AP_INIT_TAKE1("DOSPageInterval", get_page_interval, NULL, RSRC_CONF,
"Set page interval"),
AP_INIT_TAKE1("DOSSiteInterval", get_site_interval, NULL, RSRC_CONF,
"Set site interval"),
AP_INIT_TAKE1("DOSBlockingPeriod", get_blocking_period, NULL, RSRC_CONF,
"Set blocking period for detected DoS IPs"),
AP_INIT_TAKE1("DOSEmailNotify", get_email_notify, NULL, RSRC_CONF,
"Set email notification"),
AP_INIT_TAKE1("DOSLogDir", get_log_dir, NULL, RSRC_CONF,
"Set log dir"),
AP_INIT_TAKE1("DOSSystemCommand", get_system_command, NULL, RSRC_CONF,
"Set system command on DoS"),
AP_INIT_ITERATE("DOSWhitelist", whitelist, NULL, RSRC_CONF,
"IP-addresses wildcards to whitelist"),
{ NULL }
};
static void register_hooks(apr_pool_t *p) {
ap_hook_access_checker(access_checker, NULL, NULL, APR_HOOK_MIDDLE);
apr_pool_cleanup_register(p, NULL, apr_pool_cleanup_null, destroy_hit_list);
};
module AP_MODULE_DECLARE_DATA evasive20_module =
{
STANDARD20_MODULE_STUFF,
NULL,
NULL,
create_hit_list,
NULL,
access_cmds,
register_hooks
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment