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)

Reply via email to