dovecot-2.2: auth: Add and use SCRAM-SHA-1 password scheme

dovecot at dovecot.org dovecot at dovecot.org
Wed Oct 3 03:49:05 EEST 2012


details:   http://hg.dovecot.org/dovecot-2.2/rev/3e3ac2c16fa4
changeset: 15181:3e3ac2c16fa4
user:      Florian Zeitz <florob at babelmonkeys.de>
date:      Wed Sep 19 03:13:39 2012 +0200
description:
auth: Add and use SCRAM-SHA-1 password scheme

diffstat:

 src/auth/Makefile.am             |    1 +
 src/auth/mech-scram-sha1.c       |  177 +++++++++++++++++++++-----------------
 src/auth/password-scheme-scram.c |  139 ++++++++++++++++++++++++++++++
 src/auth/password-scheme.c       |    2 +
 src/auth/password-scheme.h       |    6 +
 5 files changed, 245 insertions(+), 80 deletions(-)

diffs (truncated from 472 to 300 lines):

diff -r d40f26407aad -r 3e3ac2c16fa4 src/auth/Makefile.am
--- a/src/auth/Makefile.am	Wed Oct 03 03:20:57 2012 +0300
+++ b/src/auth/Makefile.am	Wed Sep 19 03:13:39 2012 +0200
@@ -44,6 +44,7 @@
 	password-scheme.c \
 	password-scheme-crypt.c \
 	password-scheme-md5crypt.c \
+	password-scheme-scram.c \
 	password-scheme-otp.c \
 	password-scheme-rpa.c
 
diff -r d40f26407aad -r 3e3ac2c16fa4 src/auth/mech-scram-sha1.c
--- a/src/auth/mech-scram-sha1.c	Wed Oct 03 03:20:57 2012 +0300
+++ b/src/auth/mech-scram-sha1.c	Wed Sep 19 03:13:39 2012 +0200
@@ -1,11 +1,14 @@
 /*
  * SCRAM-SHA-1 SASL authentication, see RFC-5802
  *
- * Copyright (c) 2011 Florian Zeitz <florob at babelmonkeys.de>
+ * Copyright (c) 2011-2012 Florian Zeitz <florob at babelmonkeys.de>
  *
  * This software is released under the MIT license.
  */
 
+#include <stdlib.h>
+#include <limits.h>
+
 #include "auth-common.h"
 #include "base64.h"
 #include "buffer.h"
@@ -15,6 +18,7 @@
 #include "safe-memset.h"
 #include "str.h"
 #include "strfuncs.h"
+#include "strnum.h"
 #include "mech.h"
 
 /* SCRAM hash iteration count. RFC says it SHOULD be at least 4096 */
@@ -29,45 +33,22 @@
 
 	/* sent: */
 	const char *server_first_message;
-	unsigned char salt[16];
-	unsigned char salted_password[SHA1_RESULTLEN];
+	const char *snonce;
 
 	/* received: */
 	const char *gs2_cbind_flag;
 	const char *cnonce;
-	const char *snonce;
 	const char *client_first_message_bare;
 	const char *client_final_message_without_proof;
 	buffer_t *proof;
+
+	/* stored */
+	buffer_t *stored_key;
+	buffer_t *server_key;
 };
 
