View Issue Details

IDProjectCategoryView StatusLast Update
0005397libmicrohttpddigest authentication (HTTP)public2018-11-06 19:46
ReporterDirk BrinkmeierAssigned ToChristian Grothoff 
PrioritynormalSeveritytweakReproducibilityN/A
Status closedResolutionfixed 
Product Version0.9.59 
Target Version0.9.60Fixed in Version0.9.60 
Summary0005397: Patch for digest authentication from user database with precalculated 'username:realm:password' hash
DescriptionI'm using a a user database for digest authentication.
To avoid using clear text password storage, I precalculate the 'username:realm:password' MD5 hash and store the hash value in the database.

To be able to use digest authentication with libmicrohttpd, I modified/duplicated the given function MHD_digest_auth_check(...) and extend this one to MHD_digest_auth_check_digest(...).
I attached a patch for that purpose.

I hope you can use the patch, if you think that this is a useful extension...

Regards,
Dirk.


TagsNo tags attached.

Activities

Dirk Brinkmeier

2018-07-07 15:44

reporter  

digestauth_by_hash.diff (9,039 bytes)
diff -ur ./libmicrohttpd-0.9.59_org/libmicrohttpd-0.9.59/src/include/microhttpd.h ./libmicrohttpd-0.9.59_new/libmicrohttpd-0.9.59/src/include/microhttpd.h
--- ./libmicrohttpd-0.9.59_org/libmicrohttpd-0.9.59/src/include/microhttpd.h	2018-02-01 20:12:55.000000000 +0100
+++ ./libmicrohttpd-0.9.59_new/libmicrohttpd-0.9.59/src/include/microhttpd.h	2018-07-07 11:15:15.994086680 +0200
@@ -292,6 +292,9 @@
 _MHD_DEPR_MACRO("Macro MHD_LONG_LONG_PRINTF is deprecated, use MHD_UNSIGNED_LONG_LONG_PRINTF")
 #endif
 
+#ifndef MD5_DIGEST_SIZE
+#define	 MD5_DIGEST_SIZE 16
+#endif
 
 /**
  * @defgroup httpcode HTTP response codes.
@@ -3140,10 +3143,32 @@
  */
 _MHD_EXTERN int
 MHD_digest_auth_check (struct MHD_Connection *connection,
-		       const char *realm,
-		       const char *username,
-		       const char *password,
-		       unsigned int nonce_timeout);
+				const char *realm,
+				const char *username,
+				const char *password,
+				unsigned int nonce_timeout);
+
+/**
+ * Authenticates the authorization header sent by the client
+ *
+ * @param connection The MHD connection structure
+ * @param realm The realm presented to the client
+ * @param username The username needs to be authenticated
+ * @param digest An `unsigned char *' pointer to the binary MD5 sum
+ * 			for the precalculated hash value "username:realm:password"
+ * 			of MD5_DIGEST_SIZE bytes
+ * @param nonce_timeout The amount of time for a nonce to be
+ * 			invalid in seconds
+ * @return #MHD_YES if authenticated, #MHD_NO if not,
+ * 			#MHD_INVALID_NONCE if nonce is invalid
+ * @ingroup authentication
+ */
+_MHD_EXTERN int
+MHD_digest_auth_check_digest (struct MHD_Connection *connection,
+				const char *realm,
+				const char *username,
+				const unsigned char digest[MD5_DIGEST_SIZE],
+				unsigned int nonce_timeout);
 
 
 /**
diff -ur ./libmicrohttpd-0.9.59_org/libmicrohttpd-0.9.59/src/microhttpd/digestauth.c ./libmicrohttpd-0.9.59_new/libmicrohttpd-0.9.59/src/microhttpd/digestauth.c
--- ./libmicrohttpd-0.9.59_org/libmicrohttpd-0.9.59/src/microhttpd/digestauth.c	2018-02-01 20:12:55.000000000 +0100
+++ ./libmicrohttpd-0.9.59_new/libmicrohttpd-0.9.59/src/microhttpd/digestauth.c	2018-07-07 11:16:41.251463205 +0200
@@ -93,8 +93,62 @@
 
 
 /**
- * calculate H(A1) as per RFC2617 spec and store the
- * result in 'sessionkey'.
+ * calculate H(A1) from given hash as per RFC2617 spec
+ * and store the * result in 'sessionkey'.
+ *
+ * @param alg The hash algorithm used, can be "md5" or "md5-sess"
+ * @param digest An `unsigned char *' pointer to the binary MD5 sum
+ * 			for the precalculated hash value "username:realm:password"
+ * 			of MD5_DIGEST_SIZE bytes
+ * @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
+digest_calc_ha1_from_digest (const char *alg,
+		 const unsigned char digest[MD5_DIGEST_SIZE],
+		 const char *nonce,
+		 const char *cnonce,
+		 char sessionkey[HASH_MD5_HEX_LEN + 1])
+{
+  struct MD5Context md5;
+  if (MHD_str_equal_caseless_(alg,
+                              "md5-sess"))
+    {
+	  unsigned char ha1[MD5_DIGEST_SIZE];
+      MD5Init (&md5);
+      MD5Update (&md5,
+                 (const unsigned char *) digest,
+                 MD5_DIGEST_SIZE);
+      MD5Update (&md5,
+                 (const unsigned char *) ":",
+                 1);
+      MD5Update (&md5,
+                 (const unsigned char *) nonce,
+                 strlen (nonce));
+      MD5Update (&md5,
+                 (const unsigned char *) ":",
+                 1);
+      MD5Update (&md5,
+                 (const unsigned char *) cnonce,
+                 strlen (cnonce));
+      MD5Final (ha1,
+                &md5);
+      cvthex (ha1,
+              sizeof (ha1),
+              sessionkey);
+    }
+  else
+    {
+	  cvthex (digest,
+			  MD5_DIGEST_SIZE,
+	          sessionkey);
+    }
+}
+
+/**
+ * calculate H(A1) from username, realm and password as per RFC2617 spec
+ * and store the result in 'sessionkey'.
  *
  * @param alg The hash algorithm used, can be "md5" or "md5-sess"
  * @param username A `char *' pointer to the username value
@@ -105,7 +159,7 @@
  * @param sessionkey pointer to buffer of HASH_MD5_HEX_LEN+1 bytes
  */
 static void
