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