Blob Blame History Raw
From c532115a11eb1742ff53ed821685d8d975e2af0f Mon Sep 17 00:00:00 2001
From: Robert Stepanek <rsto@fastmailteam.com>
Date: Mon, 14 Oct 2019 17:43:18 +0200
Subject: [PATCH 1/5] hash: gracefully handle lookup on zero-sized tables

---
 lib/hash.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/lib/hash.c b/lib/hash.c
index bdf11507bc..ba1d4b132c 100644
--- a/lib/hash.c
+++ b/lib/hash.c
@@ -157,9 +157,14 @@ void *hash_insert(const char *key, void *data, hash_table *table)
 
 void *hash_lookup(const char *key, hash_table *table)
 {
-      unsigned val = strhash(key) % table->size;
+      unsigned val;
       bucket *ptr;
 
+      if (!table->size)
+          return NULL;
+
+      val = strhash(key) % table->size;
+
       if (!(table->table)[val])
             return NULL;
 

From 7c3156bb511b6b84413eead1ffd99bd818bb9b71 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=94=D0=B8=D0=BB=D1=8F=D0=BD=20=D0=9F=D0=B0=D0=BB=D0=B0?=
 =?UTF-8?q?=D1=83=D0=B7=D0=BE=D0=B2?= <git-dpa@aegee.org>
Date: Tue, 29 Sep 2020 22:15:18 +0300
Subject: [PATCH 2/5] hash.c:hash_del: deduplicate code

---
 lib/hash.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/lib/hash.c b/lib/hash.c
index ba1d4b132c..cf85d9d2ca 100644
--- a/lib/hash.c
+++ b/lib/hash.c
@@ -188,7 +188,6 @@ void *hash_lookup(const char *key, hash_table *table)
 void *hash_del(char *key, hash_table *table)
 {
       unsigned val = strhash(key) % table->size;
-      void *data;
       bucket *ptr, *last = NULL;
 
       if (!(table->table)[val])
@@ -209,15 +208,10 @@ void *hash_del(char *key, hash_table *table)
 	  int cmpresult = strcmp(key, ptr->key);
 	  if (!cmpresult)
 	  {
+              void *data = ptr->data;
 	      if (last != NULL )
 	      {
-		  data = ptr -> data;
 		  last -> next = ptr -> next;
-		  if(!table->pool) {
-		      free(ptr->key);
-		      free(ptr);
-		  }
-		  return data;
 	      }
 	      
 	      /*
@@ -230,15 +224,15 @@ void *hash_del(char *key, hash_table *table)
 	      
 	      else
 	      {
-		  data = ptr->data;
 		  (table->table)[val] = ptr->next;
+              }
 		  if(!table->pool) {
 		      free(ptr->key);
 		      free(ptr);
 		  }
 		  return data;
 	      }
-	  } else if (cmpresult < 0) {
+          if (cmpresult < 0) {
 	      /* its not here! */
 	      return NULL;
 	  }

From d415da7ae571c3854550564a1764e763cc83c921 Mon Sep 17 00:00:00 2001
From: ellie timoney <ellie@fastmail.com>
Date: Wed, 19 May 2021 13:39:34 +1000
Subject: [PATCH 3/5] strhash: replace ad-hoc algorithm with seeded djb2

Part of CVE-2021-33582
---
 lib/strhash.c | 37 ++++++++++++++++++++++++++-----------
 lib/strhash.h |  6 +++++-
 2 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/lib/strhash.c b/lib/strhash.c
index 3457abab27..8f68d13867 100644
--- a/lib/strhash.c
+++ b/lib/strhash.c
@@ -57,17 +57,32 @@
 #include "assert.h"
 #include "strhash.h"
 
-unsigned strhash(const char *string)
+#include "lib/strhash.h"
+
+/* The well-known djb2 algorithm (e.g. http://www.cse.yorku.ca/~oz/hash.html),
+ * with the addition of an optional seed to limit predictability.
+ *
+ * XXX return type 'unsigned' for back-compat to previous version, but
+ * XXX ought to be 'uint32_t'
+ */
+unsigned strhash_seeded_djb2(uint32_t seed, const char *string)
 {
-      unsigned ret_val = 0;
-      int i;
+    const unsigned char *ustr = (const unsigned char *) string;
+    unsigned hash = 5381;
+    int c;
+
+    if (seed) {
+        /* treat the bytes of the seed as a prefix to the string */
+        unsigned i;
+        for (i = 0; i < sizeof seed; i++) {
+            c = seed & 0xff;
+            hash = ((hash << 5) + hash) ^ c;
+            seed >>= 8;
+        }
+    }
+
+    while ((c = *ustr++))
+        hash = ((hash << 5) + hash) ^ c;
 
-      while (*string)
-      {
-            i = (int) *string;
-            ret_val ^= i;
-            ret_val <<= 1;
-            string ++;
-      }
-      return ret_val;
+    return hash;
 }
diff --git a/lib/strhash.h b/lib/strhash.h
index 3ecb432a34..dc6f6b759f 100644
--- a/lib/strhash.h
+++ b/lib/strhash.h
@@ -43,6 +43,7 @@
  */
 
 #ifndef _STRHASH_H_
+#include <stdint.h>
 
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
@@ -57,6 +58,9 @@
 #include <syslog.h>
 #include <errno.h>
 
-unsigned strhash(const char *string);
+unsigned strhash_seeded_djb2(uint32_t seed, const char *string);
+
+#define strhash(in)             strhash_seeded_djb2((0),  (in))
+#define strhash_seeded(sd, in)  strhash_seeded_djb2((sd), (in))
 
 #endif /* _STRHASH_H_ */

From f63695609c88a3f76129499bb49fb82e8155fb32 Mon Sep 17 00:00:00 2001
From: ellie timoney <ellie@fastmail.com>
Date: Wed, 19 May 2021 14:01:41 +1000
Subject: [PATCH 4/5] hash: use a seed when hashing

Part of CVE-2021-33582
---
 lib/hash.c | 9 ++++++---
 lib/hash.h | 5 +++++
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/lib/hash.c b/lib/hash.c
index cf85d9d2ca..1b025b21b5 100644
--- a/lib/hash.c
+++ b/lib/hash.c
@@ -48,6 +48,9 @@ hash_table *construct_hash_table(hash_table *table, size_t size, int use_mpool)
 	  fatal("construct_hash_table called without a size", EC_TEMPFAIL);
 
       table->size  = size;
+      do {
+        table->seed = rand();
+      } while (table->seed == 0);
 
       /* Allocate the table -- different for using memory pools and not */
       if(use_mpool) {
@@ -76,7 +79,7 @@ hash_table *construct_hash_table(hash_table *table, size_t size, int use_mpool)
 
 void *hash_insert(const char *key, void *data, hash_table *table)
 {
-      unsigned val = strhash(key) % table->size;
+      unsigned val = strhash_seeded(table->seed, key) % table->size;
       bucket *ptr, *newptr;
       bucket **prev;
 
@@ -163,7 +166,7 @@ void *hash_lookup(const char *key, hash_table *table)
       if (!table->size)
           return NULL;
 
-      val = strhash(key) % table->size;
+      val = strhash_seeded(table->seed, key) % table->size;
 
       if (!(table->table)[val])
             return NULL;
@@ -187,7 +190,7 @@ void *hash_lookup(const char *key, hash_table *table)
  * since it will leak memory until you get rid of the entire hash table */
 void *hash_del(char *key, hash_table *table)
 {
-      unsigned val = strhash(key) % table->size;
+      unsigned val = strhash_seeded(table->seed, key) % table->size;
       bucket *ptr, *last = NULL;
 
       if (!(table->table)[val])
diff --git a/lib/hash.h b/lib/hash.h
index 77d0dc8a33..c3bc1dc2f5 100644
--- a/lib/hash.h
+++ b/lib/hash.h
@@ -5,9 +5,13 @@
 #define HASH__H
 
 #include <stddef.h>           /* For size_t     */
+#include <stdint.h>
+
 #include "strhash.h"
 #include "mpool.h"
 
+#define HASH_TABLE_INITIALIZER {0, 0, NULL, NULL}
+
 /*
 ** A hash table consists of an array of these buckets.  Each bucket
 ** holds a copy of the key, a pointer to the data associated with the
@@ -32,6 +36,7 @@ typedef struct bucket {
 
 typedef struct hash_table {
     size_t size;
+    uint32_t seed;
     bucket **table;
     struct mpool *pool;
 } hash_table;

From 833c22bd7de5bbb591c2cb3705c9983b6d2b1fee Mon Sep 17 00:00:00 2001
From: ellie timoney <ellie@fastmail.com>
Date: Fri, 2 Jul 2021 10:34:20 +1000
Subject: [PATCH 5/5] hash: it's okay for seed to be zero sometimes

randomly not seeding is not any more predictable than any individual
random seed, and allowing it to be zero saves us having to deal with
preventing zeroes.
---
 lib/hash.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/lib/hash.c b/lib/hash.c
index 1b025b21b5..8081b429d5 100644
--- a/lib/hash.c
+++ b/lib/hash.c
@@ -48,9 +48,7 @@ hash_table *construct_hash_table(hash_table *table, size_t size, int use_mpool)
 	  fatal("construct_hash_table called without a size", EC_TEMPFAIL);
 
       table->size  = size;
-      do {
-        table->seed = rand();
-      } while (table->seed == 0);
+      table->seed = rand(); /* might be zero, that's okay */
 
       /* Allocate the table -- different for using memory pools and not */
       if(use_mpool) {