[Dovecot] [PATCH, RFC 3/13] OTP: parity table
Add OTP parity table. diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/lib-otp/otp-parity.c dovecot/src/lib-otp/otp-parity.c --- dovecot.vanilla/src/lib-otp/otp-parity.c 1970-01-01 03:00:00.000000000 +0300 +++ dovecot/src/lib-otp/otp-parity.c 2006-06-23 13:44:31.161891112 +0400 @@ -0,0 +1,29 @@ +/* + * OTP parity table. + * + * Copyright (c) 2006 Andrey Panin <pazke@donpac.ru> + * + * This software is released under the MIT license. + */ + +#include "lib.h" +#include "otp.h" + +const unsigned char parity_table[256] = { + 0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, + 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, 0, 1, 2, 3, + 2, 3, 0, 1, 3, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 0, + 3, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, + 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, 0, 1, 2, 3, + 2, 3, 0, 1, 3, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 0, + 3, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, + 0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, + 2, 3, 0, 1, 3, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 0, + 3, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, + 0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, + 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, 0, 1, 2, 3, + 3, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, + 0, 1, 2, 3, 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, + 1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, 0, 1, 2, 3, + 2, 3, 0, 1, 3, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 0, +}; diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/lib-otp/otp-parity.h dovecot/src/lib-otp/otp-parity.h --- dovecot.vanilla/src/lib-otp/otp-parity.h 1970-01-01 03:00:00.000000000 +0300 +++ dovecot/src/lib-otp/otp-parity.h 2006-06-23 13:44:31.161891112 +0400 @@ -0,0 +1,16 @@ +#ifndef __OTP_PARITY_H__ +#define __OTP_PARITY_H__ + +const unsigned char parity_table[256]; + +static inline unsigned int otp_parity(unsigned char *data) +{ + unsigned int i, parity = 0; + + for (i = 0; i < OTP_HASH_SIZE; i++) + parity += parity_table[*data++]; + + return parity & 3; +} + +#endif /* __OTP_PARITY_H__ */
Parser for OTP extended response as defined by RFC 2243. diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/lib-otp/otp-parse.c dovecot/src/lib-otp/otp-parse.c --- dovecot.vanilla/src/lib-otp/otp-parse.c 1970-01-01 03:00:00.000000000 +0300 +++ dovecot/src/lib-otp/otp-parse.c 2006-06-23 13:44:31.213883208 +0400 @@ -0,0 +1,254 @@ +/* + * OTP extended response parser. + * + * Copyright (c) 2006 Andrey Panin <pazke@donpac.ru> + * + * This software is released under the MIT license. + */ + +#include <stdlib.h> +#include <ctype.h> + +#include "lib.h" +#include "buffer.h" +#include "str.h" +#include "strfuncs.h" +#include "hex-binary.h" + +#include "otp.h" + + +#define IS_LWS(c) ((c) == ' ' || (c) == '\t') + +static inline const char *otp_skip_lws(const char *data) +{ + while (*data && IS_LWS(*data)) + data++; + return data; +} + +static inline int otp_check_tail(const char *data) +{ + data = otp_skip_lws(data); + + return *data != 0; +} + +int otp_read_hex(const char *data, const char **endptr, unsigned char *hash) +{ + string_t *str; + buffer_t *buf; + int i = 0; + + if (data == NULL) + return -1; + + str = t_str_new(18); + buf = buffer_create_data(unsafe_data_stack_pool, hash, OTP_HASH_SIZE); + + while (*data) { + char c = *data; + + if (isxdigit(c)) { + str_append_c(str, c); + if (++i == OTP_HASH_SIZE * 2) { + data++; + break; + } + } else if (!IS_LWS(c)) { + if (endptr) + *endptr = data; + return -1; + } + data++; + } + + if (endptr) + *endptr = data; + + if (i < OTP_HASH_SIZE * 2) + return -1; + + return hex_to_binary(str_c(str), buf); +} + +#define add_word() do { \ + tmp = otp_lookup_word(str_c(word)); \ + buffer_append(buf, &tmp, sizeof(tmp)); \ + count++; \ +} while (0) + +int otp_read_words(const char *data, const char **endptr, unsigned char *hash) +{ + int space = 0, len = 0, count = 0; + unsigned int parity = 0, bits[OTP_WORDS_NUMBER], tmp; + string_t *word; + buffer_t *buf; + + if (data == NULL) + return -1; + + word = t_str_new(8); + + data = otp_skip_lws(data); + + buf = buffer_create_data(pool_datastack_create(), bits, sizeof(bits)); + + for (; *data && (count < OTP_WORDS_NUMBER); data++) { + char c = *data; + + if (space) { + if (IS_LWS(c)) + continue; + else if (isalpha(c)) { + str_append_c(word, c); + space = 0; + len = 1; + continue; + } + } else { + if (isalpha(c)) { + if (len > OTP_MAX_WORD_LEN) { + count = 0; + break; + } + str_append_c(word, c); + continue; + } else if (IS_LWS(c)) { + add_word(); + str_truncate(word, 0); + space = 1; + continue; + } + } + break; + } + + if ((str_len(word) > 0) && (count == OTP_WORDS_NUMBER - 1)) + add_word(); + + if (endptr) + *endptr = data; + + if (count < OTP_WORDS_NUMBER) + return -1; + + hash[0] = bits[0] >> 3; + hash[1] = ((bits[0] & 7) << 5) | (bits[1] >> 6); + hash[2] = ((bits[1] & 0x3f) << 2) | (bits[2] >> 9); + hash[3] = (bits[2] >> 1) & 0xff; + hash[4] = ((bits[2] & 3) << 7) | (bits[3] >> 4); + hash[5] = ((bits[3] & 15) << 4) | (bits[4] >> 7); + hash[6] = ((bits[4] & 0x7f) << 1) | (bits[5] >> 10); + hash[7] = (bits[5] >> 2) & 0xff; + parity = bits[5] & 3; + + return otp_parity(hash) != parity; +} + +int otp_read_new_params(const char *data, const char **endptr, + struct otp_state *state) +{ + const char *p, *s; + int i = 0; + + s = p = data; + + while ((*p != 0) && !IS_LWS(*p)) p++; + if (*p == 0) + return -1; + + state->algo = digest_find(t_strdup_until(s, p++)); + if (state->algo < 0) + return -2; + + s = p; + state->seq = strtol(s, (char **) &p, 10); + if ((p == s) || !IS_LWS(*p)) + return -3; + p++; + + while (isalnum(*p) && (i < OTP_MAX_SEED_LEN)) + state->seed[i++] = tolower(*p++); + state->seed[i] = 0; + + *endptr = p; + return 0; +} + +int otp_parse_response(const char *data, unsigned char *hash, int hex) +{ + const char *end; + int ret = hex ? otp_read_hex(data, &end, hash) : + otp_read_words(data, &end, hash); + if (ret < 0) + return ret; + + return otp_check_tail(end); +} + +int otp_parse_init_response(const char *data, struct otp_state *new_state, + unsigned char *hash, int hex, const char **error) +{ + const char *end; + int ret = hex ? otp_read_hex(data, &end, hash) : + otp_read_words(data, &end, hash); + if (ret < 0) { + *error = "invalid current OTP"; + return ret; + } + + end = otp_skip_lws(end); + if (*end++ != ':') { + *error = "missing colon"; + return -1; + } + + ret = otp_read_new_params(end, &end, new_state); + if (ret < 0) { + *error = "invalid OTP parameters"; + return -1; + } + + end = otp_skip_lws(end); + if (*end++ != ':') { + *error = "missing colon"; + return -1; + } + + ret = hex ? otp_read_hex(end, &end, new_state->hash) : + otp_read_words(end, &end, new_state->hash); + if (ret < 0) { + *error = "invalid new OTP"; + return -1; + } + + if (otp_check_tail(end) != 0) { + *error = "trailing garbage found"; + return -1; + } + + return 0; +} + +int otp_parse_dbentry(const char *text, struct otp_state *state) +{ + const char *end; + int ret; + + ret = otp_read_new_params(text, &end, state); + if (ret != 0) + return ret; + + if (*end++ != ' ') + return -1; + + return otp_read_hex(end, NULL, state->hash); +} + +const char * otp_print_dbentry(const struct otp_state *state) +{ + return t_strdup_printf("%s %d %s %s", digest_name(state->algo), + state->seq, state->seed, + binary_to_hex(state->hash, 8)); +} diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/lib-otp/otp-parse.h dovecot/src/lib-otp/otp-parse.h --- dovecot.vanilla/src/lib-otp/otp-parse.h 1970-01-01 03:00:00.000000000 +0300 +++ dovecot/src/lib-otp/otp-parse.h 2006-06-23 13:44:31.213883208 +0400 @@ -0,0 +1,16 @@ +#ifndef __OTP_PARSE_H__ +#define __OTP_PARSE_H__ + +int otp_read_hex(const char *data, const char **endptr, unsigned char *hash); +int otp_read_words(const char *data, const char **endptr, unsigned char *hash); +int otp_read_new_params(const char *data, const char **endptr, + struct otp_state *state); + +int otp_parse_response(const char *data, unsigned char *hash, int hex); +int otp_parse_init_response(const char *data, struct otp_state *new_state, + unsigned char *hash, int hex, const char **error); + +int otp_parse_dbentry(const char *text, struct otp_state *state); +const char * otp_print_dbentry(const struct otp_state *state); + +#endif /* __OTP_PARSE_H__ */
Finally add lib-otp to the build process. diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/configure.in dovecot/configure.in --- dovecot.vanilla/configure.in 2006-06-23 13:42:22.158502608 +0400 +++ dovecot/configure.in 2006-06-23 13:44:31.276873632 +0400 @@ -1742,6 +1742,7 @@ src/lib-imap/Makefile src/lib-index/Makefile src/lib-mail/Makefile src/lib-ntlm/Makefile +src/lib-otp/Makefile src/lib-settings/Makefile src/lib-storage/Makefile src/lib-storage/index/Makefile diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/lib-otp/Makefile.am dovecot/src/lib-otp/Makefile.am --- dovecot.vanilla/src/lib-otp/Makefile.am 1970-01-01 03:00:00.000000000 +0300 +++ dovecot/src/lib-otp/Makefile.am 2006-06-23 13:44:31.276873632 +0400 @@ -0,0 +1,16 @@ +noinst_LIBRARIES = libotp.a + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib + +libotp_a_SOURCES = \ + otp-dictionary.c \ + otp-hash.c \ + otp-parity.c \ + otp-parse.c + +noinst_HEADERS = \ + otp-dictionary.h \ + otp-hash.h \ + otp-parity.h \ + otp-parse.h diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/lib-otp/otp.h dovecot/src/lib-otp/otp.h --- dovecot.vanilla/src/lib-otp/otp.h 1970-01-01 03:00:00.000000000 +0300 +++ dovecot/src/lib-otp/otp.h 2006-06-23 13:44:31.277873480 +0400 @@ -0,0 +1,22 @@ +#ifndef __OTP_H__ +#define __OTP_H__ + +#define OTP_MAX_SEED_LEN 16 +#define OTP_MAX_WORD_LEN 4 +#define OTP_WORDS_NUMBER 6 + +#define OTP_HASH_SIZE 8 + +struct otp_state { + int algo; + int seq; + unsigned char hash[OTP_HASH_SIZE]; + char seed[OTP_MAX_SEED_LEN + 1]; +}; + +#include "otp-hash.h" +#include "otp-dictionary.h" +#include "otp-parity.h" +#include "otp-parse.h" + +#endif /* __OTP_H__ */ diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/Makefile.am dovecot/src/Makefile.am --- dovecot.vanilla/src/Makefile.am 2006-06-23 13:42:22.126507472 +0400 +++ dovecot/src/Makefile.am 2006-06-23 13:44:31.277873480 +0400 @@ -11,6 +11,7 @@ SUBDIRS = \ lib-dict \ lib-sql \ lib-ntlm \ + lib-otp \ lib-settings \ lib-charset \ lib-mail \
Add OTP and SKEY password schemes. SKEY is the same as OTP but uses MD4 algorithm always. 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:42:22.122508080 +0400 +++ dovecot/src/auth/Makefile.am 2006-06-23 13:44:31.340863904 +0400 @@ -9,6 +9,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib-sql \ -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-ntlm \ + -I$(top_srcdir)/src/lib-otp \ -DAUTH_MODULE_DIR=\""$(moduledir)/auth"\" \ -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ $(AUTH_CFLAGS) @@ -21,12 +22,14 @@ libpassword_a_SOURCES = \ password-scheme-md5crypt.c \ password-scheme-cram-md5.c \ password-scheme-ntlm.c \ + password-scheme-otp.c \ password-scheme-rpa.c dovecot_auth_LDADD = \ libpassword.a \ ../lib-settings/libsettings.a \ ../lib-ntlm/libntlm.a \ + ../lib-otp/libotp.a \ ../lib-sql/libsql.a \ ../lib/liblib.a \ $(AUTH_LIBS) \ diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/passdb.c dovecot/src/auth/passdb.c --- dovecot.vanilla/src/auth/passdb.c 2006-06-23 13:42:22.124507776 +0400 +++ dovecot/src/auth/passdb.c 2006-06-23 13:44:31.340863904 +0400 @@ -67,6 +67,10 @@ passdb_credentials_to_str(enum passdb_cr return "LANMAN"; case PASSDB_CREDENTIALS_NTLM: return "NTLM"; + case PASSDB_CREDENTIALS_OTP: + return "OTP"; + case PASSDB_CREDENTIALS_SKEY: + return "SKEY"; case PASSDB_CREDENTIALS_RPA: return "RPA"; } diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/passdb.h dovecot/src/auth/passdb.h --- dovecot.vanilla/src/auth/passdb.h 2006-06-23 13:42:22.124507776 +0400 +++ dovecot/src/auth/passdb.h 2006-06-23 13:44:31.340863904 +0400 @@ -15,6 +15,8 @@ enum passdb_credentials { PASSDB_CREDENTIALS_DIGEST_MD5, PASSDB_CREDENTIALS_LANMAN, PASSDB_CREDENTIALS_NTLM, + PASSDB_CREDENTIALS_OTP, + PASSDB_CREDENTIALS_SKEY, PASSDB_CREDENTIALS_RPA }; diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/password-scheme.c dovecot/src/auth/password-scheme.c --- dovecot.vanilla/src/auth/password-scheme.c 2006-06-23 13:42:22.125507624 +0400 +++ dovecot/src/auth/password-scheme.c 2006-06-23 13:44:31.340863904 +0400 @@ -10,6 +10,7 @@ #include "mycrypt.h" #include "randgen.h" #include "sha1.h" +#include "otp.h" #include "str.h" #include "password-scheme.h" @@ -450,6 +451,25 @@ static const char *ntlm_generate(const c return password_generate_ntlm(plaintext); } +static bool otp_verify(const char *plaintext, const char *password, + const char *user __attr_unused__) +{ + return strcasecmp(password, + password_generate_otp(plaintext, password, -1)) == 0; +} + +static const char *otp_generate(const char *plaintext, + const char *user __attr_unused__) +{ + return password_generate_otp(plaintext, NULL, OTP_HASH_SHA1); +} + +static const char *skey_generate(const char *plaintext, + const char *user __attr_unused__) +{ + return password_generate_otp(plaintext, NULL, OTP_HASH_MD4); +} + static bool rpa_verify(const char *plaintext, const char *password, const char *user __attr_unused__) { @@ -478,6 +498,8 @@ static const struct password_scheme defa { "LDAP-MD5", ldap_md5_verify, ldap_md5_generate }, { "LANMAN", lm_verify, lm_generate }, { "NTLM", ntlm_verify, ntlm_generate }, + { "OTP", otp_verify, otp_generate }, + { "SKEY", otp_verify, skey_generate }, { "RPA", rpa_verify, rpa_generate }, { NULL, NULL, NULL } }; diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/password-scheme.h dovecot/src/auth/password-scheme.h --- dovecot.vanilla/src/auth/password-scheme.h 2006-06-23 13:42:22.125507624 +0400 +++ dovecot/src/auth/password-scheme.h 2006-06-23 13:44:31.340863904 +0400 @@ -32,6 +32,7 @@ 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_otp(const char *pw, const char *state, int algo); const char *password_generate_rpa(const char *pw); #endif diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/password-scheme-otp.c dovecot/src/auth/password-scheme-otp.c --- dovecot.vanilla/src/auth/password-scheme-otp.c 1970-01-01 03:00:00.000000000 +0300 +++ dovecot/src/auth/password-scheme-otp.c 2006-06-23 13:44:31.341863752 +0400 @@ -0,0 +1,39 @@ +/* + * OTP password scheme. + * + * Copyright (c) 2006 Andrey Panin <pazke@donpac.ru> + * + * This software is released under the MIT license. + */ + +#include "lib.h" +#include "hex-binary.h" +#include "password-scheme.h" +#include "randgen.h" +#include "otp.h" + +const char *password_generate_otp(const char *pw, const char *data, int algo) +{ + struct otp_state state; + + if (data) { + if (otp_parse_dbentry(data, &state)) { + i_warning("Invalid OTP data in passdb"); + return ""; + } + } else { + /* Generate new OTP credentials from plaintext */ + unsigned char random_data[OTP_MAX_SEED_LEN / 2]; + + random_fill(random_data, sizeof(random_data)); + strncpy(state.seed, binary_to_hex(random_data, + OTP_MAX_SEED_LEN / 2), sizeof(state.seed)); + + state.seq = 1024; + state.algo = algo; + } + + otp_hash(state.algo, state.seed, pw, state.seq, state.hash); + + return otp_print_dbentry(&state); +} diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/util/Makefile.am dovecot/src/util/Makefile.am --- dovecot.vanilla/src/util/Makefile.am 2006-06-23 13:42:22.155503064 +0400 +++ dovecot/src/util/Makefile.am 2006-06-23 13:44:31.341863752 +0400 @@ -22,6 +22,7 @@ gdbhelper_SOURCES = \ dovecotpw_LDADD = \ ../auth/libpassword.a \ ../lib-ntlm/libntlm.a \ + ../lib-otp/libotp.a \ ../lib/liblib.a \ $(AUTH_LIBS) \ $(RAND_LIBS) \
Add auth_request_set_credentials() function. diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/auth-request.c dovecot/src/auth/auth-request.c --- dovecot.vanilla/src/auth/auth-request.c 2006-06-23 13:42:22.123507928 +0400 +++ dovecot/src/auth/auth-request.c 2006-06-23 13:44:31.427850680 +0400 @@ -508,6 +508,37 @@ void auth_request_lookup_credentials(str } } +void auth_request_set_credentials(struct auth_request *request, + enum passdb_credentials credentials, + const char *data, + set_credentials_callback_t *callback) +{ + struct passdb_module *passdb = request->passdb->passdb; + const char *cache_key, *new_credentials; + + i_assert(credentials == PASSDB_CREDENTIALS_OTP); + + cache_key = passdb_cache == NULL ? NULL : passdb->cache_key; + if (cache_key != NULL) { + auth_cache_remove(passdb_cache, request, cache_key); + } + + request->private_callback.set_credentials = callback; + + new_credentials = t_strconcat("{", + passdb_credentials_to_str(credentials), "}", data, NULL); + + if (passdb->blocking) { + passdb_blocking_set_credentials(request, new_credentials); + } else if (passdb->iface.set_credentials != NULL) { + passdb->iface.set_credentials(request, new_credentials, + callback); + } else { + /* this passdb doesn't support credentials update */ + callback(PASSDB_RESULT_INTERNAL_FAILURE, request); + } +} + void auth_request_userdb_callback(struct auth_stream_reply *reply, struct auth_request *request) { diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/auth-request.h dovecot/src/auth/auth-request.h --- dovecot.vanilla/src/auth/auth-request.h 2006-06-23 13:42:22.123507928 +0400 +++ dovecot/src/auth/auth-request.h 2006-06-23 13:44:31.427850680 +0400 @@ -59,6 +59,7 @@ struct auth_request { union { verify_plain_callback_t *verify_plain; lookup_credentials_callback_t *lookup_credentials; + set_credentials_callback_t *set_credentials; userdb_callback_t *userdb; } private_callback; enum passdb_credentials credentials; @@ -149,6 +150,10 @@ void auth_request_verify_plain_callback( void auth_request_lookup_credentials_callback(enum passdb_result result, const char *credentials, struct auth_request *request); +void auth_request_set_credentials(struct auth_request *request, + enum passdb_credentials credentials, + const char *data, + set_credentials_callback_t *callback); void auth_request_userdb_callback(struct auth_stream_reply *reply, struct auth_request *request); diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/passdb.h dovecot/src/auth/passdb.h --- dovecot.vanilla/src/auth/passdb.h 2006-06-23 13:44:31.413852808 +0400 +++ dovecot/src/auth/passdb.h 2006-06-23 13:44:31.427850680 +0400 @@ -37,6 +37,8 @@ typedef void verify_plain_callback_t(enu typedef void lookup_credentials_callback_t(enum passdb_result result, const char *password, struct auth_request *request); +typedef void set_credentials_callback_t(enum passdb_result result, + struct auth_request *request); struct passdb_module_interface { const char *name; @@ -54,6 +56,11 @@ struct passdb_module_interface { auth_request->credentials. */ void (*lookup_credentials)(struct auth_request *request, lookup_credentials_callback_t *callback); + + /* Update credentials */ + int (*set_credentials)(struct auth_request *request, + const char *new_credentials, + set_credentials_callback_t *callback); }; struct passdb_module {
Add set_credentials method to sql passdb. diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/db-sql.c dovecot/src/auth/db-sql.c --- dovecot.vanilla/src/auth/db-sql.c 2006-06-23 13:42:22.123507928 +0400 +++ dovecot/src/auth/db-sql.c 2006-06-23 13:44:31.480842624 +0400 @@ -18,6 +18,7 @@ static struct setting_def setting_defs[] DEF(SET_STR, connect), DEF(SET_STR, password_query), DEF(SET_STR, user_query), + DEF(SET_STR, update_query), DEF(SET_STR, default_pass_scheme), { 0, NULL, 0 } @@ -28,6 +29,7 @@ struct sql_settings default_sql_settings MEMBER(connect) NULL, MEMBER(password_query) "SELECT password FROM users WHERE userid = '%u'", MEMBER(user_query) "SELECT home, uid, gid FROM users WHERE userid = '%u'", + MEMBER(update_query) "UPDATE users SET password = '%c' WHERE userid = '%u'", MEMBER(default_pass_scheme) "PLAIN-MD5" }; diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/db-sql.h dovecot/src/auth/db-sql.h --- dovecot.vanilla/src/auth/db-sql.h 2006-06-23 13:42:22.123507928 +0400 +++ dovecot/src/auth/db-sql.h 2006-06-23 13:44:31.481842472 +0400 @@ -8,6 +8,7 @@ struct sql_settings { const char *connect; const char *password_query; const char *user_query; + const char *update_query; const char *default_pass_scheme; }; diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/passdb-sql.c dovecot/src/auth/passdb-sql.c --- dovecot.vanilla/src/auth/passdb-sql.c 2006-06-23 13:42:22.124507776 +0400 +++ dovecot/src/auth/passdb-sql.c 2006-06-23 13:44:31.481842472 +0400 @@ -6,7 +6,9 @@ #include "str.h" #include "strescape.h" +#include "buffer.h" #include "var-expand.h" +#include "safe-memset.h" #include "password-scheme.h" #include "auth-cache.h" #include "db-sql.h" @@ -26,6 +28,7 @@ struct passdb_sql_request { union { verify_plain_callback_t *verify_plain; lookup_credentials_callback_t *lookup_credentials; + set_credentials_callback_t *set_credentials; } callback; }; @@ -175,6 +178,57 @@ static void sql_lookup_credentials(struc sql_lookup_pass(sql_request); } +static int sql_set_credentials(struct auth_request *request, + const char *new_credentials, + set_credentials_callback_t *callback) +{ + struct sql_passdb_module *module = + (struct sql_passdb_module *) request->passdb->passdb; + struct sql_transaction_context *transaction; + const struct var_expand_table *tab; + struct var_expand_table tmp; + const char *error; + string_t *query; + buffer_t *buf; + int ret; + + t_push(); + + buf = buffer_create_dynamic(unsafe_data_stack_pool, 128); + + tmp.key = 'c'; + tmp.value = new_credentials; + buffer_append(buf, &tmp, sizeof(tmp)); + + tab = auth_request_get_var_expand_table(request, passdb_sql_escape); + do { + buffer_append(buf, tab++, sizeof(*tab)); + } while (tab->key != '\0'); + + tab = buffer_free_without_data(buf); + + query = t_str_new(512); + var_expand(query, module->conn->set.update_query, tab); + + transaction = sql_transaction_begin(module->conn->db); + + sql_update(transaction, str_c(query)); + + ret = sql_transaction_commit_s(&transaction, &error); + + callback(ret == 0 ? PASSDB_RESULT_OK : PASSDB_RESULT_INTERNAL_FAILURE, + request); + + if (ret < 0) + i_info("SQL error: %s", error); + + safe_memset(str_c_modifyable(query), 0, str_len(query)); + + t_pop(); + + return 0; +} + static struct passdb_module * passdb_sql_preinit(struct auth_passdb *auth_passdb, const char *args) { @@ -221,7 +275,8 @@ struct passdb_module_interface passdb_sq passdb_sql_deinit, sql_verify_plain, - sql_lookup_credentials + sql_lookup_credentials, + sql_set_credentials, }; #endif
Add set_credentials support to blocking passdb support code. diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/auth-worker-client.c dovecot/src/auth/auth-worker-client.c --- dovecot.vanilla/src/auth/auth-worker-client.c 2006-06-23 13:42:22.123507928 +0400 +++ dovecot/src/auth/auth-worker-client.c 2006-06-23 13:44:31.543833048 +0400 @@ -228,6 +228,72 @@ auth_worker_handle_passl(struct auth_wor } static void +set_credentials_callback(enum passdb_result result, + struct auth_request *request) +{ + struct auth_worker_client *client = request->context; + + string_t *str; + + str = t_str_new(64); + str_printfa(str, "%u\t", request->id); + + if (result != PASSDB_RESULT_OK) + str_printfa(str, "FAIL\t%d\t", result); + else { + str_printfa(str, "OK\t%s\t", request->user); + } + str_append_c(str, '\n'); + o_stream_send(client->output, str_data(str), str_len(str)); + + auth_request_unref(&request); + auth_worker_client_check_throttle(client); + auth_worker_client_unref(&client); +} + +static void +auth_worker_handle_passs(struct auth_worker_client *client, + unsigned int id, const char *args) +{ + struct auth_request *auth_request; + unsigned int passdb_id; + const char *data; + + passdb_id = atoi(t_strcut(args, '\t')); + args = strchr(args, '\t'); + if (args == NULL) { + i_error("BUG: Auth worker server sent us invalid PASSS"); + return; + } + args++; + + data = t_strcut(args, '\t'); + args = strchr(args, '\t'); + if (args != NULL) args++; + + auth_request = worker_auth_request_new(client, id, args); + + if (auth_request->user == NULL || auth_request->service == NULL) { + i_error("BUG: PASSS had missing parameters"); + auth_request_unref(&auth_request); + return; + } + + while (auth_request->passdb->id != passdb_id) { + auth_request->passdb = auth_request->passdb->next; + if (auth_request->passdb == NULL) { + i_error("BUG: PASSS had invalid passdb ID"); + auth_request_unref(&auth_request); + return; + } + } + + auth_request->passdb->passdb->iface. + set_credentials(auth_request, + data, set_credentials_callback); +} + +static void lookup_user_callback(struct auth_stream_reply *reply, struct auth_request *auth_request) { @@ -297,6 +363,8 @@ auth_worker_handle_line(struct auth_work auth_worker_handle_passv(client, id, line + 6); else if (strncmp(line, "PASSL\t", 6) == 0) auth_worker_handle_passl(client, id, line + 6); + else if (strncmp(line, "PASSS\t", 6) == 0) + auth_worker_handle_passs(client, id, line + 6); else if (strncmp(line, "USER\t", 5) == 0) auth_worker_handle_user(client, id, line + 5); diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/passdb-blocking.c dovecot/src/auth/passdb-blocking.c --- dovecot.vanilla/src/auth/passdb-blocking.c 2006-06-23 13:42:22.124507776 +0400 +++ dovecot/src/auth/passdb-blocking.c 2006-06-23 13:44:31.543833048 +0400 @@ -157,3 +157,24 @@ void passdb_blocking_lookup_credentials( auth_worker_call(request, str_c(str), lookup_credentials_callback); } + +static void +set_credentials_callback(struct auth_request *request, const char *reply) +{ + enum passdb_result result = check_failure(request, &reply); + + request->private_callback.set_credentials(result, request); +} + +void passdb_blocking_set_credentials(struct auth_request *request, + const char *new_credentials) +{ + string_t *str; + + str = t_str_new(64); + str_printfa(str, "PASSS\t%u\t%s\t", + request->passdb->id, new_credentials); + auth_request_export(request, str); + + auth_worker_call(request, str_c(str), set_credentials_callback); +} diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/passdb-blocking.h dovecot/src/auth/passdb-blocking.h --- dovecot.vanilla/src/auth/passdb-blocking.h 2006-06-23 13:42:22.124507776 +0400 +++ dovecot/src/auth/passdb-blocking.h 2006-06-23 13:44:31.544832896 +0400 @@ -3,5 +3,7 @@ void passdb_blocking_verify_plain(struct auth_request *request); void passdb_blocking_lookup_credentials(struct auth_request *request); +void passdb_blocking_set_credentials(struct auth_request *request, + const char *new_credentials); #endif
Add passdb_need_set_credentials flag into mech_module structure to mark authentication mechanisms which need credentials update to work properly. diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/auth.c dovecot/src/auth/auth.c --- dovecot.vanilla/src/auth/auth.c 2006-06-23 13:42:22.123507928 +0400 +++ dovecot/src/auth/auth.c 2006-06-23 13:44:31.606823472 +0400 @@ -143,6 +143,17 @@ static bool auth_passdb_list_have_creden return FALSE; } +static int auth_passdb_list_have_set_credentials(struct auth *auth) +{ + struct auth_passdb *passdb; + + for (passdb = auth->passdbs; passdb != NULL; passdb = passdb->next) { + if (passdb->passdb->iface.set_credentials != NULL) + return TRUE; + } + return FALSE; +} + static void auth_mech_list_verify_passdb(struct auth *auth) { struct mech_module_list *list; @@ -154,6 +165,9 @@ static void auth_mech_list_verify_passdb if (list->module.passdb_need_credentials && !auth_passdb_list_have_credentials(auth)) break; + if (list->module.passdb_need_set_credentials && + !auth_passdb_list_have_set_credentials(auth)) + break; } if (list != NULL) { diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/mech-anonymous.c dovecot/src/auth/mech-anonymous.c --- dovecot.vanilla/src/auth/mech-anonymous.c 2006-06-23 13:42:22.123507928 +0400 +++ dovecot/src/auth/mech-anonymous.c 2006-06-23 13:44:31.606823472 +0400 @@ -41,6 +41,7 @@ struct mech_module mech_anonymous = { MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) FALSE, + MEMBER(passdb_need_set_credentials) FALSE, mech_anonymous_auth_new, mech_generic_auth_initial, diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/mech-apop.c dovecot/src/auth/mech-apop.c --- dovecot.vanilla/src/auth/mech-apop.c 2006-06-23 13:42:22.123507928 +0400 +++ dovecot/src/auth/mech-apop.c 2006-06-23 13:44:31.607823320 +0400 @@ -156,6 +156,7 @@ const struct mech_module mech_apop = { MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) TRUE, + MEMBER(passdb_need_set_credentials) FALSE, mech_apop_auth_new, mech_apop_auth_initial, diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/mech-cram-md5.c dovecot/src/auth/mech-cram-md5.c --- dovecot.vanilla/src/auth/mech-cram-md5.c 2006-06-23 13:42:22.123507928 +0400 +++ dovecot/src/auth/mech-cram-md5.c 2006-06-23 13:44:31.607823320 +0400 @@ -188,6 +188,7 @@ struct mech_module mech_cram_md5 = { MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) TRUE, + MEMBER(passdb_need_set_credentials) FALSE, mech_cram_md5_auth_new, mech_cram_md5_auth_initial, diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/mech-digest-md5.c dovecot/src/auth/mech-digest-md5.c --- dovecot.vanilla/src/auth/mech-digest-md5.c 2006-06-23 13:42:22.123507928 +0400 +++ dovecot/src/auth/mech-digest-md5.c 2006-06-23 13:44:31.607823320 +0400 @@ -618,6 +618,7 @@ struct mech_module mech_digest_md5 = { MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) TRUE, + MEMBER(passdb_need_set_credentials) FALSE, mech_digest_md5_auth_new, mech_digest_md5_auth_initial, diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/mech-gssapi.c dovecot/src/auth/mech-gssapi.c --- dovecot.vanilla/src/auth/mech-gssapi.c 2006-06-23 13:42:22.124507776 +0400 +++ dovecot/src/auth/mech-gssapi.c 2006-06-23 13:44:31.607823320 +0400 @@ -393,6 +393,7 @@ const struct mech_module mech_gssapi = { MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) FALSE, + MEMBER(passdb_need_set_credentials) FALSE, mech_gssapi_auth_new, mech_gssapi_auth_initial, diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/mech.h dovecot/src/auth/mech.h --- dovecot.vanilla/src/auth/mech.h 2006-06-23 13:42:22.124507776 +0400 +++ dovecot/src/auth/mech.h 2006-06-23 13:44:31.608823168 +0400 @@ -26,6 +26,7 @@ struct mech_module { enum mech_security_flags flags; unsigned int passdb_need_plain:1; unsigned int passdb_need_credentials:1; + unsigned int passdb_need_set_credentials:1; struct auth_request *(*auth_new)(void); void (*auth_initial)(struct auth_request *request, diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/mech-login.c dovecot/src/auth/mech-login.c --- dovecot.vanilla/src/auth/mech-login.c 2006-06-23 13:42:22.124507776 +0400 +++ dovecot/src/auth/mech-login.c 2006-06-23 13:44:31.608823168 +0400 @@ -84,6 +84,7 @@ const struct mech_module mech_login = { MEMBER(passdb_need_plain) TRUE, MEMBER(passdb_need_credentials) FALSE, + MEMBER(passdb_need_set_credentials) FALSE, mech_login_auth_new, mech_login_auth_initial, diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/mech-ntlm.c dovecot/src/auth/mech-ntlm.c --- dovecot.vanilla/src/auth/mech-ntlm.c 2006-06-23 13:42:22.124507776 +0400 +++ dovecot/src/auth/mech-ntlm.c 2006-06-23 13:44:31.608823168 +0400 @@ -262,6 +262,7 @@ const struct mech_module mech_ntlm = { MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) TRUE, + MEMBER(passdb_need_set_credentials) FALSE, mech_ntlm_auth_new, mech_generic_auth_initial, diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/mech-plain.c dovecot/src/auth/mech-plain.c --- dovecot.vanilla/src/auth/mech-plain.c 2006-06-23 13:42:22.124507776 +0400 +++ dovecot/src/auth/mech-plain.c 2006-06-23 13:44:31.608823168 +0400 @@ -91,6 +91,7 @@ struct mech_module mech_plain = { MEMBER(passdb_need_plain) TRUE, MEMBER(passdb_need_credentials) FALSE, + MEMBER(passdb_need_set_credentials) FALSE, mech_plain_auth_new, mech_generic_auth_initial, diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/mech-rpa.c dovecot/src/auth/mech-rpa.c --- dovecot.vanilla/src/auth/mech-rpa.c 2006-06-23 13:42:22.124507776 +0400 +++ dovecot/src/auth/mech-rpa.c 2006-06-23 13:44:31.608823168 +0400 @@ -601,6 +601,7 @@ const struct mech_module mech_rpa = { MEMBER(passdb_need_plain) FALSE, MEMBER(passdb_need_credentials) TRUE, + MEMBER(passdb_need_set_credentials) FALSE, mech_rpa_auth_new, mech_generic_auth_initial,
Add common code shared by both OTP and SKEY authentication mechanism. otp_try_lock() and otp_unlock() functions are used to implement race attack protection which is required by RFC 2289. 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.401854632 +0400 +++ dovecot/src/auth/Makefile.am 2006-06-23 13:44:31.711807512 +0400 @@ -62,6 +62,7 @@ dovecot_auth_SOURCES = \ mech-gssapi.c \ mech-rpa.c \ mech-apop.c \ + otp-skey-common.c \ passdb.c \ passdb-blocking.c \ passdb-bsdauth.c \ @@ -104,6 +105,7 @@ noinst_HEADERS = \ common.h \ mech.h \ mycrypt.h \ + otp-skey-common.h \ passdb.h \ passdb-blocking.h \ passdb-cache.h \ diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/otp-skey-common.c dovecot/src/auth/otp-skey-common.c --- dovecot.vanilla/src/auth/otp-skey-common.c 1970-01-01 03:00:00.000000000 +0300 +++ dovecot/src/auth/otp-skey-common.c 2006-06-23 13:44:31.711807512 +0400 @@ -0,0 +1,64 @@ +/* + * Common code for OTP and SKEY authentication mechanisms. + * + * Copyright (c) 2006 Andrey Panin <pazke@donpac.ru> + * + * This software is released under the MIT license. + */ + +#include "common.h" +#include "hash.h" +#include "mech.h" + +#include "otp.h" +#include "otp-skey-common.h" + +static struct hash_table *otp_lock_table; + +void otp_lock_init(void) +{ + if (otp_lock_table != NULL) + return; + + otp_lock_table = hash_create(system_pool, system_pool, + 128, strcase_hash, + (hash_cmp_callback_t *)strcasecmp); +} + +int otp_try_lock(struct auth_request *auth_request) +{ + if (hash_lookup(otp_lock_table, auth_request->user)) + return FALSE; + + hash_insert(otp_lock_table, auth_request->user, auth_request); + + return TRUE; +} + +void otp_unlock(struct auth_request *auth_request) +{ + struct otp_auth_request *request = + (struct otp_auth_request *)auth_request; + + if (request->lock == FALSE) + return; + + hash_remove(otp_lock_table, auth_request->user); + request->lock = FALSE; +} + +void otp_set_credentials_callback(enum passdb_result result, + struct auth_request *auth_request) +{ + switch (result) { + case PASSDB_RESULT_OK: + auth_request_success(auth_request, NULL, 0); + break; + default: + auth_request_internal_failure(auth_request); + otp_unlock(auth_request); + break; + } + + otp_unlock(auth_request); +} diff -urdpNX /usr/share/dontdiff -x Makefile dovecot.vanilla/src/auth/otp-skey-common.h dovecot/src/auth/otp-skey-common.h --- dovecot.vanilla/src/auth/otp-skey-common.h 1970-01-01 03:00:00.000000000 +0300 +++ dovecot/src/auth/otp-skey-common.h 2006-06-23 13:44:31.711807512 +0400 @@ -0,0 +1,21 @@ +#ifndef __OTP_SKEY_COMMON_H__ +#define __OTP_SKEY_COMMON_H__ + +struct otp_auth_request { + struct auth_request auth_request; + + pool_t pool; + + int lock; + + struct otp_state state; +}; + +void otp_lock_init(void); +int otp_try_lock(struct auth_request *auth_request); +void otp_unlock(struct auth_request *auth_request); + +void otp_set_credentials_callback(enum passdb_result result, + struct auth_request *auth_request); + +#endif /* __OTP_SKEY_COMMON_H__ */
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 +};
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 +};
On 183, 07 02, 2006 at 02:25:43PM +0300, Timo Sirainen wrote:
Yes, it should. Cut and paste error :(
-- Andrey Panin | Linux and UNIX system administrator pazke@donpac.ru | PGP key: wwwkeys.pgp.net
On Mon, 2006-06-26 at 16:58 +0400, Andrey Panin wrote:
I haven't looked at the patches too closely yet, but how about using the existing %w instead of adding a new %c?
Otherwise looks good. Well, except PASSS could be changed to something else :) SETCRED maybe.
On 177, 06 26, 2006 at 04:26:23PM +0300, Timo Sirainen wrote:
Good idea, but needs documentation update since %w is defined as "plaintext password from plaintext authentication mechanism" now.
Otherwise looks good. Well, except PASSS could be changed to something else :) SETCRED maybe.
As you wish :) I'll send an updated patches soon.
-- Andrey Panin | Linux and UNIX system administrator pazke@donpac.ru | PGP key: wwwkeys.pgp.net
On 178, 06 27, 2006 at 11:07:51 +0400, Andrey Panin wrote:
Updated patches attached, s/%c/%w/ and s/PASSS/SETCRED/
-- Andrey Panin | Linux and UNIX system administrator pazke@donpac.ru | PGP key: wwwkeys.pgp.net
participants (2)
-
Andrey Panin
-
Timo Sirainen