-digest_calc_ha1 (const char *alg,
+digest_calc_ha1_from_user (const char *alg,
 		 const char *username,
 		 const char *realm,
 		 const char *password,
@@ -134,31 +188,7 @@
              strlen (password));
   MD5Final (ha1,
             &md5);
-  if (MHD_str_equal_caseless_(alg,
-                              "md5-sess"))
-    {
-      MD5Init (&md5);
-      MD5Update (&md5,
-                 (const unsigned char *) ha1,
-                 sizeof (ha1));
-      MD5Update (&md5,
-                 (const unsigned char *) ":",
-                 1);
-      MD5Update (&md5,
-                 (const unsigned char *) nonce,
-                 strlen (nonce));
-      MD5Update (&md5,
-                 (const unsigned char *) ":",
-                 1);
-      MD5Update (&md5,
-                 (const unsigned char *) cnonce,
-                 strlen (cnonce));
-      MD5Final (ha1,
-                &md5);
-    }
-  cvthex (ha1,
-          sizeof (ha1),
-          sessionkey);
+  digest_calc_ha1_from_digest(alg, ha1, nonce, cnonce, sessionkey);
 }
 
 
@@ -664,6 +694,9 @@
  * @param realm The realm presented to the client
  * @param username The username needs to be authenticated
  * @param password The password used in the authentication