-static void Hi(const unsigned char *str, size_t str_size,
-	       const unsigned char *salt, size_t salt_size, unsigned int i,
-	       unsigned char result[SHA1_RESULTLEN])
-{
-	struct hmac_context ctx;
-	unsigned char U[SHA1_RESULTLEN];
-	unsigned int j, k;
-
-	/* Calculate U1 */
-	hmac_init(&ctx, str, str_size, &hash_method_sha1);
-	hmac_update(&ctx, salt, salt_size);
-	hmac_update(&ctx, "\0\0\0\1", 4);
-	hmac_final(&ctx, U);
-
-	memcpy(result, U, SHA1_RESULTLEN);
-
-	/* Calculate U2 to Ui and Hi */
-	for (j = 2; j <= i; j++) {
-		hmac_init(&ctx, str, str_size, &hash_method_sha1);
-		hmac_update(&ctx, U, sizeof(U));
-		hmac_final(&ctx, U);
-		for (k = 0; k < SHA1_RESULTLEN; k++)
-			result[k] ^= U[k];
-	}
-}
-
-static const char *get_scram_server_first(struct scram_auth_request *request)
+static const char *get_scram_server_first(struct scram_auth_request *request,
+					  int iter, const char *salt)
 {
 	unsigned char snonce[SCRAM_SERVER_NONCE_LEN+1];
 	string_t *str;
@@ -84,12 +65,9 @@
 	snonce[sizeof(snonce)-1] = '\0';
 	request->snonce = p_strndup(request->pool, snonce, sizeof(snonce));
 
-	random_fill(request->salt, sizeof(request->salt));
-
-	str = t_str_new(MAX_BASE64_ENCODED_SIZE(sizeof(request->salt)));
-	str_printfa(str, "r=%s%s,s=", request->cnonce, request->snonce);
-	base64_encode(request->salt, sizeof(request->salt), str);
-	str_printfa(str, ",i=%d", SCRAM_ITERATE_COUNT);
+	str = t_str_new(sizeof(snonce));
+	str_printfa(str, "r=%s%s,s=%s,i=%d", request->cnonce, request->snonce,
+		    salt, iter);
 	return str_c(str);
 }
 
@@ -105,15 +83,8 @@
 			request->server_first_message, ",",
 			request->client_final_message_without_proof, NULL);
 
-	hmac_init(&ctx, request->salted_password,
-		  sizeof(request->salted_password), &hash_method_sha1);
-	hmac_update(&ctx, "Server Key", 10);
-	hmac_final(&ctx, server_key);
-
-	safe_memset(request->salted_password, 0,
-		    sizeof(request->salted_password));
-
-	hmac_init(&ctx, server_key, sizeof(server_key), &hash_method_sha1);
+	hmac_init(&ctx, request->server_key->data, request->server_key->used,
+		  &hash_method_sha1);
 	hmac_update(&ctx, auth_message, strlen(auth_message));
 	hmac_final(&ctx, server_signature);
 
@@ -211,8 +182,7 @@
 	return TRUE;
 }
 
