gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r12964 - in libmicrohttpd: . doc src/daemon src/examples sr


From: gnunet
Subject: [GNUnet-SVN] r12964 - in libmicrohttpd: . doc src/daemon src/examples src/include src/testcurl
Date: Fri, 10 Sep 2010 14:31:43 +0200

Author: grothoff
Date: 2010-09-10 14:31:43 +0200 (Fri, 10 Sep 2010)
New Revision: 12964

Modified:
   libmicrohttpd/ChangeLog
   libmicrohttpd/doc/microhttpd.texi
   libmicrohttpd/src/daemon/daemon.c
   libmicrohttpd/src/daemon/digestauth.c
   libmicrohttpd/src/daemon/internal.c
   libmicrohttpd/src/daemon/internal.h
   libmicrohttpd/src/examples/digest_auth_example.c
   libmicrohttpd/src/include/microhttpd.h
   libmicrohttpd/src/testcurl/daemontest_digestauth.c
Log:
Hi Christian,
        This patch isn't purely about the subject matter, but rather have some 
tiny
small stuff that concerns other things so keep an eye for those. Also have to
say that documentation is included, including updated example and update unit 
test.

        As for the performance part to it, its so complex to fully test it 
performance
wise, but it seems to work just fine, although keep an eye on how it does search
for the nonce to see if it is an attack or not, or if it is a whole new nonce
that needs to be added to the array, also have an eye for the rw-locks, I think
they are very useful and add a lot to the performance.

Thanks,
Amr Ali
  End of signed message
latest_nc_mhd.patch


Note that the actual changes do not include the rw-locks (not as portable, no 
real
benefit in this context anyway) and I also made some other minor changes.  
Original
patch follows.

diff --git a/doc/microhttpd.texi b/doc/microhttpd.texi
index a86a59a..cc7f7a8 100644
--- a/doc/microhttpd.texi
+++ b/doc/microhttpd.texi
@@ -458,12 +458,31 @@ specified, ``NORMAL'' is used.
 @cindex random
 Digest Authentication nonce's seed.
 This option must be followed by an "const char *" argument
-specifying a NULL terminated array of randomly generated values
-to be used in generating the server nonce when using digest authentication.
-It is a MUST to supply these values before utilizing any of MHD
-digest authentication functions, as otherwise, it will read from
-an arbitrary address in memory which results in an undefined behavior.
-  
+an array of randomly generated values to be used in generating
+the server nonce when using digest authentication.
+It is a MUST to supply these values if MHD is compiled with
+Digest Authentication enabled (default).
+
address@hidden MHD_OPTION_DIGEST_AUTH_RAND_SIZE
address@hidden digest auth
address@hidden random
+Digest Authentication random seed size.
+This option must be followed by a "unsigned int" argument
+that have the size (number of elements) of the random seed
+array. This helps so that the seed might have zero bytes
+so it won't have a problem with string functions that
+depend on null terminated character arrays.
+
address@hidden MHD_OPTION_NONCE_NC_SIZE
address@hidden digest auth
address@hidden replay attack
+Size of an array of nonce and nonce counter map.
+This option must be followed by an "unsigned int" argument
+that have the size (number of elements) of a map of
+a nonce and a nonce-counter. This option MUST be
+specified if MHD is compiled with
+Digest Authentication enabled (default).
+
 @item MHD_OPTION_LISTEN_SOCKET
 @cindex systemd
 Listen socket to use.  Pass a listen socket for MHD to use
diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c
index f68b9fb..8f0583c 100644
--- a/src/daemon/daemon.c
+++ b/src/daemon/daemon.c
@@ -1376,6 +1376,12 @@ parse_options_va (struct MHD_Daemon *daemon,
        case MHD_OPTION_DIGEST_AUTH_RANDOM:
          daemon->digest_auth_random = va_arg (ap, const char *);
          break;
+         case MHD_OPTION_DIGEST_AUTH_RAND_SIZE:
+         daemon->digest_auth_rand_size = va_arg (ap, unsigned int);
+         break;
+       case MHD_OPTION_NONCE_NC_SIZE:
+         daemon->nonce_nc_size = va_arg (ap, unsigned int);
+         break;
 #endif
        case MHD_OPTION_LISTEN_SOCKET:
          daemon->socket_fd = va_arg (ap, int);   
@@ -1407,6 +1413,8 @@ parse_options_va (struct MHD_Daemon *daemon,
                    return MHD_NO;
                  break;
                  /* all options taking 'unsigned int' */
+               case MHD_OPTION_NONCE_NC_SIZE:
+               case MHD_OPTION_DIGEST_AUTH_RAND_SIZE:
                case MHD_OPTION_CONNECTION_LIMIT:
                case MHD_OPTION_CONNECTION_TIMEOUT:
                case MHD_OPTION_PER_IP_CONNECTION_LIMIT:
@@ -1578,6 +1586,63 @@ MHD_start_daemon_va (unsigned int options,
       return NULL;
     }
 
+#ifdef DAUTH_SUPPORT
+  if (retVal->nonce_nc_size == 0)
+    {
+#if HAVE_MESSAGES
+         fprintf (stderr,
+                          "MHD nonce-nc map size cannot be 0\n");
+#endif
+         free (retVal);
+         return NULL;
+       }
+  else
+    {
+         retVal->nnc = calloc(retVal->nonce_nc_size, sizeof(struct 
MHD_NonceNc));
+
+         if (!retVal->nnc)
+           {
+#if HAVE_MESSAGES
+                 fprintf (stderr,
+                                 "An error has occured while allocating 
nonce-nc map\n");
+#endif
+                 free (retVal);
+                 return NULL;
+               }
+       }
+
+  if (retVal->digest_auth_rand_size == 0)
+    {
+#if HAVE_MESSAGES
+         fprintf(stderr,
+                         "MHD Digest Auth random seed array size must be > 
0\n");
+#endif
+         free (retVal);
+         return NULL;
+       }
+
+  if (retVal->digest_auth_random == NULL)
+    {
+#if HAVE_MESSAGES
+         fprintf (stderr,
+                         "MHD requires `digest_auth_random' to point to a char 
array"
+                         " of random values\n");
+#endif
+         free (retVal);
+         return NULL;
+       }
+
+  if (pthread_rwlock_init(&retVal->nnc_lock, NULL))
+    {
+#if HAVE_MESSAGES
+         fprintf (stderr,
+                         "Failed to initialize nonce-nc lock\n");
+#endif
+         free (retVal);
+         return NULL;
+       }
+#endif
+
   /* poll support currently only works with MHD_USE_THREAD_PER_CONNECTION */
   if ( (0 != (options & MHD_USE_POLL)) && 
        (0 == (options & MHD_USE_THREAD_PER_CONNECTION)) ) {
@@ -2012,6 +2077,11 @@ MHD_stop_daemon (struct MHD_Daemon *daemon)
        }
     }
 #endif
+
+#ifdef DAUTH_SUPPORT
+  free (daemon->nnc);
+  pthread_rwlock_destroy (&daemon->nnc_lock);
+#endif
   pthread_mutex_destroy (&daemon->per_ip_connection_mutex);
   free (daemon);
 }
diff --git a/src/daemon/digestauth.c b/src/daemon/digestauth.c
index 591538e..ad63b79 100644
--- a/src/daemon/digestauth.c
+++ b/src/daemon/digestauth.c
@@ -23,6 +23,7 @@
  * @author Amr Ali
  */
 
+#include "platform.h"
 #include "internal.h"
 #include "md5.h"
 
@@ -82,12 +83,12 @@ cvthex(const unsigned char *bin,
  * calculate H(A1) as per RFC2617 spec and store the
  * result in 'sessionkey'.
  *
- * @param alg FIXME: document
- * @param username FIXME: document
- * @param realm FIXME: document
- * @param password FIXME: document
- * @param nonce FIXME: document
- * @param cnonce FIXME: document
+ * @param alg The hash algorithm used, can be "md5" or "md5-sess"
+ * @param username A `char *' pointer to the username value
+ * @param realm A `char *' pointer to the realm value
+ * @param password A `char *' pointer to the password value
+ * @param nonce A `char *' pointer to the nonce value
+ * @param cnonce A `char *' pointer to the cnonce value
  * @param sessionkey pointer to buffer of HASH_MD5_HEX_LEN+1 bytes
  */
 static void
@@ -275,6 +276,69 @@ lookup_sub_value(char *dest,
   return 0;
 }
 
+/**
+ * Check nonce-nc map array with either new nonce counter
+ * or a whole new nonce.
+ *
+ * @param connection The MHD connection structure
+ * @param nonce A pointer that referenced a zero-terminated array of nonce
+ * @param nc The nonce counter
+ * @return 1 if successful, 0 if invalid
+ */
+static int
+check_nonce_nc(
+               struct MHD_Connection *connection,
+               const char *nonce,
+               unsigned int nc)
+{
+       static unsigned int pos = 0;
+       int i;
+
+       /*
+        * Look for the nonce, if it does exist and its corresponding
+        * nonce counter is less than the current nonce counter by 1,
+        * then only increase the nonce counter by one.
+        */
+
+       pthread_rwlock_rdlock(&connection->daemon->nnc_lock);
+
+       for (i = 0; i < connection->daemon->nonce_nc_size; ++i) {
+               if (0 == strcmp(connection->daemon->nnc[i].nonce, nonce)) {
+                       if (nc - 1 == connection->daemon->nnc[i].nc) {
+                               
pthread_rwlock_unlock(&connection->daemon->nnc_lock);
+                               
pthread_rwlock_wrlock(&connection->daemon->nnc_lock);
+                               ++connection->daemon->nnc[i].nc;
+                               
pthread_rwlock_unlock(&connection->daemon->nnc_lock);
+                               return 1;
+                       } else {
+                               /*
+                                * Nonce is found but its nonce counter is 
invalid.
+                                */
+                               
pthread_rwlock_unlock(&connection->daemon->nnc_lock);
+                               
pthread_rwlock_wrlock(&connection->daemon->nnc_lock);
+                               connection->daemon->nnc[i].nc = -1;
+                               
pthread_rwlock_unlock(&connection->daemon->nnc_lock);
+                               return 0;
+                       }
+               }
+       }
+
+       pthread_rwlock_unlock(&connection->daemon->nnc_lock);
+
+       /*
+        * nonce is not found and will be added to the array.
+        */
+
+       pthread_rwlock_wrlock(&connection->daemon->nnc_lock);
+       strncpy(connection->daemon->nnc[pos].nonce, nonce, strlen(nonce));
+       connection->daemon->nnc[pos].nc = 1;
+
+       pos = pos >= connection->daemon->nonce_nc_size ? 0 : pos + 1;
+
+       pthread_rwlock_unlock(&connection->daemon->nnc_lock);
+
+       return 1;
+}
 
 /**
  * Get the username from the authorization header sent by the client
@@ -316,6 +380,7 @@ MHD_digest_auth_get_username(struct MHD_Connection 
*connection)
  * @param nonce_time The amount of time in seconds for a nonce to be invalid
  * @param method HTTP method
  * @param rnd A pointer to a character array for the random seed
+ * @param rnd_size The size of the random seed array
  * @param uri HTTP URI
  * @param realm A string of characters that describes the realm of auth.
  * @param nonce A pointer to a character array for the nonce to put in
@@ -324,6 +389,7 @@ static void
 calculate_nonce (uint32_t nonce_time,
                 const char *method,
                 const char *rnd,
+                unsigned int rnd_size,
                 const char *uri,
                 const char *realm,
                 char *nonce)
@@ -342,7 +408,7 @@ calculate_nonce (uint32_t nonce_time,
   MD5Update(&md5, ":", 1);
   MD5Update(&md5, method, strlen(method));
   MD5Update(&md5, ":", 1);
-  MD5Update(&md5, rnd, strlen(rnd));
+  MD5Update(&md5, rnd, rnd_size);
   MD5Update(&md5, ":", 1);
   MD5Update(&md5, uri, strlen(uri));
   MD5Update(&md5, ":", 1);
@@ -436,7 +502,7 @@ MHD_digest_auth_check(struct MHD_Connection *connection,
       return MHD_NO;
       
     /* 8 = 4 hexadecimal numbers for the timestamp */  
-    nonce_time = strtoul(nonce + len - 8, 0, 16);  
+    nonce_time = strtoul(nonce + len - 8, (char **)NULL, 16);  
     t = (uint32_t) time(NULL);    
     /*
      * First level vetting for the nonce validity
@@ -449,14 +515,15 @@ MHD_digest_auth_check(struct MHD_Connection *connection,
     calculate_nonce (nonce_time,
                     connection->method,
                     connection->daemon->digest_auth_random,
+                        connection->daemon->digest_auth_rand_size,
                     uri,
                     realm,
                     noncehashexp);
     /*
      * Second level vetting for the nonce validity
      * if the timestamp attached to the nonce is valid
-     * and possibility fabricated (in case of an attack)
-     * the attacker must also know the password to be
+     * and possibly fabricated (in case of an attack)
+     * the attacker must also know the random seed to be
      * able to generate a "sane" nonce, which if he does
      * not, the nonce fabrication process going to be
      * very hard to achieve.
@@ -471,6 +538,15 @@ MHD_digest_auth_check(struct MHD_Connection *connection,
         (0 == lookup_sub_value(nc, sizeof (nc), header, "nc"))  ||
         (0 == lookup_sub_value(response, sizeof (response), header, 
"response")) )
       return MHD_NO;
+
+       /*
+        * Checking if that combination of nonce and nc is sound
+        * and not a replay attack attempt. Also adds the nonce
+        * to the nonce-nc map if it does not exist there.
+        */
+
+       if (! check_nonce_nc(connection, nonce, strtoul(nc, (char **)NULL, 16)) 
)
+         return MHD_NO;
     
     digest_calc_ha1("md5",
                    username,
@@ -519,6 +595,7 @@ MHD_queue_auth_fail_response(struct MHD_Connection 
*connection,
   calculate_nonce ((uint32_t) time(NULL),
                   connection->method,
                   connection->daemon->digest_auth_random,
+                  connection->daemon->digest_auth_rand_size,
                   connection->url,
                   realm,
                   nonce);
diff --git a/src/daemon/internal.h b/src/daemon/internal.h
index 3887b27..9308355 100644
--- a/src/daemon/internal.h
+++ b/src/daemon/internal.h
@@ -93,6 +93,24 @@ struct MHD_Pollfd {
   enum MHD_PollActions events;
 };
 
+#ifdef DAUTH_SUPPORT
+/**
+ * A structure representing the internal holder of the
+ * nonce-nc map.
+ */
+struct MHD_NonceNc {
+       /**
+        * Nonce value: 32(MD5 Hex) + 8(Timestamp Hex) + 1(NULL)
+        */
+       char nonce[41];
+
+       /**
+        * Nonce counter, a value that increases for each subsequent
+        * request for the same nonce.
+        */
+       int nc;
+};
+#endif
 
 #if HAVE_MESSAGES
 /**
@@ -866,12 +884,34 @@ struct MHD_Daemon
 #endif
 
 #ifdef DAUTH_SUPPORT
+
   /**
    * Character array of random values.
    */
   const char *digest_auth_random;
 
+  /**
+   * Size of `digest_auth_random.
+   */
+  unsigned int digest_auth_rand_size;
+
+  /**
+   * Size of the nonce-nc array.
+   */
+  unsigned int nonce_nc_size;
+
+  /**
+   * An array that contains the map nonce-nc.
+   */
+  struct MHD_NonceNc *nnc;
+
+  /**
+   * A rw-lock for synchronizing access to `nnc'.
+   */
+  pthread_rwlock_t nnc_lock;
+
 #endif
+
   /**
    * Pointer to master daemon (NULL if this is the master)
    */
diff --git a/src/examples/digest_auth_example.c 
b/src/examples/digest_auth_example.c
index 51c81b8..9ead210 100644
--- a/src/examples/digest_auth_example.c
+++ b/src/examples/digest_auth_example.c
@@ -90,7 +90,7 @@ int
 main (int argc, char *const *argv)
 {
   int fd;
-  char rnd[9];
+  char rnd[8];
   size_t len;
   size_t off;
   struct MHD_Daemon *d;
@@ -123,11 +123,12 @@ main (int argc, char *const *argv)
       off += len;
     }
   (void) close(fd);
-  rnd[8] = '\0';
   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
                         atoi (argv[1]),
                         NULL, NULL, &ahc_echo, PAGE,
                        MHD_OPTION_DIGEST_AUTH_RANDOM, rnd,
+                       MHD_OPTION_DIGEST_AUTH_RAND_SIZE, sizeof(rnd),
+                       MHD_OPTION_NONCE_NC_SIZE, 300,
                        MHD_OPTION_END);
   if (d == NULL)
     return 1;
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index a74bf55..45d49f0 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -527,8 +527,22 @@ enum MHD_OPTION
    * Auth module. This option should be followed by an "const char *"
    * argument.
    */
-  MHD_OPTION_DIGEST_AUTH_RANDOM = 17
+  MHD_OPTION_DIGEST_AUTH_RANDOM = 17,
 
+  /**
+   * Size of Digest Auth random seed. This option should be followed
+   * by an "unsigned int" argument. It is needed as the random seed array
+   * may contain values of zero, which would cause problems for
+   * string functions.
+   */
+  MHD_OPTION_DIGEST_AUTH_RAND_SIZE = 18,
+
+  /**
+   * Size of the internal array holding the map of the nonce and
+   * the nonce counter. This option should be followed by a "unsigend int"
+   * argument.
+   */
+  MHD_OPTION_NONCE_NC_SIZE = 19
 };
 
 
diff --git a/src/testcurl/daemontest_digestauth.c 
b/src/testcurl/daemontest_digestauth.c
index e05d857..86ea305 100644
--- a/src/testcurl/daemontest_digestauth.c
+++ b/src/testcurl/daemontest_digestauth.c
@@ -129,7 +129,7 @@ testDigestAuth ()
   size_t len;
   size_t off = 0;
   char buf[2048];
-  char rnd[9];
+  char rnd[8];
 
   cbc.buf = buf;
   cbc.size = 2048;
@@ -156,10 +156,11 @@ testDigestAuth ()
          off += len;
        }
   (void) close(fd);
