[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