+ * @param digest An optional `unsigned char *' pointer to the binary MD5 sum
+ * 			for the precalculated hash value "username:realm:password"
+ * 			of MD5_DIGEST_SIZE bytes
  * @param nonce_timeout The amount of time for a nonce to be
  * 			invalid in seconds
  * @return #MHD_YES if authenticated, #MHD_NO if not,
@@ -671,10 +704,11 @@
  * @ingroup authentication
  */
 int
-MHD_digest_auth_check (struct MHD_Connection *connection,
+MHD_digest_auth_check_all (struct MHD_Connection *connection,
 		       const char *realm,
 		       const char *username,
 		       const char *password,
+		       const unsigned char digest[MD5_DIGEST_SIZE],
 		       unsigned int nonce_timeout)
 {
   struct MHD_Daemon *daemon = connection->daemon;
@@ -868,13 +902,21 @@
       return MHD_NO;
     }
 
-    digest_calc_ha1 ("md5",
-                     username,
-                     realm,
-                     password,
-                     nonce,
-                     cnonce,
-                     ha1);
+    if (digest) {
+		digest_calc_ha1_from_digest ("md5",
+									 digest,
+									 nonce,
+									 cnonce,
+									 ha1);
+    } else {
+		digest_calc_ha1_from_user ("md5",
+								 username,
+								 realm,
+								 password,
+								 nonce,
+								 cnonce,
+								 ha1);
+    }
     digest_calc_response (ha1,
 			  nonce,
 			  nc,
@@ -885,6 +927,7 @@
 			  hentity,
 			  respexp);
 
+
     /* Need to unescape URI before comparing with connection->url */
     daemon->unescape_callback (daemon->unescape_callback_cls,
                                connection,
@@ -929,6 +972,62 @@
   }
 }
 
+/**
+ * Authenticates the authorization header sent by the client
+ *
+ * @param connection The MHD connection structure
+ * @param realm The realm presented to the client
+ * @param username The username needs to be authenticated
+ * @param password The password used in the authentication
+ * @param nonce_timeout The amount of time for a nonce to be
+ * 			invalid in seconds
+ * @return #MHD_YES if authenticated, #MHD_NO if not,
+ * 			#MHD_INVALID_NONCE if nonce is invalid
+ * @ingroup authentication
+ */
+_MHD_EXTERN int
+MHD_digest_auth_check (struct MHD_Connection *connection,
+				const char *realm,
+				const char *username,
+				const char *password,
+				unsigned int nonce_timeout)
+{
+	return MHD_digest_auth_check_all(
+			connection,
+			realm, username, password,
+			NULL,
+			nonce_timeout);
+}
+
+/**
+ * Authenticates the authorization header sent by the client
+ *
+ * @param connection The MHD connection structure
+ * @param realm The realm presented to the client
+ * @param username The username needs to be authenticated
+ * @param digest An `unsigned char *' pointer to the binary MD5 sum
+ * 			for the precalculated hash value "username:realm:password"
+ * 			of MD5_DIGEST_SIZE bytes
+ * @param nonce_timeout The amount of time for a nonce to be
+ * 			invalid in seconds
+ * @return #MHD_YES if authenticated, #MHD_NO if not,
+ * 			#MHD_INVALID_NONCE if nonce is invalid
+ * @ingroup authentication
+ */
+_MHD_EXTERN int
+MHD_digest_auth_check_digest (struct MHD_Connection *connection,
+				const char *realm,
+				const char *username,
+				const unsigned char digest[MD5_DIGEST_SIZE],
+				unsigned int nonce_timeout)
+{
+	return MHD_digest_auth_check_all(
+			connection,
+			realm, username, NULL,
+			digest,
+			nonce_timeout);
+}
+
 
 /**
  * Queues a response to request authentication from the client
digestauth_by_hash.diff (9,039 bytes)

Christian Grothoff

2018-07-14 13:24

manager   ~0013143

Patch integrated in Git head. Thanks, Dirk!

Issue History

Date Modified Username Field Change
2018-07-07 15:44 Dirk Brinkmeier New Issue
2018-07-07 15:44 Dirk Brinkmeier File Added: digestauth_by_hash.diff
2018-07-14 13:23 Christian Grothoff Assigned To => Christian Grothoff
2018-07-14 13:23 Christian Grothoff Status new => assigned
2018-07-14 13:24 Christian Grothoff Status assigned => resolved
2018-07-14 13:24 Christian Grothoff Resolution open => fixed
2018-07-14 13:24 Christian Grothoff Fixed in Version => 0.9.60
2018-07-14 13:24 Christian Grothoff Note Added: 0013143
2018-07-14 13:24 Christian Grothoff Target Version => 0.9.60
2018-11-06 19:46 Christian Grothoff Status resolved => closed