-  rnd[8] = '\0';
   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
                         1337, NULL, NULL, &ahc_echo, PAGE,
                                                MHD_OPTION_DIGEST_AUTH_RANDOM, 
rnd,
+                                               
MHD_OPTION_DIGEST_AUTH_RAND_SIZE, sizeof(rnd),
+                                               MHD_OPTION_NONCE_NC_SIZE, 300,
                                                MHD_OPTION_END);
   if (d == NULL)
     return 1;



Modified: libmicrohttpd/ChangeLog
===================================================================
--- libmicrohttpd/ChangeLog     2010-09-10 10:07:26 UTC (rev 12963)
+++ libmicrohttpd/ChangeLog     2010-09-10 12:31:43 UTC (rev 12964)
@@ -1,3 +1,6 @@
+Fri Sep 10 14:29:37 CEST 2010
+       Adding proper nonce counter checking for digest authentication. -CG/AA
+
 Sat Sep  4 21:55:52 CEST 2010
        Digest authentication now seems to be working. -CG/AA
 

Modified: libmicrohttpd/doc/microhttpd.texi
===================================================================
--- libmicrohttpd/doc/microhttpd.texi   2010-09-10 10:07:26 UTC (rev 12963)
+++ libmicrohttpd/doc/microhttpd.texi   2010-09-10 12:31:43 UTC (rev 12964)
@@ -457,13 +457,37 @@
 @cindex digest auth
 @cindex random
 Digest Authentication nonce's seed.