-static bool verify_credentials(struct scram_auth_request *request,
-			       const unsigned char *credentials, size_t size)
+static bool verify_credentials(struct scram_auth_request *request)
 {
 	struct hmac_context ctx;
 	const char *auth_message;
@@ -221,54 +191,90 @@
 	unsigned char stored_key[SHA1_RESULTLEN];
 	size_t i;
 
-	/* FIXME: credentials should be SASLprepped UTF8 data here */
-	Hi(credentials, size, request->salt, sizeof(request->salt),
-	   SCRAM_ITERATE_COUNT, request->salted_password);
-
-	hmac_init(&ctx, request->salted_password,
-		  sizeof(request->salted_password), &hash_method_sha1);
-	hmac_update(&ctx, "Client Key", 10);
-	hmac_final(&ctx, client_key);
-
-	sha1_get_digest(client_key, sizeof(client_key), stored_key);
-
 	auth_message = t_strconcat(request->client_first_message_bare, ",",
 			request->server_first_message, ",",
 			request->client_final_message_without_proof, NULL);
 
-	hmac_init(&ctx, stored_key, sizeof(stored_key), &hash_method_sha1);
+	hmac_init(&ctx, request->stored_key->data, request->stored_key->used,
+		  &hash_method_sha1);
 	hmac_update(&ctx, auth_message, strlen(auth_message));
 	hmac_final(&ctx, client_signature);
 
 	for (i = 0; i < sizeof(client_signature); i++)
-		client_signature[i] ^= client_key[i];
+		client_key[i] =
+			((char*)request->proof->data)[i] ^ client_signature[i];
+
+	sha1_get_digest(client_key, sizeof(client_key), stored_key);
 
 	safe_memset(client_key, 0, sizeof(client_key));
-	safe_memset(stored_key, 0, sizeof(stored_key));
+	safe_memset(client_signature, 0, sizeof(client_signature));
 
-	return memcmp(client_signature, request->proof->data,
-		      request->proof->used) == 0;
+	return memcmp(stored_key, request->stored_key->data,
+		      request->stored_key->used) == 0;
 }
 
 static void credentials_callback(enum passdb_result result,
 				 const unsigned char *credentials, size_t size,
 				 struct auth_request *auth_request)
 {
+	const char *const *fields;
+	size_t len;
+	unsigned int iter;
+	const char *salt;
 	struct scram_auth_request *request =
 		(struct scram_auth_request *)auth_request;
-	const char *server_final_message;
 
 	switch (result) {
 	case PASSDB_RESULT_OK:
-		if (!verify_credentials(request, credentials, size)) {
+		fields = t_strsplit(t_strndup(credentials, size), ",");
+
+		if (str_array_length(fields) != 4) {
 			auth_request_log_info(auth_request, "scram-sha-1",
-					      "password mismatch");
+					      "Invalid passdb entry");
 			auth_request_fail(auth_request);
-		} else {
-			server_final_message = get_scram_server_final(request);
-			auth_request_success(auth_request, server_final_message,
-					     strlen(server_final_message));
+			break;
 		}
+
+		if (str_to_uint(fields[0], &iter) < 0 || (iter < 4096) ||
+		    (iter > INT_MAX)) {
+			auth_request_log_info(auth_request, "scram-sha-1",
+					      "Invalid iteration count");
+			auth_request_fail(auth_request);
+			break;
+		}
+
+		salt = fields[1];
+
+		len = strlen(fields[2]);
+		request->stored_key = buffer_create_dynamic(request->pool,
+					MAX_BASE64_DECODED_SIZE(len));
+		if (base64_decode(fields[2], len, NULL,
+				  request->stored_key) < 0) {
+			auth_request_log_info(auth_request, "scram-sha-1",
+					      "Invalid base64 encoding"
+					      "of StoredKey in passdb");
+			auth_request_fail(auth_request);
+			break;
+		}
+
+		len = strlen(fields[3]);
+		request->server_key = buffer_create_dynamic(request->pool,
+					MAX_BASE64_DECODED_SIZE(len));
+		if (base64_decode(fields[3], len, NULL,
+				  request->server_key) < 0) {
+			auth_request_log_info(auth_request, "scram-sha-1",
+					      "Invalid base64 encoding"
+					      "of ServerKey in passdb");
+			auth_request_fail(auth_request);
+			break;
+		}
+
+		request->server_first_message = p_strdup(request->pool,
+			get_scram_server_first(request, iter, salt));
+
+		auth_request_handler_reply_continue(auth_request,
+					request->server_first_message,
+					strlen(request->server_first_message));
 		break;
 	case PASSDB_RESULT_INTERNAL_FAILURE:
 		auth_request_internal_failure(auth_request);
@@ -333,8 +339,6 @@
 	request->client_final_message_without_proof =
 		p_strdup(request->pool, t_strarray_join(fields, ","));
 
-	auth_request_lookup_credentials(&request->auth_request, "PLAIN",
-					credentials_callback);
 	return TRUE;
 }
 
@@ -345,22 +349,35 @@
 	struct scram_auth_request *request =
 		(struct scram_auth_request *)auth_request;
 	const char *error = NULL;
+	const char *server_final_message;
+	int len;
 
 	if (!request->client_first_message_bare) {
 		/* Received client-first-message */
 		if (parse_scram_client_first(request, data,
 					     data_size, &error)) {
-			request->server_first_message = p_strdup(request->pool,
-					get_scram_server_first(request));
-			auth_request_handler_reply_continue(auth_request,
-					request->server_first_message,
-					strlen(request->server_first_message));
+			auth_request_lookup_credentials(&request->auth_request,
+							"SCRAM-SHA-1",
+							credentials_callback);
 			return;
 		}
 	} else {
 		/* Received client-final-message */
-		if (parse_scram_client_final(request, data, data_size, &error))
-			return;
+		if (parse_scram_client_final(request, data, data_size,
+					     &error)) {
+			if (!verify_credentials(request)) {
+				auth_request_log_info(auth_request,
+						      "scram-sha-1",
+						      "password mismatch");
+			} else {
+				server_final_message =
+					get_scram_server_final(request);
+				len = strlen(server_final_message);
+				auth_request_success(auth_request,
+						     server_final_message, len);
+				return;
+			}
+		}
 	}
 


More information about the dovecot-cvs mailing list