]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-sasl: sasl-server-mech-digest-md5 - Move core processing to lib-auth/auth-digest
authorStephan Bosch <stephan.bosch@open-xchange.com>
Tue, 18 Feb 2025 01:40:10 +0000 (02:40 +0100)
committertimo.sirainen <timo.sirainen@open-xchange.com>
Thu, 9 Oct 2025 08:41:22 +0000 (08:41 +0000)
src/lib-auth/auth-digest.c
src/lib-auth/auth-digest.h
src/lib-sasl/sasl-server-mech-digest-md5.c

index 06df4da42b8288b5d5f7683c202f194d89f3b515..575d02cffff4ed34508ffa45072347713aa60acc 100644 (file)
@@ -1,6 +1,7 @@
 /* Copyright (c) 2025 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "hex-binary.h"
 #include "hash-method.h"
 
 #include "auth-digest.h"
@@ -89,3 +90,140 @@ void auth_digest_get_hash_a1_secret(const struct hash_method *hmethod,
        hash_method_loop(&ctx, password, strlen(password));
        hash_method_result(&ctx, digest_r);
 }
+
+const char *
+auth_digest_get_hash_a1(const struct hash_method *hmethod,
+                       const unsigned char *hash_a1_secret,
+                       const char *nonce, const char *cnonce,
+                       const char *authzid)
+{
+       struct hash_method_context ctx;
+
+       if (nonce == NULL)
+               return binary_to_hex(hash_a1_secret, hmethod->digest_size);
+       i_assert(cnonce != NULL);
+
+       unsigned char digest[hmethod->digest_size];
+
+       /* A1       = H( unq(username) ":" unq(realm) ":" passwd )
+                         ":" unq(nonce-prime) ":" unq(cnonce-prime)
+
+          If authzid is not NULL it is added in an additional ":" authzid as
+          per RFC 2831.
+        */
+
+       hash_method_init(&ctx, hmethod);
+       hash_method_loop(&ctx, hash_a1_secret, hmethod->digest_size);
+       hash_method_loop(&ctx, ":", 1);
+       hash_method_loop(&ctx, nonce, strlen(nonce));
+       hash_method_loop(&ctx, ":", 1);
+       hash_method_loop(&ctx, cnonce, strlen(cnonce));
+       if (authzid != NULL) {
+               hash_method_loop(&ctx, ":", 1);
+               hash_method_loop(&ctx, authzid, strlen(authzid));
+       }
+       hash_method_result(&ctx, digest);
+
+       return binary_to_hex(digest, sizeof(digest));
+}
+
+const char *
+auth_digest_get_hash_a2(const struct hash_method *hmethod,
+                       const char *req_method, const char *req_uri,
+                       const char *entity_body_hash)
+{
+       struct hash_method_context ctx;
+       unsigned char digest[hmethod->digest_size];
+
+       /* If the qop parameter's value is "auth" or is unspecified, then A2 is:
+                A2       = Method ":" request-uri
+
+          If the qop value is "auth-int", then A2 is:
+
+                A2       = Method ":" request-uri ":" H(entity-body)
+        */
+
+       hash_method_init(&ctx, hmethod);
+       if (req_method != NULL)
+               hash_method_loop(&ctx, req_method, strlen(req_method));
+       hash_method_loop(&ctx, ":", 1);
+       if (req_uri != NULL)
+               hash_method_loop(&ctx, req_uri, strlen(req_uri));
+       if (entity_body_hash != NULL) {
+               hash_method_loop(&ctx, ":", 1);
+               hash_method_loop(&ctx, entity_body_hash,
+                                strlen(entity_body_hash));
+       }
+       hash_method_result(&ctx, digest);
+
+       return binary_to_hex(digest, sizeof(digest));
+}
+
+static const char *
+auth_digest_get_response(const struct hash_method *hmethod,
+                        const char *hash_a1, const char *hash_a2,
+                        const char *qop, const char *nonce, const char *nc,
+                        const char *cnonce)
+{
+       /* response = <"> < KD ( H(A1), unq(nonce)
+                                  ":" nc
+                                  ":" unq(cnonce)
+                                  ":" unq(qop)
+                                  ":" H(A2)
+                         ) <">
+        */
+
+       struct hash_method_context ctx;
+       unsigned char digest[hmethod->digest_size];
+
+       hash_method_init(&ctx, hmethod);
+       hash_method_loop(&ctx, hash_a1, strlen(hash_a1));
+       hash_method_loop(&ctx, ":", 1);
+       hash_method_loop(&ctx, nonce, strlen(nonce));
+       hash_method_loop(&ctx, ":", 1);
+       if (qop != NULL) {
+               hash_method_loop(&ctx, nc, strlen(nc));
+               hash_method_loop(&ctx, ":", 1);
+               hash_method_loop(&ctx, cnonce, strlen(cnonce));
+               hash_method_loop(&ctx, ":", 1);
+               hash_method_loop(&ctx, qop, strlen(qop));
+               hash_method_loop(&ctx, ":", 1);
+       }
+       hash_method_loop(&ctx, hash_a2, strlen(hash_a2));
+       hash_method_result(&ctx, digest);
+
+       return binary_to_hex(digest, sizeof(digest));
+}
+
+const char *
+auth_digest_get_client_response(const struct hash_method *hmethod,
+                               const char *hash_a1, const char *req_method,
+                               const char *req_uri, const char *qop,
+                               const char *nonce, const char *nc,
+                               const char *cnonce,
+                               const char *entity_body_hash)
+{
+       const char *hash_a2;
+
+       hash_a2 = auth_digest_get_hash_a2(hmethod, req_method, req_uri,
+                                         entity_body_hash);
+
+       return auth_digest_get_response(hmethod, hash_a1, hash_a2,
+                                       qop, nonce, nc, cnonce);
+}
+
+const char *
+auth_digest_get_server_response(const struct hash_method *hmethod,
+                               const char *hash_a1, const char *req_uri,
+                               const char *qop, const char *nonce,
+                               const char *nc, const char *cnonce,
+                               const char *entity_body_hash)
+{
+       const char *hash_a2;
+
+       hash_a2 = auth_digest_get_hash_a2(hmethod, NULL, req_uri,
+                                         entity_body_hash);
+
+       return auth_digest_get_response(hmethod, hash_a1, hash_a2,
+                                       qop, nonce, nc, cnonce);
+}
index 83c430218277eb2b0c8d6631b3234bc20ce14c24..2ab4ab3640381e3da58c791e6f8aceb6b210b345 100644 (file)
@@ -15,5 +15,27 @@ void auth_digest_get_hash_a1_secret(const struct hash_method *hmethod,
                                    const char *username, const char *realm,
                                    const char *password,
                                    unsigned char *digest_r);
