[Dovecot] [PATCH, RFC 13/13] OTP: SKEY authentication mechanism

Andrey Panin pazke at donpac.ru
Mon Jun 26 15:58:22 EEST 2006


diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/Makefile.am dovecot/src/auth/Makefile.am
--- dovecot.vanilla/src/auth/Makefile.am	2006-06-23 13:44:31.820790944 +0400
+++ dovecot/src/auth/Makefile.am	2006-06-23 13:44:31.822790640 +0400
@@ -60,6 +60,7 @@ dovecot_auth_SOURCES = \
 	mech-digest-md5.c \
 	mech-ntlm.c \
 	mech-otp.c \
+	mech-skey.c \
 	mech-gssapi.c \
 	mech-rpa.c \
 	mech-apop.c \
diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/mech.c dovecot/src/auth/mech.c
--- dovecot.vanilla/src/auth/mech.c	2006-06-23 13:44:31.821790792 +0400
+++ dovecot/src/auth/mech.c	2006-06-23 13:44:31.822790640 +0400
@@ -69,6 +69,7 @@ extern struct mech_module mech_cram_md5;
 extern struct mech_module mech_digest_md5;
 extern struct mech_module mech_ntlm;
 extern struct mech_module mech_otp;
+extern struct mech_module mech_skey;
 extern struct mech_module mech_rpa;
 extern struct mech_module mech_anonymous;
 #ifdef HAVE_GSSAPI
@@ -84,6 +85,7 @@ void mech_init(void)
 	mech_register_module(&mech_digest_md5);
 	mech_register_module(&mech_ntlm);
 	mech_register_module(&mech_otp);
+	mech_register_module(&mech_skey);
 	mech_register_module(&mech_rpa);
 	mech_register_module(&mech_anonymous);
 #ifdef HAVE_GSSAPI
@@ -100,6 +102,7 @@ void mech_deinit(void)
 	mech_unregister_module(&mech_digest_md5);
 	mech_unregister_module(&mech_ntlm);
 	mech_unregister_module(&mech_otp);
+	mech_unregister_module(&mech_skey);
 	mech_unregister_module(&mech_rpa);
 	mech_unregister_module(&mech_anonymous);
 #ifdef HAVE_GSSAPI
diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/mech-skey.c dovecot/src/auth/mech-skey.c
--- dovecot.vanilla/src/auth/mech-skey.c	1970-01-01 03:00:00.000000000 +0300
+++ dovecot/src/auth/mech-skey.c	2006-06-23 13:44:31.823790488 +0400
@@ -0,0 +1,224 @@
+/*
+ * S/Key (RFC 1731) authentication mechanism.
+ *
+ * Copyright (c) 2006 Andrey Panin <pazke at donpac.ru>
+ *
+ * This software is released under the MIT license.
+ */
+
+#include "common.h"
+#include "safe-memset.h"
+#include "hash.h"
+#include "mech.h"
+#include "passdb.h"
+#include "hex-binary.h"
+#include "otp.h"
+#include "otp-skey-common.h"
+
+static void 
+skey_send_challenge(struct auth_request *auth_request,
+		    const char *credentials)
+{
+	struct otp_auth_request *request =
+		(struct otp_auth_request *)auth_request;
+	const char *answer;
+
+	if (otp_parse_dbentry(credentials, &request->state) != 0) {
+		auth_request_log_error(&request->auth_request, "skey",
+				       "invalid OTP data in passdb");
+		auth_request_fail(auth_request);
+		return;
+	}
+
+	if (request->state.algo != OTP_HASH_MD4) {
+		auth_request_log_error(&request->auth_request, "skey",
+				       "md4 hash is needed");
+		auth_request_fail(auth_request);
+		return;
+	}
+
+	if (--request->state.seq < 1) {
+		auth_request_log_error(&request->auth_request, "skey",
+				       "sequence number < 1");
+		auth_request_fail(auth_request);
+		return;
+	}
+
+	request->lock = otp_try_lock(auth_request);
+	if (request->lock == FALSE) {
+		auth_request_log_error(&request->auth_request, "skey",
+				       "user %s is locked, race attack ?",
+				       auth_request->user);
+		auth_request_fail(auth_request);
+		return;
+	}
+
+	answer = p_strdup_printf(request->pool, "%u %s",
+				 request->state.seq, request->state.seed);
+
+	auth_request->callback(auth_request,
+			       AUTH_CLIENT_RESULT_CONTINUE,
+			       answer, strlen(answer));
+}
+
+static void
+otp_credentials_callback(enum passdb_result result,
+			 const char *credentials,
+			 struct auth_request *auth_request)
+{
+	switch (result) {
+	case PASSDB_RESULT_OK:
+		skey_send_challenge(auth_request, credentials);
+		break;
+	case PASSDB_RESULT_INTERNAL_FAILURE:
+		auth_request_internal_failure(auth_request);
+		break;
+	default:
+		auth_request_fail(auth_request);
+		break;
+	}
+}
+
+static void
+skey_credentials_callback(enum passdb_result result,
+			  const char *credentials,
+			  struct auth_request *auth_request)
+{
+	switch (result) {
+	case PASSDB_RESULT_OK:
+		skey_send_challenge(auth_request, credentials);
+		break;
+	case PASSDB_RESULT_INTERNAL_FAILURE:
+		auth_request_internal_failure(auth_request);
+		break;
+	default:
+		/* S/KEY credentials not found, try OTP */
+		auth_request_lookup_credentials(auth_request,
+						PASSDB_CREDENTIALS_OTP,
+						otp_credentials_callback);
+		break;
+	}
+}
+
+static void
+mech_skey_auth_phase1(struct auth_request *auth_request,
+		      const unsigned char *data, size_t data_size)
+{
+	const char *username, *error;
+
+	username = t_strndup(data, data_size);
+
+	if (!auth_request_set_username(auth_request, username, &error)) {
+		auth_request_log_info(auth_request, "skey", "%s", error);
+		auth_request_fail(auth_request);
+		return;
+	}
+
+	auth_request_lookup_credentials(auth_request, PASSDB_CREDENTIALS_SKEY,
+					skey_credentials_callback);
+}
+
+static void
+mech_skey_auth_phase2(struct auth_request *auth_request,
+		      const unsigned char *data, size_t data_size)
+{
+	struct otp_auth_request *request =
+		(struct otp_auth_request *)auth_request;
+	struct otp_state *state = &request->state;
+	unsigned char hash[OTP_HASH_SIZE], cur_hash[OTP_HASH_SIZE];
+	int ret;
+
+	if (data_size == 8) {
+		memcpy(hash, data, 8);
+	} else {
+		const char *words = t_strndup(data, data_size);
+
+		ret = otp_parse_response(words, hash, 0);
+		if (ret < 0) {
+			auth_request_log_error(&request->auth_request, "skey",
+					       "invalid response");
+			auth_request_fail(auth_request);
+			otp_unlock(auth_request);
+			return;
+		}
+	}
+
+	otp_next_hash(state->algo, hash, cur_hash);
+
+	ret = memcmp(cur_hash, state->hash, OTP_HASH_SIZE);
+	if (ret != 0) {
+		auth_request_fail(auth_request);
+		otp_unlock(auth_request);
+		return;
+	}
+
+	memcpy(state->hash, hash, sizeof(state->hash));
+
+	auth_request_set_credentials(auth_request,
+				     PASSDB_CREDENTIALS_OTP,
+				     otp_print_dbentry(state),
+				     otp_set_credentials_callback);
+}
+
+static void
+mech_skey_auth_continue(struct auth_request *auth_request,
+		       const unsigned char *data, size_t data_size)
+{
+	if (auth_request->user == NULL) {
+		mech_skey_auth_phase1(auth_request, data, data_size);
+	} else {
+		mech_skey_auth_phase2(auth_request, data, data_size);
+	}
+}
+
+static void
+mech_skey_auth_initial(struct auth_request *request,
+		      const unsigned char *data, size_t data_size)
+{
+	if (data_size == 0) {
+		request->callback(request, AUTH_CLIENT_RESULT_CONTINUE,
+				  NULL, 0);
+	} else {
+		mech_skey_auth_continue(request, data, data_size);
+	}
+}
+
+static void
+mech_skey_auth_free(struct auth_request *request)
+{
+	otp_unlock(request);
+
+	pool_unref(request->pool);
+}
+
+static struct auth_request *mech_skey_auth_new(void)
+{
+	struct otp_auth_request *request;
+	pool_t pool;
+
+	otp_lock_init();
+
+	pool = pool_alloconly_create("skey_auth_request", 256);
+	request = p_new(pool, struct otp_auth_request, 1);
+	request->pool = pool;
+	request->lock = FALSE;
+
+	request->auth_request.refcount = 1;
+	request->auth_request.pool = pool;
+	return &request->auth_request;
+}
+
+const struct mech_module mech_skey = {
+	"SKEY",
+
+	MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE,
+
+	MEMBER(passdb_need_plain) FALSE,
+	MEMBER(passdb_need_credentials) TRUE,
+	MEMBER(passdb_need_set_credentials) TRUE,
+
+	mech_skey_auth_new,
+	mech_skey_auth_initial,
+	mech_skey_auth_continue,
+	mech_skey_auth_free
+};



More information about the dovecot mailing list