-This option must be followed by an "const char *" argument
-specifying a NULL terminated array of randomly generated values
-to be used in generating the server nonce when using digest authentication.
-It is a MUST to supply these values before utilizing any of MHD
-digest authentication functions, as otherwise, it will read from
-an arbitrary address in memory which results in an undefined behavior.
-  
+
+This option should be followed by two arguments.  First an integer of
+type "size_t" which specifies the size of the buffer pointed to by the
+second argument in bytes.  Note that the application must ensure that
+the buffer of the second argument remains allocated and unmodified
+while the deamon is running.  For security, you SHOULD provide a fresh
+random nonce when using MHD with Digest Authentication.  
+
address@hidden MHD_OPTION_NONCE_NC_SIZE
address@hidden digest auth
address@hidden replay attack
+
+Size of an array of nonce and nonce counter map.  This option must be
+followed by an "unsigned int" argument that have the size (number of
+elements) of a map of a nonce and a nonce-counter.  If this option
+is not specified, a default value of 4 will be used (which might be
+too small for servers handling many requests).  If you do not use
+digest authentication at all, you can specify a value of zero to
+save some memory.
+
+You should calculate the value of NC_SIZE based on the number of
+connections per second multiplied by your expected session duration
+plus a factor of about two for hash table collisions.  For example, if
+you expect 100 digest-authenticated connections per second and the
+average user to stay on your site for 5 minutes, then you likely need
+a value of about 60000.  On the other hand, if you can only expect
+only 10 digest-authenticated connections per second, tolerate browsers
+getting a fresh nonce for each request and expect a HTTP request
+latency of 250 ms, then a value of about 5 should be fine.
+
+
 @item MHD_OPTION_LISTEN_SOCKET
 @cindex systemd
 Listen socket to use.  Pass a listen socket for MHD to use

Modified: libmicrohttpd/src/daemon/daemon.c
===================================================================
--- libmicrohttpd/src/daemon/daemon.c   2010-09-10 10:07:26 UTC (rev 12963)
+++ libmicrohttpd/src/daemon/daemon.c   2010-09-10 12:31:43 UTC (rev 12964)
@@ -1374,8 +1374,12 @@
 #endif
 #ifdef DAUTH_SUPPORT
        case MHD_OPTION_DIGEST_AUTH_RANDOM:
+         daemon->digest_auth_rand_size = va_arg (ap, size_t);
          daemon->digest_auth_random = va_arg (ap, const char *);
          break;
+       case MHD_OPTION_NONCE_NC_SIZE:
+         daemon->nonce_nc_size = va_arg (ap, unsigned int);
+         break;
 #endif
        case MHD_OPTION_LISTEN_SOCKET:
          daemon->socket_fd = va_arg (ap, int);   
@@ -1407,6 +1411,7 @@
                    return MHD_NO;
                  break;
                  /* all options taking 'unsigned int' */
+               case MHD_OPTION_NONCE_NC_SIZE:
                case MHD_OPTION_CONNECTION_LIMIT:
                case MHD_OPTION_CONNECTION_TIMEOUT:
                case MHD_OPTION_PER_IP_CONNECTION_LIMIT:
@@ -1433,7 +1438,6 @@
                case MHD_OPTION_HTTPS_MEM_KEY:
                case MHD_OPTION_HTTPS_MEM_CERT:
                case MHD_OPTION_HTTPS_PRIORITIES:
-               case MHD_OPTION_DIGEST_AUTH_RANDOM:
                case MHD_OPTION_ARRAY:
                  if (MHD_YES != parse_options (daemon,
                                                servaddr,
@@ -1455,7 +1459,15 @@
                                                MHD_OPTION_END))
                    return MHD_NO;
                  break;
-                 
+                 /* options taking size_t-number followed by pointer */
+               case MHD_OPTION_DIGEST_AUTH_RANDOM:
+                 if (MHD_YES != parse_options (daemon,
+                                               servaddr,
+                                               opt,
+                                               (size_t) oa[i].value,
+                                               oa[i].ptr_value,
+                                               MHD_OPTION_END))
+                   return MHD_NO;                
                default:
                  return MHD_NO;
                }
