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 + * + * 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@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)); +}