+const char *
+auth_digest_get_hash_a1(const struct hash_method *hmethod,
+                       const unsigned char *hash_a1_secret,
+                       const char *nonce, const char *cnonce,
+                       const char *authzid);
+const char *
+auth_digest_get_hash_a2(const struct hash_method *hmethod,
+                       const char *req_method, const char *req_uri,
+                       const char *entity_body_hash);
 
+const char *
+auth_digest_get_client_response(const struct hash_method *hmethod,
+                               const char *hash_a1, const char *req_method,
+                               const char *req_uri, const char *qop,
+                               const char *nonce, const char *nc,
+                               const char *cnonce,
+                               const char *entity_body_hash);
+const char *
+auth_digest_get_server_response(const struct hash_method *hmethod,
+                               const char *hash_a1, const char *req_uri,
+                               const char *qop, const char *nonce,
+                               const char *nc, const char *cnonce,
+                               const char *entity_body_hash);
 #endif
index 26353fff069b09595b737714a5e884396a823198..07a1bbfc285b568b73e80d0ef8c58e5dabb83988 100644 (file)
@@ -112,16 +112,14 @@ static void
 verify_credentials(struct sasl_server_mech_request *auth_request,
                   const unsigned char *credentials, size_t size)
 {
+       static const struct hash_method *const hmethod = &hash_method_md5;
        struct digest_auth_request *request =
                container_of(auth_request, struct digest_auth_request,
                             auth_request);
-       struct md5_context ctx;
-       unsigned char digest[MD5_RESULTLEN];
-       const char *a1_hex, *a2_hex, *response_hex;
-       int i;
+       const char *a1_hex, *response_hex;
 
        /* get the MD5 password */
-       if (size != MD5_RESULTLEN) {
+       if (size != hmethod->digest_size) {
                e_error(auth_request->event, "invalid credentials length");
                sasl_server_request_failure(auth_request);
                return;
@@ -154,74 +152,37 @@ verify_credentials(struct sasl_server_mech_request *auth_request,
        */
 
        /* A1 */
-       md5_init(&ctx);
-       md5_update(&ctx, credentials, size);
-       md5_update(&ctx, ":", 1);
-       md5_update(&ctx, request->nonce, strlen(request->nonce));
-       md5_update(&ctx, ":", 1);
-       md5_update(&ctx, request->cnonce, strlen(request->cnonce));
-       if (request->authzid != NULL) {
-               md5_update(&ctx, ":", 1);
-               md5_update(&ctx, request->authzid, strlen(request->authzid));
-       }
-       md5_final(&ctx, digest);
-       a1_hex = binary_to_hex(digest, 16);
-
-       /* do it twice, first verify the user's response, the second is
-          sent for client as a reply */
-       for (i = 0; i < 2; i++) {
-               /* A2 */
-               md5_init(&ctx);
-               if (i == 0)
-                       md5_update(&ctx, "AUTHENTICATE:", 13);
-               else
-                       md5_update(&ctx, ":", 1);
-
-               if (request->digest_uri != NULL) {
-                       md5_update(&ctx, request->digest_uri,
-                                  strlen(request->digest_uri));
-               }
-               if (request->qop == QOP_AUTH_INT ||
-                   request->qop == QOP_AUTH_CONF) {
-                       md5_update(&ctx, ":00000000000000000000000000000000",
-                                  33);
-               }
-               md5_final(&ctx, digest);
-               a2_hex = binary_to_hex(digest, 16);
-
-               /* response */
-               md5_init(&ctx);
-               md5_update(&ctx, a1_hex, 32);
-               md5_update(&ctx, ":", 1);
-               md5_update(&ctx, request->nonce, strlen(request->nonce));
-               md5_update(&ctx, ":", 1);
-               md5_update(&ctx, request->nonce_count,
-                          strlen(request->nonce_count));
-               md5_update(&ctx, ":", 1);
-               md5_update(&ctx, request->cnonce, strlen(request->cnonce));
-               md5_update(&ctx, ":", 1);
-               md5_update(&ctx, request->qop_value,
-                          strlen(request->qop_value));
-               md5_update(&ctx, ":", 1);
-               md5_update(&ctx, a2_hex, 32);
-               md5_final(&ctx, digest);
-               response_hex = binary_to_hex(digest, 16);
-
-               if (i == 0) {
-                       /* verify response */
-                       if (!mem_equals_timing_safe(response_hex,
-                                                   request->response, 32)) {
-                               sasl_server_request_password_mismatch(
-                                       auth_request);
-                               return;
-                       }
-               } else {
-                       request->rspauth =
-                               p_strconcat(auth_request->pool, "rspauth=",
-                                           response_hex, NULL);
-               }
+       a1_hex = auth_digest_get_hash_a1(hmethod, credentials,
+                                        request->nonce, request->cnonce,
+                                        request->authzid);
+
+       const char *entity_body_hash = NULL;
+
+       if (request->qop == QOP_AUTH_INT ||
+           request->qop == QOP_AUTH_CONF)
+               entity_body_hash = "00000000000000000000000000000000";
+
+       /* client response */
+       response_hex = auth_digest_get_client_response(
+               hmethod, a1_hex, "AUTHENTICATE", request->digest_uri,
+               request->qop_value, request->nonce, request->nonce_count,
+               request->cnonce, entity_body_hash);
+
+       /* verify response */
+       if (!mem_equals_timing_safe(response_hex, request->response,
+                                   hmethod->digest_size * 2)) {
+               sasl_server_request_password_mismatch(auth_request);
+               return;
        }
 
+       /* server response */
+       response_hex = auth_digest_get_server_response(
+               hmethod, a1_hex, request->digest_uri, request->qop_value,
+               request->nonce, request->nonce_count, request->cnonce,
+               entity_body_hash);
+
+       request->rspauth = p_strconcat(auth_request->pool, "rspauth=",
+                                      response_hex, NULL);
        sasl_server_request_success(auth_request, request->rspauth,
                                    strlen(request->rspauth));
 }