@@ -1545,6 +1557,11 @@
   retVal->pool_size = MHD_POOL_SIZE_DEFAULT;
   retVal->unescape_callback = &MHD_http_unescape;
   retVal->connection_timeout = 0;       /* no timeout */
+#ifdef DAUTH_SUPPORT
+  retVal->digest_auth_rand_size = 0;
+  retVal->digest_auth_random = NULL;
+  retVal->nonce_nc_size = 4; /* tiny */
+#endif
 #if HAVE_MESSAGES
   retVal->custom_error_log =
     (void (*)(void *, const char *, va_list)) &vfprintf;
@@ -1578,24 +1595,61 @@
       return NULL;
     }
 
+#ifdef DAUTH_SUPPORT
+  if (retVal->nonce_nc_size > 0) 
+    {
+      if ( ( (size_t) (retVal->nonce_nc_size * sizeof(struct MHD_NonceNc))) / 
+          sizeof(struct MHD_NonceNc) != retVal->nonce_nc_size)
+       {
+#if HAVE_MESSAGES
+         MHD_DLOG (retVal,
+                   "Specified value for NC_SIZE too large\n");
+#endif
+         free (retVal);
+         return NULL;    
+       }
+      retVal->nnc = malloc (retVal->nonce_nc_size * sizeof(struct 
MHD_NonceNc));
+      if (NULL == retVal->nnc)
+           {
+#if HAVE_MESSAGES
+             MHD_DLOG (retVal,
+                       "Failed to allocate memory for nonce-nc map: %s\n",
+                       STRERROR (errno));
+#endif
+             free (retVal);
+             return NULL;
+           }
+    }
+  if (0 != pthread_mutex_init (&retVal->nnc_lock, NULL))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (retVal,
+               "MHD failed to initialize nonce-nc mutex\n");
+#endif
+      free (retVal);
+      return NULL;
+    }
+#endif
+
   /* poll support currently only works with MHD_USE_THREAD_PER_CONNECTION */
   if ( (0 != (options & MHD_USE_POLL)) && 
-       (0 == (options & MHD_USE_THREAD_PER_CONNECTION)) ) {
+       (0 == (options & MHD_USE_THREAD_PER_CONNECTION)) ) 
+    {
 #if HAVE_MESSAGES
-      fprintf (stderr,
-               "MHD poll support only works with 
MHD_USE_THREAD_PER_CONNECTION\n");
+      MHD_DLOG (retVal,
+               "MHD poll support only works with 
MHD_USE_THREAD_PER_CONNECTION\n");
 #endif
       free (retVal);
       return NULL;
-  }
+    }
 
   /* Thread pooling currently works only with internal select thread model */
-  if ((0 == (options & MHD_USE_SELECT_INTERNALLY))
-      && (retVal->worker_pool_size > 0))
+  if ( (0 == (options & MHD_USE_SELECT_INTERNALLY)) && 
+       (retVal->worker_pool_size > 0) )
     {
 #if HAVE_MESSAGES
-      fprintf (stderr,
-               "MHD thread pooling only works with 
MHD_USE_SELECT_INTERNALLY\n");
+      MHD_DLOG (retVal,
+               "MHD thread pooling only works with 
MHD_USE_SELECT_INTERNALLY\n");
 #endif
       free (retVal);
       return NULL;
@@ -1605,8 +1659,8 @@
   if (0 != (options & (MHD_USE_SELECT_INTERNALLY | 
MHD_USE_THREAD_PER_CONNECTION)))
     {
 #if HAVE_MESSAGES
-      fprintf (stderr,
-               "Threaded operations are not supported on Symbian.\n");
+      MHD_DLOG (retVal,
+               "Threaded operations are not supported on Symbian.\n");
 #endif
       free (retVal);
       return NULL;
@@ -1620,7 +1674,8 @@
 #else
       {
 #if HAVE_MESSAGES
-       fprintf (stderr, "AF_INET6 not supported\n");
+       MHD_DLOG (retVal, 
+                 "AF_INET6 not supported\n");
 #endif
        free (retVal);
        return NULL;
@@ -1632,7 +1687,9 @@
        {
 #if HAVE_MESSAGES
          if ((options & MHD_USE_DEBUG) != 0)
-           FPRINTF (stderr, "Call to socket failed: %s\n", STRERROR (errno));
+           MHD_DLOG (retVal, 
+                     "Call to socket failed: %s\n", 
+                     STRERROR (errno));
 #endif
          free (retVal);
          return NULL;
@@ -1643,7 +1700,9 @@
                       &on, sizeof (on)) < 0) && ((options & MHD_USE_DEBUG) != 
0))
        {
 #if HAVE_MESSAGES
-         FPRINTF (stderr, "setsockopt failed: %s\n", STRERROR (errno));
+         MHD_DLOG (retVal, 
+                   "setsockopt failed: %s\n", 
+                   STRERROR (errno));
 #endif
        }
       
@@ -1701,8 +1760,10 @@
        {
 #if HAVE_MESSAGES
          if ((options & MHD_USE_DEBUG) != 0)
-           FPRINTF (stderr,
-                    "Failed to bind to port %u: %s\n", port, STRERROR (errno));
+           MHD_DLOG (retVal,
+                     "Failed to bind to port %u: %s\n", 
+                     port, 
+                     STRERROR (errno));
 #endif
          CLOSE (socket_fd);
          free (retVal);
@@ -1713,8 +1774,9 @@
        {
 #if HAVE_MESSAGES
          if ((options & MHD_USE_DEBUG) != 0)
-           FPRINTF (stderr,
-                    "Failed to listen for connections: %s\n", STRERROR 
(errno));
+           MHD_DLOG (retVal,
+                     "Failed to listen for connections: %s\n", 
+                     STRERROR (errno));
 #endif
          CLOSE (socket_fd);
          free (retVal);
@@ -1731,10 +1793,10 @@
     {
 #if HAVE_MESSAGES
       if ((options & MHD_USE_DEBUG) != 0)
-        FPRINTF (stderr,
-                 "Socket descriptor larger than FD_SETSIZE: %d > %d\n",
-                 socket_fd,
-                 FD_SETSIZE);
+        MHD_DLOG (retVal,
+                 "Socket descriptor larger than FD_SETSIZE: %d > %d\n",
+                 socket_fd,
+                 FD_SETSIZE);
 #endif
       CLOSE (socket_fd);
       free (retVal);
@@ -1758,7 +1820,8 @@
   if ((0 != (options & MHD_USE_SSL)) && (0 != MHD_TLS_init (retVal)))
     {
 #if HAVE_MESSAGES
-      MHD_DLOG (retVal, "Failed to initialize TLS support\n");
+      MHD_DLOG (retVal, 
+               "Failed to initialize TLS support\n");
 #endif
       CLOSE (socket_fd);
       pthread_mutex_destroy (&retVal->per_ip_connection_mutex);
@@ -1774,7 +1837,8 @@
     {
 #if HAVE_MESSAGES
       MHD_DLOG (retVal,
-                "Failed to create listen thread: %s\n", STRERROR 
(res_thread_create));
+                "Failed to create listen thread: %s\n", 
+               STRERROR (res_thread_create));
 #endif
       pthread_mutex_destroy (&retVal->per_ip_connection_mutex);
       free (retVal);
@@ -2012,6 +2076,11 @@
        }
     }
 #endif
+
+#ifdef DAUTH_SUPPORT
+  free (daemon->nnc);
+  pthread_mutex_destroy (&daemon->nnc_lock);
+#endif
   pthread_mutex_destroy (&daemon->per_ip_connection_mutex);
   free (daemon);
 }

Modified: libmicrohttpd/src/daemon/digestauth.c
===================================================================
--- libmicrohttpd/src/daemon/digestauth.c       2010-09-10 10:07:26 UTC (rev 
12963)
+++ libmicrohttpd/src/daemon/digestauth.c       2010-09-10 12:31:43 UTC (rev 
12964)
@@ -23,6 +23,7 @@
  * @author Amr Ali
  */
 
+#include "platform.h"
 #include "internal.h"
 #include "md5.h"
 
@@ -44,11 +45,6 @@
 #define MAX_REALM_LENGTH 256
 
 /**
- * Maximum length of a nonce in digest authentication.
- */
-#define MAX_NONCE_LENGTH 128
-
-/**
  * Maximum length of the response in digest authentication.
  */
 #define MAX_AUTH_RESPONSE_LENGTH 128
@@ -82,12 +78,12 @@
  * calculate H(A1) as per RFC2617 spec and store the
  * result in 'sessionkey'.
  *
- * @param alg FIXME: document
- * @param username FIXME: document
- * @param realm FIXME: document
- * @param password FIXME: document
- * @param nonce FIXME: document
- * @param cnonce FIXME: document
+ * @param alg The hash algorithm used, can be "md5" or "md5-sess"
+ * @param username A `char *' pointer to the username value
+ * @param realm A `char *' pointer to the realm value
+ * @param password A `char *' pointer to the password value
+ * @param nonce A `char *' pointer to the nonce value
+ * @param cnonce A `char *' pointer to the cnonce value
  * @param sessionkey pointer to buffer of HASH_MD5_HEX_LEN+1 bytes
  */
 static void
@@ -277,6 +273,63 @@
 
 
 /**
+ * Check nonce-nc map array with either new nonce counter
+ * or a whole new nonce.
+ *
+ * @param connection The MHD connection structure
+ * @param nonce A pointer that referenced a zero-terminated array of nonce
+ * @param nc The nonce counter, zero to add the nonce to the array
+ * @return MHD_YES if successful, MHD_NO if invalid (or we have no NC array)
+ */
+static int
+check_nonce_nc (struct MHD_Connection *connection,
+               const char *nonce,
+               unsigned int nc)
+{
+  uint32_t off;
+  uint32_t mod;
+  const char *np;
+
+  mod = connection->daemon->nonce_nc_size;
+  if (0 == mod)
+    return MHD_NO; /* no array! */
+  /* super-fast xor-based "hash" function for HT lookup in nonce array */
+  off = 0;
+  np = nonce;
+  while (*np != '\0')
+    {
+      off = (off << 8) | (*np & (off >> 24));
+      np++;
+    }
+  off = off % mod;
+  /*
+   * Look for the nonce, if it does exist and its corresponding
+   * nonce counter is less than the current nonce counter by 1,
+   * then only increase the nonce counter by one.
+   */
+  
+  pthread_mutex_lock(&connection->daemon->nnc_lock);
+  if (nc == 0)
+    {
+      strcpy(connection->daemon->nnc[off].nonce, 
+            nonce);
+      connection->daemon->nnc[off].nc = 0;  
+      pthread_mutex_unlock(&connection->daemon->nnc_lock);
+      return MHD_YES;
+    }
+  if ( (nc >= connection->daemon->nnc[off].nc) ||
+       (0 != strcmp(connection->daemon->nnc[off].nonce, nonce)) )
+    {
+      pthread_mutex_unlock(&connection->daemon->nnc_lock);
+      return MHD_NO;
+    }
+  connection->daemon->nnc[off].nc = nc;
+  pthread_mutex_unlock(&connection->daemon->nnc_lock);
+  return MHD_YES;
+}
+
+
+/**
  * Get the username from the authorization header sent by the client
  *
  * @param connection The MHD connection structure
@@ -316,6 +369,7 @@
  * @param nonce_time The amount of time in seconds for a nonce to be invalid
  * @param method HTTP method
  * @param rnd A pointer to a character array for the random seed
+ * @param rnd_size The size of the random seed array
  * @param uri HTTP URI
  * @param realm A string of characters that describes the realm of auth.
  * @param nonce A pointer to a character array for the nonce to put in
@@ -324,6 +378,7 @@
 calculate_nonce (uint32_t nonce_time,
                 const char *method,
                 const char *rnd,
+                unsigned int rnd_size,
                 const char *uri,
                 const char *realm,
                 char *nonce)
@@ -342,7 +397,8 @@
   MD5Update(&md5, ":", 1);
   MD5Update(&md5, method, strlen(method));
   MD5Update(&md5, ":", 1);
-  MD5Update(&md5, rnd, strlen(rnd));
+  if (rnd_size > 0)
+    MD5Update(&md5, rnd, rnd_size);
   MD5Update(&md5, ":", 1);
   MD5Update(&md5, uri, strlen(uri));
   MD5Update(&md5, ":", 1);
@@ -388,6 +444,7 @@
   uint32_t nonce_time;
   uint32_t t;
   size_t left; /* number of characters left in 'header' for 'uri' */
+  unsigned int nci;
 
   header = MHD_lookup_connection_value(connection,
                                       MHD_HEADER_KIND,
@@ -436,7 +493,7 @@
       return MHD_NO;
       
     /* 8 = 4 hexadecimal numbers for the timestamp */  
-    nonce_time = strtoul(nonce + len - 8, 0, 16);  
+    nonce_time = strtoul(nonce + len - 8, (char **)NULL, 16);  
     t = (uint32_t) time(NULL);    
     /*
      * First level vetting for the nonce validity
@@ -449,14 +506,15 @@
     calculate_nonce (nonce_time,
                     connection->method,
                     connection->daemon->digest_auth_random,
+                    connection->daemon->digest_auth_rand_size,
                     uri,
                     realm,
                     noncehashexp);
     /*
      * Second level vetting for the nonce validity
      * if the timestamp attached to the nonce is valid
-     * and possibility fabricated (in case of an attack)
-     * the attacker must also know the password to be
+     * and possibly fabricated (in case of an attack)
+     * the attacker must also know the random seed to be
      * able to generate a "sane" nonce, which if he does
      * not, the nonce fabrication process going to be
      * very hard to achieve.
@@ -468,10 +526,21 @@
                                sizeof (cnonce), 
                                header, "cnonce")) ||
         /*      (0 == lookup_sub_value(qop, sizeof (qop), header, "qop")) || 
// Uncomment when supporting "auth-int" */
+        (0 != strcmp (qop, "auth")) ||
         (0 == lookup_sub_value(nc, sizeof (nc), header, "nc"))  ||
+        (1 != sscanf (nc, "%u", &nci)) ||
         (0 == lookup_sub_value(response, sizeof (response), header, 
"response")) )
       return MHD_NO;
     
+    /*
+     * Checking if that combination of nonce and nc is sound
+     * and not a replay attack attempt. Also adds the nonce
+     * to the nonce-nc map if it does not exist there.
+     */
+    
+    if (MHD_YES != check_nonce_nc (connection, nonce, nci))
+      return MHD_NO;
+    
     digest_calc_ha1("md5",
                    username,
                    realm,
@@ -514,15 +583,22 @@
   size_t hlen;
   char nonce[HASH_MD5_HEX_LEN + 9];
 
-  
   /* Generating the server nonce */  
   calculate_nonce ((uint32_t) time(NULL),
                   connection->method,
                   connection->daemon->digest_auth_random,
+                  connection->daemon->digest_auth_rand_size,
                   connection->url,
                   realm,
                   nonce);
-  
+  if (MHD_YES != check_nonce_nc (connection, nonce, 0))
+    {
+#if HAVE_MESSAGES
+      MHD_DLOG (connection->daemon, 
+               "Could not register nonce (is the nonce array size zero?).\n");
+#endif
+      return MHD_NO;  
+    }
   /* Building the authentication header */
   hlen = snprintf(NULL,
                  0,

Modified: libmicrohttpd/src/daemon/internal.c
===================================================================
--- libmicrohttpd/src/daemon/internal.c 2010-09-10 10:07:26 UTC (rev 12963)
+++ libmicrohttpd/src/daemon/internal.c 2010-09-10 12:31:43 UTC (rev 12964)
@@ -101,13 +101,6 @@
 }
 #endif
 
-void
-MHD_tls_log_func (int level, const char *str)
-{
-#ifdef HAVE_MESSAGES
-  FPRINTF (stderr, "|<%d>| %s", level, str);
-#endif
-}
 
 /**
  * Process escape sequences ('+'=space, %HH) Updates val in place; the

Modified: libmicrohttpd/src/daemon/internal.h
===================================================================
--- libmicrohttpd/src/daemon/internal.h 2010-09-10 10:07:26 UTC (rev 12963)
+++ libmicrohttpd/src/daemon/internal.h 2010-09-10 12:31:43 UTC (rev 12964)
@@ -81,7 +81,8 @@
 /**
  * Socket descriptor and events we care about.
  */
-struct MHD_Pollfd {
+struct MHD_Pollfd 
+{
   /**
    * Socket descriptor.
    */
@@ -94,6 +95,33 @@
 };
 
 
+/**
+ * Maximum length of a nonce in digest authentication.
+ * 32(MD5 Hex) + 8(Timestamp Hex) + 1(NULL)
+ */
+#define MAX_NONCE_LENGTH 41
+
+
+/**
+ * A structure representing the internal holder of the
+ * nonce-nc map.
+ */
+struct MHD_NonceNc 
+{
+  
+  /**
+   * Nonce counter, a value that increases for each subsequent
+   * request for the same nonce.
+   */
+  unsigned int nc;
+
+  /**
+   * Nonce value: 
+   */
+  char nonce[MAX_NONCE_LENGTH];
+
+};
+
 #if HAVE_MESSAGES
 /**
  * fprintf-like helper function for logging debug
@@ -102,7 +130,6 @@
 void MHD_DLOG (const struct MHD_Daemon *daemon, const char *format, ...);
 
 #endif
-void MHD_tls_log_func (int level, const char *str);
 
 /**
  * Process escape sequences ('+'=space, %HH) Updates val in place; the
@@ -775,11 +802,41 @@
 #endif
 
   /**
+   * Pointer to master daemon (NULL if this is the master)
+   */
+  struct MHD_Daemon *master;
+
+  /**
+   * Worker daemons (one per thread)
+   */
+  struct MHD_Daemon *worker_pool;
+
+  /**
+   * Table storing number of connections per IP
+   */
+  void *per_ip_connection_count;
+
+  /**
+   * Size of the per-connection memory pools.
+   */
+  size_t pool_size;
+
+  /**
+   * Number of worker daemons
+   */
+  unsigned int worker_pool_size;
+
+  /**
    * PID of the select thread (if we have internal select)
    */
   pthread_t pid;
 
   /**
+   * Mutex for per-IP connection counts
+   */
+  pthread_mutex_t per_ip_connection_mutex;
+
+  /**
    * Listen socket.
    */
   int socket_fd;
@@ -790,11 +847,6 @@
   int shutdown;
 
   /**
-   * Size of the per-connection memory pools.
-   */
-  size_t pool_size;
-
-  /**
    * Limit on the number of parallel connections.
    */
   unsigned int max_connections;
@@ -812,16 +864,6 @@
   unsigned int per_ip_connection_limit;
 
   /**
-   * Table storing number of connections per IP
-   */
-  void *per_ip_connection_count;
-
-  /**
-   * Mutex for per-IP connection counts
-   */
-  pthread_mutex_t per_ip_connection_mutex;
-
-  /**
    * Daemon's options.
    */
   enum MHD_OPTION options;
@@ -866,26 +908,34 @@
 #endif
 
 #ifdef DAUTH_SUPPORT
+
   /**
    * Character array of random values.
    */
   const char *digest_auth_random;
 
-#endif
   /**
-   * Pointer to master daemon (NULL if this is the master)
+   * An array that contains the map nonce-nc.
    */
-  struct MHD_Daemon *master;
+  struct MHD_NonceNc *nnc;
 
   /**
-   * Worker daemons (one per thread)
+   * A rw-lock for synchronizing access to `nnc'.
    */
-  struct MHD_Daemon *worker_pool;
+  pthread_mutex_t nnc_lock;
 
   /**
-   * Number of worker daemons
+   * Size of `digest_auth_random.
    */
-  unsigned int worker_pool_size;
+  unsigned int digest_auth_rand_size;
+
+  /**
+   * Size of the nonce-nc array.
+   */
+  unsigned int nonce_nc_size;
+
+#endif
+
 };
 
 

Modified: libmicrohttpd/src/examples/digest_auth_example.c
===================================================================
--- libmicrohttpd/src/examples/digest_auth_example.c    2010-09-10 10:07:26 UTC 
(rev 12963)
+++ libmicrohttpd/src/examples/digest_auth_example.c    2010-09-10 12:31:43 UTC 
(rev 12964)
@@ -90,7 +90,7 @@
 main (int argc, char *const *argv)
 {
   int fd;
-  char rnd[9];
+  char rnd[8];
   size_t len;
   size_t off;
   struct MHD_Daemon *d;
@@ -123,11 +123,11 @@
       off += len;
     }
   (void) close(fd);
-  rnd[8] = '\0';
   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
                         atoi (argv[1]),
                         NULL, NULL, &ahc_echo, PAGE,
-                       MHD_OPTION_DIGEST_AUTH_RANDOM, rnd,
+                       MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof(rnd), rnd,
+                       MHD_OPTION_NONCE_NC_SIZE, 300,
                        MHD_OPTION_END);
   if (d == NULL)
     return 1;

Modified: libmicrohttpd/src/include/microhttpd.h
===================================================================
--- libmicrohttpd/src/include/microhttpd.h      2010-09-10 10:07:26 UTC (rev 
12963)
+++ libmicrohttpd/src/include/microhttpd.h      2010-09-10 12:31:43 UTC (rev 
12964)
@@ -524,11 +524,21 @@
 
   /**
    * Memory pointer for the random values to be used by the Digest
-   * Auth module. This option should be followed by an "const char *"
+   * Auth module. This option should be followed by two arguments.
+   * First an integer of type  "size_t" which specifies the size
+   * of the buffer pointed to by the second argument in bytes.  
+   * Note that the application must ensure that the buffer of the
+   * second argument remains allocated and unmodified while the
+   * deamon is running.
+   */
+  MHD_OPTION_DIGEST_AUTH_RANDOM = 17,
+
+  /**
+   * Size of the internal array holding the map of the nonce and
+   * the nonce counter. This option should be followed by a "unsigend int"
    * argument.
    */
-  MHD_OPTION_DIGEST_AUTH_RANDOM = 17
-
+  MHD_OPTION_NONCE_NC_SIZE = 18
 };
 
 

Modified: libmicrohttpd/src/testcurl/daemontest_digestauth.c
===================================================================
--- libmicrohttpd/src/testcurl/daemontest_digestauth.c  2010-09-10 10:07:26 UTC 
(rev 12963)
+++ libmicrohttpd/src/testcurl/daemontest_digestauth.c  2010-09-10 12:31:43 UTC 
(rev 12964)
@@ -129,7 +129,7 @@
   size_t len;
   size_t off = 0;
   char buf[2048];
-  char rnd[9];
+  char rnd[8];
 
   cbc.buf = buf;
   cbc.size = 2048;
@@ -156,10 +156,11 @@
          off += len;
        }
   (void) close(fd);
-  rnd[8] = '\0';
   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
                         1337, NULL, NULL, &ahc_echo, PAGE,
                                                MHD_OPTION_DIGEST_AUTH_RANDOM, 
rnd,
+                                               
MHD_OPTION_DIGEST_AUTH_RAND_SIZE, sizeof(rnd),
+                                               MHD_OPTION_NONCE_NC_SIZE, 300,
                                                MHD_OPTION_END);
   if (d == NULL)
     return 1;




reply via email to

[Prev in Thread] Current Thread [Next in Thread]