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.762799760 +0400 +++ dovecot/src/auth/Makefile.am 2006-06-23 13:44:31.763799608 +0400 @@ -59,6 +59,7 @@ dovecot_auth_SOURCES = \ mech-cram-md5.c \ mech-digest-md5.c \ mech-ntlm.c \ + mech-otp.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:42:22.124507776 +0400 +++ dovecot/src/auth/mech.c 2006-06-23 13:44:31.764799456 +0400 @@ -68,6 +68,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_otp; extern struct mech_module mech_rpa; extern struct mech_module mech_anonymous; #ifdef HAVE_GSSAPI @@ -82,6 +83,7 @@ void mech_init(void) mech_register_module(&mech_cram_md5); mech_register_module(&mech_digest_md5); mech_register_module(&mech_ntlm); + mech_register_module(&mech_otp); mech_register_module(&mech_rpa); mech_register_module(&mech_anonymous); #ifdef HAVE_GSSAPI @@ -97,6 +99,7 @@ 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_otp); 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-otp.c dovecot/src/auth/mech-otp.c --- dovecot.vanilla/src/auth/mech-otp.c 1970-01-01 03:00:00.000000000 +0300 +++ dovecot/src/auth/mech-otp.c 2006-06-23 13:44:31.764799456 +0400 @@ -0,0 +1,295 @@ +/* + * One-Time-Password (RFC 2444) authentication mechanism. + * + * Copyright (c) 2006 Andrey Panin <pazke@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 +otp_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, "otp", + "invalid OTP data in passdb"); + auth_request_fail(auth_request); + return; + } + + if (--request->state.seq < 1) { + auth_request_log_error(&request->auth_request, "otp", + "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, "otp", + "user %s is locked, race attack ?", + auth_request->user); + auth_request_fail(auth_request); + return; + } + + answer = p_strdup_printf(request->pool, "otp-%s %u %s ext", + digest_name(request->state.algo), + request->state.seq, request->state.seed); + + auth_request->callback(auth_request, + AUTH_CLIENT_RESULT_CONTINUE, + answer, strlen(answer)); +} + +static void +skey_credentials_callback(enum passdb_result result, + const char *credentials, + struct auth_request *auth_request) +{ + switch (result) { + case PASSDB_RESULT_OK: + otp_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 +otp_credentials_callback(enum passdb_result result, + const char *credentials, + struct auth_request *auth_request) +{ + switch (result) { + case PASSDB_RESULT_OK: + otp_send_challenge(auth_request, credentials); + break; + case PASSDB_RESULT_INTERNAL_FAILURE: + auth_request_internal_failure(auth_request); + break; + default: + /* OTP credentials not found, try S/KEY */ + auth_request_lookup_credentials(auth_request, + PASSDB_CREDENTIALS_OTP, + skey_credentials_callback); + break; + } +} + +static void +mech_otp_auth_phase1(struct auth_request *auth_request, + const unsigned char *data, size_t data_size) +{ + struct otp_auth_request *request = + (struct otp_auth_request *)auth_request; + const char *authzid, *authenid, *error; + size_t i, count; + + /* authorization ID \0 authentication ID + we'll ignore authorization ID for now. */ + authzid = (const char *) data; + authenid = NULL; + + count = 0; + for (i = 0; i < data_size; i++) { + if (data[i] == '\0') { + if (++count == 1) + authenid = (const char *) data + i + 1; + } + } + + /* + * Cyrus SASL library sends zero terminated authentication identity, + * thus violating RFC-2444. Let's be liberal to it. + */ + if ((count < 1) || (count > 2)) { + auth_request_log_error(&request->auth_request, "otp", + "invalid input"); + auth_request_fail(auth_request); + return; + } + + if (!auth_request_set_username(auth_request, authenid, &error)) { + auth_request_log_info(auth_request, "otp", "%s", error); + auth_request_fail(auth_request); + return; + } + + auth_request_lookup_credentials(auth_request, PASSDB_CREDENTIALS_OTP, + otp_credentials_callback); +} + +static void mech_otp_verify(struct auth_request *auth_request, + const char *data, int hex) +{ + 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; + + ret = otp_parse_response(data, hash, hex); + if (ret < 0) { + auth_request_log_error(&request->auth_request, "otp", + "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_otp_verify_init(struct auth_request *auth_request, + const char *data, int hex) +{ + struct otp_auth_request *request = + (struct otp_auth_request *)auth_request; + struct otp_state *state = &request->state; + struct otp_state new_state; + unsigned char hash[OTP_HASH_SIZE], cur_hash[OTP_HASH_SIZE]; + const char *error; + int ret; + + ret = otp_parse_init_response(data, &new_state, cur_hash, hex, &error); + if (ret < 0) { + auth_request_log_error(&request->auth_request, "otp", + "invalid init response, %s", error); + auth_request_fail(auth_request); + otp_unlock(auth_request); + return; + } + + otp_next_hash(request->state.algo, cur_hash, hash); + + ret = memcmp(hash, request->state.hash, OTP_HASH_SIZE); + if (ret != 0) { + auth_request_fail(auth_request); + otp_unlock(auth_request); + return; + } + + state = &new_state; + + auth_request_set_credentials(auth_request, + PASSDB_CREDENTIALS_OTP, + otp_print_dbentry(state), + otp_set_credentials_callback); +} + +static void +mech_otp_auth_phase2(struct auth_request *auth_request, + const unsigned char *data, size_t data_size) +{ + const char *str = t_strndup(data, data_size); + + if (strncmp(str, "hex:", 4) == 0) { + mech_otp_verify(auth_request, str + 4, 1); + } else if (strncmp(str, "word:", 5) == 0) { + mech_otp_verify(auth_request, str + 5, 0); + } else if (strncmp(str, "init-hex:", 9) == 0) { + mech_otp_verify_init(auth_request, str + 9, 1); + } else if (strncmp(str, "init-word:", 10) == 0) { + mech_otp_verify_init(auth_request, str + 10, 0); + } else { + auth_request_log_error(auth_request, "otp", + "unsupported response type"); + auth_request_fail(auth_request); + otp_unlock(auth_request); + } +} + +static void +mech_otp_auth_continue(struct auth_request *auth_request, + const unsigned char *data, size_t data_size) +{ + if (auth_request->user == NULL) { + mech_otp_auth_phase1(auth_request, data, data_size); + } else { + mech_otp_auth_phase2(auth_request, data, data_size); + } +} + +static void +mech_otp_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_otp_auth_continue(request, data, data_size); + } +} + +static void +mech_otp_auth_free(struct auth_request *request) +{ + otp_unlock(request); + + pool_unref(request->pool); +} + +static struct auth_request *mech_otp_auth_new(void) +{ + struct otp_auth_request *request; + pool_t pool; + + otp_lock_init(); + + pool = pool_alloconly_create("otp_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_otp = { + "OTP", + + 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_otp_auth_new, + mech_otp_auth_initial, + mech_otp_auth_continue, + mech_otp_auth_free +};