[Dovecot] [PATCH] RPA authentication mechanism

Andrey Panin pazke at donpac.ru
Mon Aug 9 15:33:35 EEST 2004


Hello all,

attached patch implements Compuserve RPA athentication mechanism.

Tested with:
	- Eudora 6 (uses Compuserve "Virtual Key" RPA software);
	- TheBat! 2.11 (uses it's own RPA implementation).

I hope it can be useful for someone.

Best regards.

-- 
Andrey Panin		| Linux and UNIX system administrator
pazke at donpac.ru		| PGP key: wwwkeys.pgp.net
-------------- next part --------------
diff -urpNX /usr/share/dontdiff dovecot-1.0-test32.vanilla/src/auth/Makefile.am dovecot-1.0-test32/src/auth/Makefile.am
--- dovecot-1.0-test32.vanilla/src/auth/Makefile.am	2004-07-30 05:37:21.000000000 +0400
+++ dovecot-1.0-test32/src/auth/Makefile.am	2004-08-07 22:53:06.000000000 +0400
@@ -17,7 +17,8 @@ libpassword_a_SOURCES = \
 	password-scheme.c \
 	password-scheme-md5crypt.c \
 	password-scheme-cram-md5.c \
-	password-scheme-ntlm.c
+	password-scheme-ntlm.c \
+	password-scheme-rpa.c
 
 dovecot_auth_LDADD = \
 	libpassword.a \
@@ -45,6 +46,7 @@ dovecot_auth_SOURCES = \
 	mech-cram-md5.c \
 	mech-digest-md5.c \
 	mech-ntlm.c \
+	mech-rpa.c \
 	mech-apop.c \
 	passdb.c \
 	passdb-bsdauth.c \
diff -urpNX /usr/share/dontdiff dovecot-1.0-test32.vanilla/src/auth/mech.c dovecot-1.0-test32/src/auth/mech.c
--- dovecot-1.0-test32.vanilla/src/auth/mech.c	2004-07-28 19:07:13.000000000 +0400
+++ dovecot-1.0-test32/src/auth/mech.c	2004-07-30 21:17:33.000000000 +0400
@@ -389,6 +389,7 @@ extern struct mech_module mech_apop;
 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_rpa;
 extern struct mech_module mech_anonymous;
 
 void mech_init(void)
@@ -424,6 +425,8 @@ void mech_init(void)
 			mech_register_module(&mech_digest_md5);
 		else if (strcasecmp(*mechanisms, "NTLM") == 0)
 			mech_register_module(&mech_ntlm);
+		else if (strcasecmp(*mechanisms, "RPA") == 0)
+			mech_register_module(&mech_rpa);
 		else if (strcasecmp(*mechanisms, "ANONYMOUS") == 0) {
 			if (anonymous_username == NULL) {
 				i_fatal("ANONYMOUS listed in mechanisms, "
@@ -485,5 +488,6 @@ void mech_deinit(void)
 	mech_unregister_module(&mech_cram_md5);
 	mech_unregister_module(&mech_digest_md5);
 	mech_unregister_module(&mech_ntlm);
+	mech_unregister_module(&mech_rpa);
 	mech_unregister_module(&mech_anonymous);
 }
diff -urpNX /usr/share/dontdiff dovecot-1.0-test32.vanilla/src/auth/mech-rpa.c dovecot-1.0-test32/src/auth/mech-rpa.c
--- dovecot-1.0-test32.vanilla/src/auth/mech-rpa.c	1970-01-01 03:00:00.000000000 +0300
+++ dovecot-1.0-test32/src/auth/mech-rpa.c	2004-08-07 23:43:01.000000000 +0400
@@ -0,0 +1,612 @@
+/*
+ * Compuserve RPA authentication mechanism.
+ *
+ * Copyright (c) 2004 Andrey Panin <pazke at donpac.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published 
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "common.h"
+#include "mech.h"
+#include "passdb.h"
+#include "str.h"
+#include "strfuncs.h"
+#include "safe-memset.h"
+#include "randgen.h"
+#include "buffer.h"
+#include "hostpid.h"
+#include "hex-binary.h"
+#include "md5.h"
+
+struct rpa_auth_request {
+	struct auth_request auth_request;
+
+	pool_t pool;
+
+	int phase;
+
+	/* cached: */
+	unsigned char *pwd_md5;
+	size_t service_len;
+	const unsigned char *service_ucs2be;
+	size_t username_len;
+	const unsigned char *username_ucs2be;
+	size_t realm_len;
+	const unsigned char *realm_ucs2be;
+
+	/* requested: */
+	unsigned char *service_challenge;
+	unsigned char *service_timestamp;
+
+	/* received: */
+	unsigned int user_challenge_len;
+	unsigned char *user_challenge;
+	unsigned char *user_response;
+	unsigned char *session_key;
+};
+
+#define RPA_SCHALLENGE_LEN	32
+#define RPA_UCHALLENGE_LEN	16
+#define RPA_TIMESTAMP_LEN	14
+
+#define ASN1_APPLICATION	0x60
+
+/* Object id encoded using ASN.1 DER */
+static const unsigned char rpa_oid[] = {
+	0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x73, 0x01, 0x01,
+};
+
+void * ucs2be_str(pool_t pool, const char *str, size_t *size);
+
+/*
+ * Compute client -> server authentication response.
+ */
+static void rpa_user_response(struct rpa_auth_request *auth,
+			      unsigned char *digest)
+{
+	struct md5_context ctx;
+	unsigned char z[48];
+
+	memset(z, 0, sizeof(z));
+
+	md5_init(&ctx);
+	md5_update(&ctx, auth->pwd_md5, 16);
+	md5_update(&ctx, z, sizeof(z));
+	md5_update(&ctx, auth->username_ucs2be, auth->username_len);
+	md5_update(&ctx, auth->service_ucs2be, auth->service_len);
+	md5_update(&ctx, auth->realm_ucs2be, auth->realm_len);
+	md5_update(&ctx, auth->user_challenge, auth->user_challenge_len);
+	md5_update(&ctx, auth->service_challenge, RPA_SCHALLENGE_LEN);
+	md5_update(&ctx, auth->service_timestamp, RPA_TIMESTAMP_LEN);
+	md5_update(&ctx, auth->pwd_md5, 16);
+	md5_final(&ctx, digest);
+}
+
+/*
+ * Compute server -> client authentication response.
+ */
+static void rpa_server_response(struct rpa_auth_request *auth,
+				unsigned char *digest)
+{
+	struct md5_context ctx;
+	unsigned char tmp[16];
+	unsigned char z[48];
+	int i;
+
+	memset(z, 0, sizeof(z));
+
+	md5_init(&ctx);
+	md5_update(&ctx, auth->pwd_md5, 16);
+	md5_update(&ctx, z, sizeof(z));
+	md5_update(&ctx, auth->service_ucs2be, auth->service_len);
+	md5_update(&ctx, auth->username_ucs2be, auth->username_len);
+	md5_update(&ctx, auth->realm_ucs2be, auth->realm_len);
+	md5_update(&ctx, auth->service_challenge, RPA_SCHALLENGE_LEN);
+	md5_update(&ctx, auth->user_challenge, auth->user_challenge_len);
+	md5_update(&ctx, auth->service_timestamp, RPA_TIMESTAMP_LEN);
+	md5_update(&ctx, auth->pwd_md5, 16);
+	md5_final(&ctx, tmp);
+
+	for (i = 0; i < 16; i++)
+		tmp[i] = auth->session_key[i] ^ tmp[i];
+
+	md5_init(&ctx);
+	md5_update(&ctx, auth->pwd_md5, 16);
+	md5_update(&ctx, z, sizeof(z));
+	md5_update(&ctx, auth->service_ucs2be, auth->service_len);
+	md5_update(&ctx, auth->username_ucs2be, auth->username_len);
+	md5_update(&ctx, auth->realm_ucs2be, auth->realm_len);
+	md5_update(&ctx, auth->session_key, 16);
+	md5_update(&ctx, auth->service_challenge, RPA_SCHALLENGE_LEN);
+	md5_update(&ctx, auth->user_challenge, auth->user_challenge_len);
+	md5_update(&ctx, auth->service_timestamp, RPA_TIMESTAMP_LEN);
+	md5_update(&ctx, tmp, 16);
+	md5_update(&ctx, auth->pwd_md5, 16);
+	md5_final(&ctx, digest);
+}
+
+static const unsigned char *
+rpa_check_message(const unsigned char *data, const unsigned char *end,
+		  char **error)
+{
+	const unsigned char *p = data;
+	unsigned int len = 0;
+
+	if (*p++ != ASN1_APPLICATION) {
+		*error = "invalid data type";
+		return NULL;
+	}
+
+	if (*p & 0x80) {
+		unsigned int nbytes = *p++ & 0x7f;
+
+		for ( ; nbytes--; ) {
+			if (p >= end) {
+				*error = "invalid structure length";
+				return NULL;
+			}
+
+			len = (len << 8) | *p++;
+		}
+	} else
+		len = *p++;
+
+	if (p + len != end) {
+		*error = "structure length disagrees with data size";
+		return NULL;
+	}
+
+	if (p + sizeof(rpa_oid) > end) {
+		*error = "not enough space for object id";
+		return NULL;
+	}
+
+	if (memcmp(p, rpa_oid, sizeof(rpa_oid))) {
+		*error = "invalid object id";
+		return NULL;
+	}
+
+	return p + sizeof(rpa_oid);
+}
+
+static int
+rpa_parse_token1(const void *data, size_t data_size, char **error)
+{
+	const unsigned char *end = ((unsigned char *) data) + data_size;
+	const unsigned char *p;
+	unsigned int version_lo, version_hi;
+
+	p = rpa_check_message(data, end, error);
+	if (!p)
+		return FALSE;
+
+	if (p + 6 > end) {
+		*error = "message too short";
+		return FALSE;
+	}
+
+	version_lo = p[0] + (p[1] << 8);
+	version_hi = p[2] + (p[3] << 8);
+
+	if ((version_lo > 3) || (version_hi < 3)) {
+		*error = "protocol version mismatch";
+		return FALSE;
+	}
+	p += 4;
+
+	if ((p[0] != 0) || (p[1] != 1)) {
+		*error = "invalid message flags";
+		return FALSE;
+	}
+	p += 2;
+
+	if (p != end) {
+		*error = "unneeded data found";
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static unsigned int
+rpa_read_buffer(pool_t pool, const unsigned char **data,
+		const unsigned char *end, unsigned char **buffer)
+{
+	const unsigned char *p = *data;
+	unsigned int len;
+
+	if (p > end)
+		return 0;
+
+	len = *p++;
+	if (p + len > end)
+		return 0;
+
+	*buffer = p_malloc(pool, len);
+	memcpy(*buffer, p, len);
+
+	*data += 1 + len;
+
+	return len;
+}
+
+static char *
+rpa_parse_username(pool_t pool, const char *username)
+{
+	const char *p = strrchr(username, '@');
+
+	return p_strndup(pool, username, p - username);
+}
+
+static int
+rpa_parse_token3(struct rpa_auth_request *auth, const void *data,
+		 size_t data_size, char **error)
+{
+	struct auth_request *auth_request = (struct auth_request *) auth;
+	const unsigned char *end = ((unsigned char *) data) + data_size;
+	const unsigned char *p;
+	unsigned int len;
+	const char *user;
+
+	p = rpa_check_message(data, end, error);
+	if (!p)
+		return FALSE;
+
+	/* Read username at realm */
+	if (p + 2 > end) {
+		*error = "message too short";
+		return FALSE;
+	}
+
+	len = (p[0] >> 8) + p[1];
+	if (p + len > end) {
+		*error = "message too short";
+		return FALSE;
+	}
+	p += 2;
+
+	user = t_strndup(p, len);
+	p += len;
+
+	auth_request->user = rpa_parse_username(auth->pool, user);
+
+	auth->username_ucs2be = ucs2be_str(auth->pool, auth_request->user,
+					    &auth->username_len);
+
+	/* Read user challenge */
+	auth->user_challenge_len = rpa_read_buffer(auth->pool, &p, end,
+						   &auth->user_challenge);
+	if (auth->user_challenge_len == 0) {
+		*error = "invalid user challenge";
+		return FALSE;
+	}
+
+	/* Read user response */
+	len = rpa_read_buffer(auth->pool, &p, end, &auth->user_response);
+	if (len != RPA_UCHALLENGE_LEN) {
+		*error = "invalid user response";
+		return FALSE;
+	}
+
+	if (p != end) {
+		*error = "unneeded data found";
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+buffer_append_asn1_length(buffer_t *buf, unsigned int length)
+{
+	if (length < 0x80) {
+		buffer_append_c(buf, length);
+	} else if (length < 0x100) {
+		buffer_append_c(buf, 0x81);
+		buffer_append_c(buf, length);
+	} else {
+		buffer_append_c(buf, 0x82);
+		buffer_append_c(buf, length >> 8);
+		buffer_append_c(buf, length & 0xff);
+	}
+}
+
+static const char *
+mech_rpa_build_token2(struct rpa_auth_request *auth,
+		      const char *realms, size_t *size)
+{
+	unsigned int realms_len = strlen(realms);
+	unsigned int length = sizeof(rpa_oid) + 3 + RPA_SCHALLENGE_LEN +
+		RPA_TIMESTAMP_LEN + 2 + realms_len;
+	buffer_t *buf = buffer_create_dynamic(auth->pool, length + 4, 4096);
+	unsigned char timestamp[RPA_TIMESTAMP_LEN / 2];
+
+	buffer_append_c(buf, ASN1_APPLICATION);
+	buffer_append_asn1_length(buf, length);
+	buffer_append(buf, rpa_oid, sizeof(rpa_oid));
+
+	/* Protocol version */
+	buffer_append_c(buf, 3);
+	buffer_append_c(buf, 0);
+
+	/* Service challenge */
+	auth->service_challenge = p_malloc(auth->pool, RPA_SCHALLENGE_LEN);
+	random_fill(auth->service_challenge, RPA_SCHALLENGE_LEN);
+	buffer_append_c(buf, RPA_SCHALLENGE_LEN);
+	buffer_append(buf, auth->service_challenge, RPA_SCHALLENGE_LEN);
+
+	/* Timestamp, looks like clients accept anything we send */
+	random_fill(timestamp, sizeof(timestamp));
+	auth->service_timestamp = p_malloc(auth->pool, RPA_TIMESTAMP_LEN);
+	memcpy(auth->service_timestamp,
+	       binary_to_hex(timestamp, sizeof(timestamp)),
+	       RPA_TIMESTAMP_LEN);
+	buffer_append(buf, auth->service_timestamp, RPA_TIMESTAMP_LEN);
+
+	/* Realm list */
+	buffer_append_c(buf, realms_len >> 8);
+	buffer_append_c(buf, realms_len & 0xff);
+	buffer_append(buf, realms, realms_len);
+
+	*size = buffer_get_used_size(buf);
+	return buffer_free_without_data(buf);
+}
+
+static const char *
+mech_rpa_build_token4(struct rpa_auth_request *auth, size_t *size)
+{
+	unsigned int length = sizeof(rpa_oid) + 17 + 17 + 1;
+	buffer_t *buf = buffer_create_dynamic(auth->pool, length + 4, 4096);
+	unsigned char server_response[16];
+
+	buffer_append_c(buf, ASN1_APPLICATION);
+	buffer_append_asn1_length(buf, length);
+	buffer_append(buf, rpa_oid, sizeof(rpa_oid));
+
+	/* Generate random session key */
+	auth->session_key = p_malloc(auth->pool, 16);
+	random_fill(auth->session_key, 16);
+
+	/* Server authentication response */
+	rpa_server_response(auth, server_response);
+	buffer_append_c(buf, 16);
+	buffer_append(buf, server_response, 16);
+
+	buffer_append_c(buf, 16);
+	buffer_append(buf, auth->session_key, 16);
+
+	/* Status, 0 - success */
+	buffer_append_c(buf, 0);
+
+	*size = buffer_get_used_size(buf);
+	return buffer_free_without_data(buf);
+}
+
+static void
+rpa_credentials_callback(const char *credentials,
+			 struct auth_request *auth_request)
+{
+	struct rpa_auth_request *auth =
+		(struct rpa_auth_request *)auth_request;
+	buffer_t *hash_buffer;
+
+	if (credentials == NULL)
+		return;
+
+	auth->pwd_md5 = p_malloc(auth->pool, 16);
+
+	hash_buffer = buffer_create_data(auth->pool, auth->pwd_md5, 16);
+
+	hex_to_binary(credentials, hash_buffer);
+}
+
+static int
+mech_rpa_auth_phase1(struct auth_request *auth_request,
+		     const unsigned char *data, size_t data_size,
+		     mech_callback_t *callback)
+{
+	struct rpa_auth_request *auth =
+		(struct rpa_auth_request *)auth_request;
+	struct auth_client_request_reply reply;
+	const unsigned char *token2;
+	size_t token2_size;
+	const char *service;
+	char *error;
+
+	if (!rpa_parse_token1(data, data_size, &error)) {
+		if (verbose)
+			i_info("rpa(%s): invalid token 1, %s",
+			       get_log_prefix(auth_request),
+			       error);
+		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		return TRUE;
+	}
+
+	service = t_str_lcase(auth_request->protocol);
+
+	token2 = mech_rpa_build_token2(auth, t_strconcat(service, "@",
+				       my_hostname, NULL), &token2_size);
+
+	auth->service_ucs2be = ucs2be_str(auth->pool, service,
+					  &auth->service_len);
+	auth->realm_ucs2be = ucs2be_str(auth->pool, my_hostname,
+					&auth->realm_len);
+
+	mech_init_auth_client_reply(&reply);
+	reply.id = auth_request->id;
+	reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+
+	reply.reply_idx = 0;
+	reply.data_size = token2_size;
+	callback(&reply, token2, auth_request->conn);
+
+	auth->phase = 1;
+
+	return TRUE;
+}
+
+static int
+mech_rpa_auth_phase2(struct auth_request *auth_request,
+		     const unsigned char *data, size_t data_size,
+		     mech_callback_t *callback)
+{
+	struct rpa_auth_request *auth =
+		(struct rpa_auth_request *)auth_request;
+	struct auth_client_request_reply reply;
+	unsigned char response[16];
+	const unsigned char *token4;
+	size_t token4_size;
+	char *error;
+
+	if (!rpa_parse_token3(auth, data, data_size, &error)) {
+		if (verbose)
+			i_info("rpa(%s): invalid token 3, %s",
+			       get_log_prefix(auth_request),
+			       error);
+		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		return TRUE;
+	}
+
+	if (!mech_is_valid_username(auth_request->user)) {
+		if (verbose)
+			i_info("rpa(%s): invalid username",
+			       get_log_prefix(auth_request));
+		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		return TRUE;
+	}
+
+	passdb->lookup_credentials(auth_request, PASSDB_CREDENTIALS_RPA,
+				   rpa_credentials_callback);
+	if (auth->pwd_md5 == NULL) {
+		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		return TRUE;
+	}
+
+	rpa_user_response(auth, response);
+	if (memcmp(response, auth->user_response, 16) != 0) {
+		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		return TRUE;
+	}
+
+	token4 = mech_rpa_build_token4(auth, &token4_size);
+
+	mech_init_auth_client_reply(&reply);
+	reply.id = auth_request->id;
+	reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+
+	reply.reply_idx = 0;
+	reply.data_size = token4_size;
+	callback(&reply, token4, auth_request->conn);
+
+	auth->phase = 2;
+
+	return TRUE;
+}
+
+static int
+mech_rpa_auth_phase3(struct auth_request *auth_request,
+		     const unsigned char *data, size_t data_size,
+		     mech_callback_t *callback __attr_unused__)
+{
+	static const unsigned char client_ack[3] = { 0x60, 0x01, 0x00 };
+	int ret = TRUE;
+
+	if ((data_size != sizeof(client_ack)) ||
+	    (memcmp(data, client_ack, sizeof(client_ack)) != 0)) {
+		if (verbose)
+			i_info("rpa(%s): invalid token 5 or client rejects us",
+			       get_log_prefix(auth_request));
+		ret = FALSE;
+	}
+
+	mech_auth_finish(auth_request, NULL, 0, ret);
+	return TRUE;
+}
+
+static int
+mech_rpa_auth_continue(struct auth_request *auth_request,
+			const unsigned char *data, size_t data_size,
+			mech_callback_t *callback)
+{
+	struct rpa_auth_request *auth =
+		(struct rpa_auth_request *)auth_request;
+
+	auth_request->callback = callback;
+
+	switch (auth->phase) {
+		case 0:	return mech_rpa_auth_phase1(auth_request, data,
+						    data_size, callback);
+		case 1:	return mech_rpa_auth_phase2(auth_request, data,
+						    data_size, callback);
+		case 2:	return mech_rpa_auth_phase3(auth_request, data,
+						    data_size, callback);
+	}
+
+	mech_auth_finish(auth_request, NULL, 0, FALSE);
+	return TRUE;
+}
+
+static int
+mech_rpa_auth_initial(struct auth_request *auth_request,
+		      struct auth_client_request_new *request,
+		      const unsigned char *data __attr_unused__,
+		      mech_callback_t *callback)
+{
+	struct auth_client_request_reply reply;
+
+	mech_init_auth_client_reply(&reply);
+	reply.id = request->id;
+	reply.result = AUTH_CLIENT_RESULT_CONTINUE;
+
+	reply.reply_idx = 0;
+	reply.data_size = 0;
+	callback(&reply, "", auth_request->conn);
+
+	return TRUE;
+}
+
+static void
+mech_rpa_auth_free(struct auth_request *auth_request)
+{
+	struct rpa_auth_request *auth =
+		(struct rpa_auth_request *)auth_request;
+
+	if (auth->pwd_md5)
+		safe_memset(auth->pwd_md5, 0, 16);
+
+	pool_unref(auth_request->pool);
+}
+
+static struct auth_request *mech_rpa_auth_new(void)
+{
+	struct rpa_auth_request *auth;
+	pool_t pool;
+
+	pool = pool_alloconly_create("rpa_auth_request", 256);
+	auth = p_new(pool, struct rpa_auth_request, 1);
+	auth->pool = pool;
+	auth->phase = 0;
+
+	auth->auth_request.refcount = 1;
+	auth->auth_request.pool = pool;
+	auth->auth_request.auth_initial = mech_rpa_auth_initial;
+	auth->auth_request.auth_continue = mech_rpa_auth_continue;
+	auth->auth_request.auth_free = mech_rpa_auth_free;
+
+	return &auth->auth_request;
+}
+
+const struct mech_module mech_rpa = {
+	"RPA",
+
+	MEMBER(plaintext) FALSE,
+	MEMBER(advertise) TRUE,
+
+	MEMBER(passdb_need_plain) FALSE,
+	MEMBER(passdb_need_credentials) TRUE,
+
+	mech_rpa_auth_new,
+};
diff -urpNX /usr/share/dontdiff dovecot-1.0-test32.vanilla/src/auth/passdb.c dovecot-1.0-test32/src/auth/passdb.c
--- dovecot-1.0-test32.vanilla/src/auth/passdb.c	2004-07-30 05:40:29.000000000 +0400
+++ dovecot-1.0-test32/src/auth/passdb.c	2004-08-07 23:11:25.000000000 +0400
@@ -32,6 +32,8 @@ passdb_credentials_to_str(enum passdb_cr
 		return "LANMAN";
 	case PASSDB_CREDENTIALS_NTLM:
 		return "NTLM";
+	case PASSDB_CREDENTIALS_RPA:
+		return "RPA";
 	}
 
 	return "??";
diff -urpNX /usr/share/dontdiff dovecot-1.0-test32.vanilla/src/auth/passdb.h dovecot-1.0-test32/src/auth/passdb.h
--- dovecot-1.0-test32.vanilla/src/auth/passdb.h	2004-07-30 05:40:29.000000000 +0400
+++ dovecot-1.0-test32/src/auth/passdb.h	2004-08-07 22:35:50.000000000 +0400
@@ -14,7 +14,8 @@ enum passdb_credentials {
 	PASSDB_CREDENTIALS_CRAM_MD5,
 	PASSDB_CREDENTIALS_DIGEST_MD5,
 	PASSDB_CREDENTIALS_LANMAN,
-	PASSDB_CREDENTIALS_NTLM
+	PASSDB_CREDENTIALS_NTLM,
+	PASSDB_CREDENTIALS_RPA
 };
 
 enum passdb_result {
diff -urpNX /usr/share/dontdiff dovecot-1.0-test32.vanilla/src/auth/password-scheme.c dovecot-1.0-test32/src/auth/password-scheme.c
--- dovecot-1.0-test32.vanilla/src/auth/password-scheme.c	2004-07-30 05:40:29.000000000 +0400
+++ dovecot-1.0-test32/src/auth/password-scheme.c	2004-08-07 23:09:27.000000000 +0400
@@ -424,6 +424,18 @@ static const char *ntlm_generate(const c
 	return password_generate_ntlm(plaintext);
 }
 
+static int rpa_verify(const char *plaintext, const char *password,
+		      const char *user __attr_unused__)
+{
+	return strcasecmp(password, password_generate_rpa(plaintext)) == 0;
+}
+
+static const char *rpa_generate(const char *plaintext,
+				const char *user __attr_unused__)
+{
+	return password_generate_rpa(plaintext);
+}
+
 static const struct password_scheme default_schemes[] = {
 	{ "CRYPT", crypt_verify, crypt_generate },
 	{ "MD5", md5_verify, md5_generate },
@@ -439,6 +451,7 @@ static const struct password_scheme defa
 	{ "LDAP-MD5", ldap_md5_verify, ldap_md5_generate },
 	{ "LANMAN", lm_verify, lm_generate },
 	{ "NTLM", ntlm_verify, ntlm_generate },
+	{ "RPA", rpa_verify, rpa_generate },
 	{ NULL, NULL, NULL }
 };
 
diff -urpNX /usr/share/dontdiff dovecot-1.0-test32.vanilla/src/auth/password-scheme.h dovecot-1.0-test32/src/auth/password-scheme.h
--- dovecot-1.0-test32.vanilla/src/auth/password-scheme.h	2004-07-30 05:40:29.000000000 +0400
+++ dovecot-1.0-test32/src/auth/password-scheme.h	2004-08-07 22:36:55.000000000 +0400
@@ -32,5 +32,6 @@ const char *password_generate_md5_crypt(
 const char *password_generate_cram_md5(const char *pw);
 const char *password_generate_lm(const char *pw);
 const char *password_generate_ntlm(const char *pw);
+const char *password_generate_rpa(const char *pw);
 
 #endif
diff -urpNX /usr/share/dontdiff dovecot-1.0-test32.vanilla/src/auth/password-scheme-rpa.c dovecot-1.0-test32/src/auth/password-scheme-rpa.c
--- dovecot-1.0-test32.vanilla/src/auth/password-scheme-rpa.c	1970-01-01 03:00:00.000000000 +0300
+++ dovecot-1.0-test32/src/auth/password-scheme-rpa.c	2004-08-07 23:40:35.000000000 +0400
@@ -0,0 +1,40 @@
+
+#include "lib.h"
+#include "buffer.h"
+#include "md5.h"
+#include "hex-binary.h"
+#include "safe-memset.h"
+#include "password-scheme.h"
+
+void * ucs2be_str(pool_t pool, const char *str, size_t *size);
+
+/*
+ * Convert string to big-endian ucs2.
+ */
+void * ucs2be_str(pool_t pool, const char *str, size_t *size)
+{
+	buffer_t *buf = buffer_create_dynamic(pool, 32, 4096);
+
+	while (*str) {
+		buffer_append_c(buf, '\0');
+		buffer_append_c(buf, *str++);
+	}
+
+	*size = buffer_get_used_size(buf);
+	return buffer_free_without_data(buf);
+}
+
+const char *password_generate_rpa(const char *pw)
+{
+	unsigned char hash[16];
+	unsigned char *ucs2be_pw;
+	size_t size;
+
+	ucs2be_pw = ucs2be_str(unsafe_data_stack_pool, pw, &size);
+
+	md5_get_digest(ucs2be_pw, size, hash);
+
+	safe_memset(ucs2be_pw, 0, size);
+
+	return binary_to_hex(hash, sizeof(hash));
+}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 189 bytes
Desc: Digital signature
URL: <http://dovecot.org/pipermail/dovecot/attachments/20040809/e3f743b6/attachment-0001.bin>


More information about the dovecot mailing list