From f87327db8e63dfaaeb2de50ade01aeb98cd14bf7 Mon Sep 17 00:00:00 2001
From: deuce <>
Date: Sun, 25 Nov 2007 00:07:55 +0000
Subject: [PATCH] Check digest against copy in user.dat AND lower and upper
 case versions.

---
 src/sbbs3/websrvr.c | 91 ++++++++++++++++++++++++++++++++-------------
 1 file changed, 66 insertions(+), 25 deletions(-)

diff --git a/src/sbbs3/websrvr.c b/src/sbbs3/websrvr.c
index 15fb19f098..74ac29954d 100644
--- a/src/sbbs3/websrvr.c
+++ b/src/sbbs3/websrvr.c
@@ -1391,6 +1391,35 @@ BOOL http_checkuser(http_session_t * session)
 	return(TRUE);
 }
 
+static void calculate_digest(http_session_t * session, char *ha1, char *ha2, unsigned char digest[MD5_DIGEST_SIZE])
+{
+	MD5		ctx;
+
+	MD5_open(&ctx);
+	MD5_digest(&ctx, ha1, strlen(ha1));
+	MD5_digest(&ctx, ":", 1);
+	MD5_digest(&ctx, session->req.auth.nonce, strlen(session->req.auth.nonce));
+	MD5_digest(&ctx, ":", 1);
+
+	if(session->req.auth.qop_value != QOP_NONE) {
+		MD5_digest(&ctx, session->req.auth.nonce_count, strlen(session->req.auth.nonce_count));
+		MD5_digest(&ctx, ":", 1);
+		MD5_digest(&ctx, session->req.auth.cnonce, strlen(session->req.auth.cnonce));
+		MD5_digest(&ctx, ":", 1);
+		switch(session->req.auth.qop_value) {
+			case QOP_AUTH:
+				MD5_digest(&ctx, "auth", 4);
+				break;
+			case QOP_AUTH_INT:
+				MD5_digest(&ctx, "auth-int", 7);
+				break;
+		}
+		MD5_digest(&ctx, ":", 1);
+	}
+	MD5_digest(&ctx, ha2, strlen(ha2));
+	MD5_close(&ctx, digest);
+}
+
 static BOOL check_ars(http_session_t * session)
 {
 	char	*last;
@@ -1469,7 +1498,10 @@ static BOOL check_ars(http_session_t * session)
 			{
 				unsigned char	digest[MD5_DIGEST_SIZE];
 				char			ha1[MD5_DIGEST_SIZE*2+1];
+				char			ha1l[MD5_DIGEST_SIZE*2+1];
+				char			ha1u[MD5_DIGEST_SIZE*2+1];
 				char			ha2[MD5_DIGEST_SIZE*2+1];
+				char			*pass;
 				MD5		ctx;
 
 				if(session->req.auth.qop_value==QOP_UNKNOWN)
@@ -1487,44 +1519,53 @@ static BOOL check_ars(http_session_t * session)
 				MD5_close(&ctx, digest);
 				MD5_hex(ha1, digest);
 
+				/* H(A1)l */
+				pass=strdup(thisuser.pass);
+				strlwr(pass);
+				MD5_open(&ctx);
+				MD5_digest(&ctx, session->req.auth.username, strlen(session->req.auth.username));
+				MD5_digest(&ctx, ":", 1);
+				MD5_digest(&ctx, session->req.realm?session->req.realm:scfg.sys_name, strlen(session->req.realm?session->req.realm:scfg.sys_name));
+				MD5_digest(&ctx, ":", 1);
+				MD5_digest(&ctx, pass, strlen(pass));
+				MD5_close(&ctx, digest);
+				MD5_hex(ha1l, digest);
+
+				/* H(A1)u */
+				strupr(pass);
+				MD5_open(&ctx);
+				MD5_digest(&ctx, session->req.auth.username, strlen(session->req.auth.username));
+				MD5_digest(&ctx, ":", 1);
+				MD5_digest(&ctx, session->req.realm?session->req.realm:scfg.sys_name, strlen(session->req.realm?session->req.realm:scfg.sys_name));
+				MD5_digest(&ctx, ":", 1);
+				MD5_digest(&ctx, thisuser.pass, strlen(thisuser.pass));
+				MD5_close(&ctx, digest);
+				MD5_hex(ha1u, digest);
+				free(pass);
+
 				/* H(A2) */
 				MD5_open(&ctx);
 				MD5_digest(&ctx, methods[session->req.method], strlen(methods[session->req.method]));
 				MD5_digest(&ctx, ":", 1);
 				MD5_digest(&ctx, session->req.auth.digest_uri, strlen(session->req.auth.digest_uri));
-
 				/* TODO QOP==AUTH_INT */
 				if(session->req.auth.qop_value == QOP_AUTH_INT)
 					return(FALSE);
 				MD5_close(&ctx, digest);
 				MD5_hex(ha2, digest);
 
-				MD5_open(&ctx);
-				MD5_digest(&ctx, ha1, strlen(ha1));
-				MD5_digest(&ctx, ":", 1);
-				MD5_digest(&ctx, session->req.auth.nonce, strlen(session->req.auth.nonce));
-				MD5_digest(&ctx, ":", 1);
-
-				if(session->req.auth.qop_value != QOP_NONE) {
-					MD5_digest(&ctx, session->req.auth.nonce_count, strlen(session->req.auth.nonce_count));
-					MD5_digest(&ctx, ":", 1);
-					MD5_digest(&ctx, session->req.auth.cnonce, strlen(session->req.auth.cnonce));
-					MD5_digest(&ctx, ":", 1);
-					switch(session->req.auth.qop_value) {
-						case QOP_AUTH:
-							MD5_digest(&ctx, "auth", 4);
-							break;
-						case QOP_AUTH_INT:
-							MD5_digest(&ctx, "auth-int", 7);
-							break;
+				/* Check password as in user.dat */
+				calculate_digest(session, ha1, ha2, digest);
+				if(memcmp(digest, session->req.auth.digest, sizeof(digest))) {
+					/* Check against lower-case password */
+					calculate_digest(session, ha1l, ha2, digest);
+					if(memcmp(digest, session->req.auth.digest, sizeof(digest))) {
+						/* Check against upper-case password */
+						calculate_digest(session, ha1u, ha2, digest);
+						if(memcmp(digest, session->req.auth.digest, sizeof(digest)))
+							return(FALSE);
 					}
-					MD5_digest(&ctx, ":", 1);
 				}
-				MD5_digest(&ctx, ha2, strlen(ha2));
-				MD5_close(&ctx, digest);
-
-				if(memcmp(digest, session->req.auth.digest, sizeof(digest)))
-					return(FALSE);
 			}
 	}
 
-- 
GitLab