Hi,
I have just produced a patch against the upstream HEAD version,
to seek a way to fight against DoS attack in openssl itself,
the logic is simple, get client's ip address in BIO layer,
and send this info to upper SSL layer; In SSL layer,
according to the client ip and control policy to do control.
And I have just finished the enhancement to use rb-tree as the main struct,
the patch is attached,and have took a simple test from 2 machines.
the test script looks like this:
#thc-ssl-dosit() { while :; do (while :; do echo R; done) | openssl s_client
-ssl3 -connect hostname:4433 2>/dev/null; done }
thc-ssl-dosit() { openssl s_client -ssl3 -connect hostname:4433 2>/dev/null;}
for x in `seq 1 5000`; do thc-ssl-dosit & done
and looks like it works ok,and perform very well.
But, still, I can not give real pressure to the rb-tree, actually only 2
different ip address used,
and 'openssl s_client' have no option to use a bonding ip address, so, still
have no environment
to make a real pressure test.Do you have any method to do this?If someone think
add a bonding ip address
option is a good idea, please response.
The struct has been changed to use rb-tree, so, the performance can be
estimated.
Please response and comment!
thanks,
Guanjun
diff -Nupr openssl/ssl/s3_srvr.c openssl.1/ssl/s3_srvr.c
--- openssl/ssl/s3_srvr.c 2011-09-05 21:36:22.000000000 +0800
+++ openssl.1/ssl/s3_srvr.c 2011-12-09 13:14:56.000000000 +0800
@@ -169,6 +169,499 @@
#include <openssl/krb5_asn.h>
#endif
#include <openssl/md5.h>
+#include <time.h>
+
+static time_t start = 0;
+static int timer_interval = 300;/*5 min, these 2 values are configure-able*/
+static int dos_benchmark = 3000;
+
+typedef enum color_t
+{
+ RED = 0,
+ BLACK = 1
+}color_t;
+
+typedef struct rb_node_t
+{
+ struct rb_node_t *left, *right, *parent;
+ struct rb_node_t *next;/*also keep a simple list, for destroy a tree*/
+ unsigned long key;/*ip*/
+ unsigned long data;/*counter*/
+ color_t color;
+}rb_node_t;
+
+static rb_node_t* rb_dlist = NULL;
+static rb_node_t* rb_iter = NULL;
+static rb_node_t maxCounterNode;
+static rb_node_t* clist = NULL;
+static rb_node_t* blacklist = NULL;
+
+rb_node_t* rb_insert(unsigned long key, unsigned long data, rb_node_t* root);
+rb_node_t* rb_insert2(rb_node_t* node, rb_node_t* root);
+
+rb_node_t* rb_search(unsigned long key, rb_node_t* root);
+/*rb_node_t* rb_search(rb_node_t* node, rb_node_t* root);*/
+
+rb_node_t* rb_erase(unsigned long key, rb_node_t* root);
+/*rb_node_t* rb_erase(rb_node_t* node, rb_node_t* root);*/
+
+void rb_destroy(rb_node_t* root);
+
+void rb_destroy(rb_node_t* root)
+{
+ rb_node_t* tmp_node = NULL;
+ while(root)
+ {
+ tmp_node = root->next;
+ free(root);
+ root = tmp_node;
+ }
+}
+
+static rb_node_t* rb_new_node(unsigned long key, unsigned long data)
+{
+ rb_node_t *node = (rb_node_t*)malloc(sizeof(struct rb_node_t));
+ if (!node)
+ {
+ return NULL;
+ }
+ node->key = key;
+ node->data = data;
+ return node;
+}
+
+static rb_node_t* rb_rotate_left(rb_node_t* node, rb_node_t* root)
+{
+ rb_node_t* right = node->right;
+ if ((node->right = right->left))
+ {
+ right->left->parent = node;
+ }
+ right->left = node;
+
+ if ((right->parent = node->parent))
+ {
+ if (node == node->parent->right)
+ {
+ node->parent->right = right;
+ }
+ else
+ {
+ node->parent->left = right;
+ }
+ }
+ else
+ {
+ root = right;
+ }
+ node->parent = right;
+
+ return root;
+}
+
+static rb_node_t* rb_rotate_right(rb_node_t* node, rb_node_t* root)
+{
+ rb_node_t* left = node->left;
+
+ if ((node->left = left->right))
+ {
+ left->right->parent = node;
+ }
+ left->right = node;
+
+ if ((left->parent = node->parent))
+ {
+ if (node == node->parent->right)
+ {
+ node->parent->right = left;
+ }
+ else
+ {
+ node->parent->left = left;
+ }
+ }
+ else
+ {
+ root = left;
+ }
+ node->parent = left;
+
+ return root;
+}
+
+static rb_node_t* rb_insert_rebalance(rb_node_t *node, rb_node_t *root)
+{
+ rb_node_t *parent, *gparent, *uncle, *tmp;
+
+ while ((parent = node->parent) && parent->color == RED)
+ {
+ gparent = parent->parent;
+ if (parent == gparent->left)
+ {
+ uncle = gparent->right;
+ if (uncle && uncle->color == RED)
+ {
+ uncle->color = BLACK;
+ parent->color = BLACK;
+ gparent->color = RED;
+ node = gparent;
+ }
+ else
+ {
+ if (parent->right == node)
+ {
+ root = rb_rotate_left(parent, root);
+ tmp = parent;
+ parent = node;
+ node = tmp;
+ }
+ parent->color = BLACK;
+ gparent->color = RED;
+ root = rb_rotate_right(gparent, root);
+ }
+ }
+ else
+ {
+ uncle = gparent->left;
+ if (uncle && uncle->color == RED)
+ {
+ uncle->color = BLACK;
+ parent->color = BLACK;
+ gparent->color = RED;
+ node = gparent;
+ }
+ else
+ {
+ if (parent->left == node)
+ {
+ root = rb_rotate_right(parent, root);
+ tmp = parent;
+ parent = node;
+ node = tmp;
+ }
+ parent->color = BLACK;
+ gparent->color = RED;
+ root = rb_rotate_left(gparent, root);
+ }
+ }
+ }
+ root->color = BLACK;
+
+ return root;
+}
+
+static rb_node_t* rb_erase_rebalance(rb_node_t *node, rb_node_t *parent,
rb_node_t *root)
+{
+ rb_node_t *other, *o_left, *o_right;
+
+ while ((!node || node->color == BLACK) && node != root)
+ {
+ if (parent->left == node)
+ {
+ other = parent->right;
+ if (other->color == RED)
+ {
+ other->color = BLACK;
+ parent->color = RED;
+ root = rb_rotate_left(parent, root);
+ other = parent->right;
+ }
+ if ((!other->left || other->left->color == BLACK) &&
+ (!other->right || other->right->color
== BLACK))
+ {
+ other->color = RED;
+ node = parent;
+ parent = node->parent;
+ }
+ else
+ {
+ if (!other->right || other->right->color ==
BLACK)
+ {
+ if ((o_left = other->left))
+ {
+ o_left->color = BLACK;
+ }
+ other->color = RED;
+ root = rb_rotate_right(other, root);
+ other = parent->right;
+ }
+ other->color = parent->color;
+ parent->color = BLACK;
+ if (other->right)
+ {
+ other->right->color = BLACK;
+ }
+ root = rb_rotate_left(parent, root);
+ node = root;
+ break;
+ }
+ }
+ else
+ {
+ other = parent->left;
+ if (other->color == RED)
+ {
+ other->color = BLACK;
+ parent->color = RED;
+ root = rb_rotate_right(parent, root);
+ other = parent->left;
+ }
+
+ if ((!other->left || other->left->color == BLACK) &&
+ (!other->right || other->right->color
== BLACK))
+ {
+ other->color = RED;
+ node = parent;
+ parent = node->parent;
+ }
+ else
+ {
+ if (!other->left || other->left->color == BLACK)
+ {
+ if ((o_right = other->right))
+ {
+ o_right->color = BLACK;
+ }
+ other->color = RED;
+ root = rb_rotate_left(other, root);
+ other = parent->left;
+ }
+ other->color = parent->color;
+ parent->color = BLACK;
+ if (other->left)
+ {
+ other->left->color = BLACK;
+ }
+ root = rb_rotate_right(parent, root);
+ node = root;
+ break;
+ }
+ }
+ }
+ if (node)
+ {
+ node->color = BLACK;
+ }
+
+ return root;
+}
+
+static rb_node_t* rb_search_ass(unsigned long key, rb_node_t* root,
rb_node_t** save)
+{
+ rb_node_t *node = root, *parent = NULL;
+ unsigned long ret;
+
+ while (node)
+ {
+ parent = node;
+ ret = node->key - key;
+ if (0 < ret)
+ {
+ node = node->left;
+ }
+ else if (0 > ret)
+ {
+ node = node->right;
+ }
+ else
+ {
+ return node;
+ }
+ }
+
+ if (save)
+ {
+ *save = parent;
+ }
+
+ return NULL;
+}
+
+rb_node_t* rb_search(unsigned long key, rb_node_t* root)
+{
+ return rb_search_ass(key, root, NULL);
+}
+
+rb_node_t* rb_insert(unsigned long key, unsigned long data, rb_node_t* root)
+{
+ rb_node_t *parent = NULL, *node;
+ parent = NULL;
+
+ if ((node = rb_search_ass(key, root, &parent)))
+ {
+ return root;
+ }
+
+ node = rb_new_node(key, data);
+ node->parent = parent;
+ node->left = node->right = NULL;
+ node->color = RED;
+
+ if (parent)
+ {
+ if (parent->key > key)
+ {
+ parent->left = node;
+ }
+ else
+ {
+ parent->right = node;
+ }
+ }
+ else
+ {
+ root = node;
+ }
+
+ return rb_insert_rebalance(node, root);
+}
+
+rb_node_t* rb_insert2(rb_node_t* ins_node, rb_node_t* root)
+{
+ rb_node_t *parent = NULL, *node;
+ parent = NULL;
+
+ if (node = rb_search_ass(ins_node->key, root, &parent))
+ {
+ return root;
+ }
+
+ node = ins_node;
+ node->parent = parent;
+ node->left = node->right = NULL;
+ node->color = RED;
+
+ if (parent)
+ {
+ if (parent->key > node->key)
+ {
+ parent->left = node;
+ }
+ else
+ {
+ parent->right = node;
+ }
+ }
+ else
+ {
+ root = node;
+ }
+
+ return rb_insert_rebalance(node, root);
+}
+
+rb_node_t* rb_erase(unsigned long key, rb_node_t *root)
+{
+ rb_node_t *child, *parent, *old, *left, *node;
+ color_t color;
+
+ if (!(node = rb_search_ass(key, root, NULL)))
+ {
+ return root;
+ }
+ old = node;
+ if (node->left && node->right)
+ {
+ node = node->right;
+ while ((left = node->left) != NULL)
+ {
+ node = left;
+ }
+ child = node->right;
+ parent = node->parent;
+ color = node->color;
+
+ if (child)
+ {
+ child->parent = parent;
+ }
+ if (parent)
+ {
+ if (parent->left == node)
+ {
+ parent->left = child;
+ }
+ else
+ {
+ parent->right = child;
+ }
+ }
+ else
+ {
+ root = child;
+ }
+ if (node->parent == old)
+ {
+ parent = node;
+ }
+ node->parent = old->parent;
+ node->color = old->color;
+ node->right = old->right;
+ node->left = old->left;
+ if (old->parent)
+ {
+ if (old->parent->left == old)
+ {
+ old->parent->left = node;
+ }
+ else
+ {
+ old->parent->right = node;
+ }
+ }
+ else
+ {
+ root = node;
+ }
+
+ old->left->parent = node;
+ if (old->right)
+ {
+ old->right->parent = node;
+ }
+ }
+ else
+ {
+ if (!node->left)
+ {
+ child = node->right;
+ }
+ else if (!node->right)
+ {
+ child = node->left;
+ }
+ parent = node->parent;
+ color = node->color;
+ if (child)
+ {
+ child->parent = parent;
+ }
+ if (parent)
+ {
+ if (parent->left == node)
+ {
+ parent->left = child;
+ }
+ else
+ {
+ parent->right = child;
+ }
+ }
+ else
+ {
+ root = child;
+ }
+ }
+ free(old);
+ if (color == BLACK)
+ {
+ root = rb_erase_rebalance(child, parent, root);
+ }
+ return root;
+}
+
+
+
static const SSL_METHOD *ssl3_get_server_method(int ver);
@@ -220,6 +713,10 @@ int ssl3_accept(SSL *s)
#ifndef OPENSSL_NO_SRP
int srp_no_username =0;
#endif
+ time_t current = 0;
+ int duration = 0;
+ int tmp_dos = 0;
+ rb_node_t* tmp_node = NULL;
RAND_add(&Time,sizeof(Time),0);
ERR_clear_error();
@@ -343,6 +840,91 @@ int ssl3_accept(SSL *s)
#ifndef OPENSSL_NO_SRP
case SSL3_ST_SR_CLNT_HELLO_SRP_USERNAME:
#endif
+ if(s->client && !getenv("OPENSSL_NO_DOS_CONTROL"))
+ {
+ CRYPTO_r_lock(CRYPTO_LOCK_SSL);
+ if(blacklist != NULL)
+ {
+ if(tmp_node=rb_search(s->client,
blacklist))
+ {
+ current = time(NULL);
+ duration = (int)(current -
tmp_node->data);
+ if(duration > 60 * 30)/*30 min,
clear the blacklist*/
+ {
+
CRYPTO_r_unlock(CRYPTO_LOCK_SSL);
+
CRYPTO_w_lock(CRYPTO_LOCK_SSL);
+ blacklist =
rb_erase(tmp_node->key, blacklist);
+
CRYPTO_w_unlock(CRYPTO_LOCK_SSL);
+
CRYPTO_r_lock(CRYPTO_LOCK_SSL);
+ goto dos_normal;
+ }
+
CRYPTO_r_unlock(CRYPTO_LOCK_SSL);
+ goto end;
+ }
+ }
+dos_normal:
+ if(clist != NULL)
+ {
+ current = time(NULL);
+ duration = (int)(current - start);
+ tmp_node = rb_search(s->client, clist);
+ CRYPTO_r_unlock(CRYPTO_LOCK_SSL);
+ CRYPTO_w_lock(CRYPTO_LOCK_SSL);
+ if(getenv("OPENSSL_DOS_TIMER_INTERVAL")
!= NULL)
+ {
+ tmp_dos =
atoi(getenv("OPENSSL_DOS_TIMER_INTERVAL"));
+ timer_interval =
tmp_dos?tmp_dos:timer_interval;
+ }
+ if(getenv("OPENSSL_DOS_BENCHAMARK") !=
NULL)
+ {
+ tmp_dos =
atoi(getenv("OPENSSL_DOS_BENCHAMARK"));
+ dos_benchmark =
tmp_dos?tmp_dos:dos_benchmark;
+ }
+ if(tmp_node != NULL)
+ {
+ tmp_node->data++;
+ if(tmp_node->data >
maxCounterNode.data)
+ {
+ maxCounterNode.data =
tmp_node->data;
+ maxCounterNode.key =
tmp_node->key;
+ }
+ /*do the policy and clear the
statistics data*/
+ if(duration > timer_interval)
+ {
+ if(maxCounterNode.data
> dos_benchmark)
+ {
+
maxCounterNode.data = time(NULL);/*time stamp*/
+ blacklist =
rb_insert(maxCounterNode.key, maxCounterNode.data, blacklist);
+ }
+ maxCounterNode.data = 0;
+ rb_destroy(rb_dlist);
+ clist = NULL;
+ start = time(NULL);
+ }
+ }
+ else
+ {
+ tmp_node =
rb_new_node(s->client, 1);
+ clist = rb_insert2(tmp_node,
clist);
+ tmp_node->next = NULL;
+ rb_iter->next = tmp_node;
+ rb_iter = tmp_node;
+ }
+ CRYPTO_w_unlock(CRYPTO_LOCK_SSL);
+ }
+ else/*initialize the tree*/
+ {
+ CRYPTO_r_unlock(CRYPTO_LOCK_SSL);
+ CRYPTO_w_lock(CRYPTO_LOCK_SSL);
+ clist = rb_insert(s->client, 1, clist);
+ clist->next = NULL;
+ rb_dlist = rb_iter = clist;
+ maxCounterNode.key = clist->key;
+ maxCounterNode.data = 1;
+ start = time(NULL);
+ CRYPTO_w_unlock(CRYPTO_LOCK_SSL);
+ }
+ }
s->shutdown=0;
ret=ssl3_get_client_hello(s);
diff -Nupr openssl/ssl/ssl.h openssl.1/ssl/ssl.h
--- openssl/ssl/ssl.h 2011-11-16 07:50:52.000000000 +0800
+++ openssl.1/ssl/ssl.h 2011-12-09 12:21:12.000000000 +0800
@@ -1347,6 +1347,7 @@ struct ssl_st
#else
#define session_ctx ctx
#endif /* OPENSSL_NO_TLSEXT */
+ unsigned long client;
};
#endif
diff -Nupr openssl/ssl/ssl_lib.c openssl.1/ssl/ssl_lib.c
--- openssl/ssl/ssl_lib.c 2011-11-16 07:50:52.000000000 +0800
+++ openssl.1/ssl/ssl_lib.c 2011-12-09 12:20:49.000000000 +0800
@@ -161,6 +161,11 @@
#include <openssl/engine.h>
#endif
+#include<sys/socket.h>
+#include<sys/types.h>
+#include<netinet/in.h>
+
+
const char *SSL_version_str=OPENSSL_VERSION_TEXT;
SSL3_ENC_METHOD ssl3_undef_enc_method={
@@ -607,6 +612,8 @@ void SSL_free(SSL *s)
void SSL_set_bio(SSL *s,BIO *rbio,BIO *wbio)
{
+ struct sockaddr_in c_addr;
+ socklen_t len;
/* If the output buffering BIO is still in place, remove it
*/
if (s->bbio != NULL)
@@ -623,6 +630,13 @@ void SSL_set_bio(SSL *s,BIO *rbio,BIO *w
BIO_free_all(s->wbio);
s->rbio=rbio;
s->wbio=wbio;
+ /*add the ip to SSL*/
+ if(s && s->rbio && s->rbio->method->type == BIO_TYPE_SOCKET)
+ {
+ len = sizeof c_addr;
+ getpeername(s->rbio->num, (struct sockaddr*)&c_addr, &len);
+ s->client = ntohl(c_addr.sin_addr.s_addr);
+ }
}
BIO *SSL_get_rbio(const SSL *s)