Andrey Panin
2004-Jul-01 10:30 UTC
[Dovecot] [PATCH, RFC] add APOP authentication mechanism
Hello all, this patch add APOP authentication mechanism to dovecot 1.0-test23. Please take a look. Best regards. -- Andrey Panin | Linux and UNIX system administrator pazke at donpac.ru | PGP key: wwwkeys.pgp.net -------------- next part -------------- diff -udrpN -X /usr/share/dontdiff -x Makefile dovecot-1.0-test23.vanilla/src/auth/Makefile.am dovecot-1.0-test23/src/auth/Makefile.am --- dovecot-1.0-test23.vanilla/src/auth/Makefile.am 2004-06-24 12:14:13.000000000 +0400 +++ dovecot-1.0-test23/src/auth/Makefile.am 2004-07-01 11:38:32.000000000 +0400 @@ -31,6 +31,7 @@ dovecot_auth_SOURCES = \ mech-plain.c \ mech-cram-md5.c \ mech-digest-md5.c \ + mech-apop.c \ mycrypt.c \ passdb.c \ passdb-bsdauth.c \ diff -udrpN -X /usr/share/dontdiff -x Makefile dovecot-1.0-test23.vanilla/src/auth/mech-apop.c dovecot-1.0-test23/src/auth/mech-apop.c --- dovecot-1.0-test23.vanilla/src/auth/mech-apop.c 1970-01-01 03:00:00.000000000 +0300 +++ dovecot-1.0-test23/src/auth/mech-apop.c 2004-07-01 11:38:32.000000000 +0400 @@ -0,0 +1,183 @@ +/* + * APOP (RFC-1460) 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 "safe-memset.h" +#include "mech.h" +#include "passdb.h" +#include "md5.h" +#include "buffer.h" +#include "hex-binary.h" + +#include <ctype.h> + +struct apop_auth_request { + struct auth_request auth_request; + + pool_t pool; + + /* requested: */ + char *challenge; + + /* received: */ + char *username; + char *digest; + unsigned long maxbuf; +}; + +static void +apop_credentials_callback(const char *credentials, + struct auth_request *auth_request) +{ + struct apop_auth_request *auth + (struct apop_auth_request *)auth_request; + buffer_t *digest_buf; + unsigned char remote_digest[16]; + unsigned char digest[16]; + struct md5_context ctx; + + digest_buf = buffer_create_data(pool_datastack_create(), + remote_digest, sizeof(remote_digest)); + if (hex_to_binary(auth->digest, digest_buf) <= 0) { + if (verbose) + i_info("apop(%s): invalid characters in APOP digest", get_log_prefix(auth_request)); + mech_auth_finish(auth_request, NULL, 0, FALSE); + return; + } + + md5_init(&ctx); + md5_update(&ctx, auth->challenge, strlen(auth->challenge)); + md5_update(&ctx, credentials, strlen(credentials)); + md5_final(&ctx, digest); + + safe_memset((void *) credentials, 0, strlen(credentials)); + + mech_auth_finish(auth_request, NULL, 0, + memcmp(digest, remote_digest, 16) ? FALSE : TRUE); +} + +static int +mech_apop_auth_initial(struct auth_request *auth_request, + struct auth_client_request_new *request, + const unsigned char *data, + mech_callback_t *callback) +{ + struct apop_auth_request *auth + (struct apop_auth_request *)auth_request; + const unsigned char *tmp, *end, *username; + + auth_request->callback = callback; + + if (strcmp(auth_request->protocol, "POP3")) { + if (verbose) + i_info("apop(%s): wrong protocol %s", get_log_prefix(auth_request), + auth_request->protocol); + mech_auth_finish(auth_request, NULL, 0, FALSE); + return TRUE; + } + + if (!AUTH_CLIENT_REQUEST_HAVE_INITIAL_RESPONSE(request)) { + /* Should never happen */ + if (verbose) + i_info("apop(%s): no initial respone", get_log_prefix(auth_request)); + mech_auth_finish(auth_request, NULL, 0, FALSE); + return TRUE; + } + + tmp = data = data + request->initial_resp_idx; + end = data + request->data_size - request->initial_resp_idx; + + while (*tmp && (tmp < end)) + tmp++; + + if (tmp == end) { + /* Should never happen */ + if (verbose) + i_info("apop(%s): mailformed data", get_log_prefix(auth_request)); + mech_auth_finish(auth_request, NULL, 0, FALSE); + return TRUE; + } + tmp++; + + auth->challenge = p_strdup(auth->pool, data); + + username = tmp; + while (*tmp && !isspace(*tmp) && (tmp < end)) + tmp++; + + if (tmp == end) { + if (verbose) + i_info("apop(%s): mailformed response", get_log_prefix(auth_request)); + mech_auth_finish(auth_request, NULL, 0, FALSE); + return TRUE; + } + tmp++; + + auth->username = p_strndup(auth->pool, username, tmp - username - 1); + if (!mech_is_valid_username(auth->username)) { + if (verbose) + i_info("apop(%s): invalid username", get_log_prefix(auth_request)); + mech_auth_finish(auth_request, NULL, 0, FALSE); + return TRUE; + } + + auth_request->user = p_strdup(auth->pool, auth->username); + + if ((end - tmp) != 32) { + if (verbose) + i_info("apop(%s): wrong APOP digest length", get_log_prefix(auth_request)); + mech_auth_finish(auth_request, NULL, 0, FALSE); + return TRUE; + } + + auth->digest = p_strndup(auth->pool, tmp, 32); + + passdb->lookup_credentials(auth_request, PASSDB_CREDENTIALS_PLAINTEXT, + apop_credentials_callback); + + return TRUE; +} + +static void +mech_apop_auth_free(struct auth_request *auth_request) +{ + pool_unref(auth_request->pool); +} + +static struct auth_request *mech_apop_auth_new(void) +{ + struct apop_auth_request *auth; + pool_t pool; + + pool = pool_alloconly_create("apop_auth_request", 256); + auth = p_new(pool, struct apop_auth_request, 1); + auth->pool = pool; + + auth->auth_request.refcount = 1; + auth->auth_request.pool = pool; + auth->auth_request.auth_initial = mech_apop_auth_initial; + auth->auth_request.auth_continue = NULL; + auth->auth_request.auth_free = mech_apop_auth_free; + + return &auth->auth_request; +} + +const struct mech_module mech_apop = { + "APOP", + + MEMBER(plaintext) FALSE, + MEMBER(advertise) FALSE, + + MEMBER(passdb_need_plain) FALSE, + MEMBER(passdb_need_credentials) TRUE, + + mech_apop_auth_new, +}; diff -udrpN -X /usr/share/dontdiff -x Makefile dovecot-1.0-test23.vanilla/src/auth/mech.c dovecot-1.0-test23/src/auth/mech.c --- dovecot-1.0-test23.vanilla/src/auth/mech.c 2004-06-01 00:10:02.000000000 +0400 +++ dovecot-1.0-test23/src/auth/mech.c 2004-07-01 11:38:33.000000000 +0400 @@ -384,6 +384,7 @@ static void auth_failure_timeout(void *c } extern struct mech_module mech_plain; +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_anonymous; @@ -411,6 +412,8 @@ void mech_init(void) while (*mechanisms != NULL) { if (strcasecmp(*mechanisms, "PLAIN") == 0) mech_register_module(&mech_plain); + else if (strcasecmp(*mechanisms, "APOP") == 0) + mech_register_module(&mech_apop); else if (strcasecmp(*mechanisms, "CRAM-MD5") == 0) mech_register_module(&mech_cram_md5); else if (strcasecmp(*mechanisms, "DIGEST-MD5") == 0) @@ -471,6 +474,7 @@ void mech_deinit(void) timeout_remove(to_auth_failures); mech_unregister_module(&mech_plain); + mech_unregister_module(&mech_apop); mech_unregister_module(&mech_cram_md5); mech_unregister_module(&mech_digest_md5); mech_unregister_module(&mech_anonymous); diff -udrpN -X /usr/share/dontdiff -x Makefile dovecot-1.0-test23.vanilla/src/pop3-login/client-authenticate.c dovecot-1.0-test23/src/pop3-login/client-authenticate.c --- dovecot-1.0-test23.vanilla/src/pop3-login/client-authenticate.c 2004-06-24 12:14:14.000000000 +0400 +++ dovecot-1.0-test23/src/pop3-login/client-authenticate.c 2004-07-01 11:38:33.000000000 +0400 @@ -338,3 +338,49 @@ int cmd_auth(struct pop3_client *client, return TRUE; } + +int cmd_apop(struct pop3_client *client, const char *args) +{ + const char *error; + struct auth_request_info info; + string_t *apop_data; + + if (!client->apop_challenge) { + client_send_line(client, "-ERR Unknown command."); + return TRUE; + } + + /* APOP challenge \0 APOP responce */ + apop_data = t_str_new(128); + str_append(apop_data, client->apop_challenge); + str_append_c(apop_data, '\0'); + str_append(apop_data, args); + + memset(&info, 0, sizeof(info)); + info.mech = "APOP"; + info.protocol = "POP3"; + info.flags = client_get_auth_flags(client); + info.local_ip = client->common.local_ip; + info.remote_ip = client->common.ip; + info.initial_resp_data = str_data(apop_data); + info.initial_resp_size = str_len(apop_data); + + client_ref(client); + client->common.auth_request + auth_client_request_new(auth_client, &info, + login_callback, client, &error); + + if (client->common.auth_request != NULL) { + /* don't read any input from client until login is finished */ + if (client->common.io != NULL) { + io_remove(client->common.io); + client->common.io = NULL; + } + return TRUE; + } else { + client_send_line(client, + t_strconcat("-ERR Login failed: ", error, NULL)); + client_unref(client); + return TRUE; + } +} diff -udrpN -X /usr/share/dontdiff -x Makefile dovecot-1.0-test23.vanilla/src/pop3-login/client-authenticate.h dovecot-1.0-test23/src/pop3-login/client-authenticate.h --- dovecot-1.0-test23.vanilla/src/pop3-login/client-authenticate.h 2003-01-30 22:52:39.000000000 +0300 +++ dovecot-1.0-test23/src/pop3-login/client-authenticate.h 2004-07-01 11:38:33.000000000 +0400 @@ -5,5 +5,6 @@ int cmd_capa(struct pop3_client *client, int cmd_user(struct pop3_client *client, const char *args); int cmd_pass(struct pop3_client *client, const char *args); int cmd_auth(struct pop3_client *client, const char *args); +int cmd_apop(struct pop3_client *client, const char *args); #endif diff -udrpN -X /usr/share/dontdiff -x Makefile dovecot-1.0-test23.vanilla/src/pop3-login/client.c dovecot-1.0-test23/src/pop3-login/client.c --- dovecot-1.0-test23.vanilla/src/pop3-login/client.c 2004-05-31 22:04:47.000000000 +0400 +++ dovecot-1.0-test23/src/pop3-login/client.c 2004-07-01 13:57:40.000000000 +0400 @@ -13,6 +13,8 @@ #include "client-authenticate.h" #include "auth-client.h" #include "ssl-proxy.h" +#include "hostpid.h" +#include "imem.h" /* max. length of input command line (spec says 512) */ #define MAX_INBUF_SIZE 2048 @@ -122,6 +124,8 @@ static int client_command_execute(struct return cmd_pass(client, args); if (strcmp(cmd, "AUTH") == 0) return cmd_auth(client, args); + if (strcmp(cmd, "APOP") == 0) + return cmd_apop(client, args); if (strcmp(cmd, "STLS") == 0) return cmd_stls(client); if (strcmp(cmd, "QUIT") == 0) @@ -228,6 +232,15 @@ static void client_destroy_oldest(void) } } +static char *get_apop_challenge(void) +{ + if (auth_client_find_mech(auth_client, "APOP")) { + hostpid_init(); + return i_strdup_printf("<%s.%s@%s>", my_pid, dec2str(ioloop_time), my_hostname); + } else + return NULL; +} + struct client *client_create(int fd, int ssl, const struct ip_addr *local_ip, const struct ip_addr *ip) { @@ -265,7 +278,8 @@ struct client *client_create(int fd, int main_ref(); - client_send_line(client, "+OK " PACKAGE " ready."); + client->apop_challenge = get_apop_challenge(); + client_send_line(client, t_strconcat("+OK " PACKAGE " ready.", client->apop_challenge, NULL)); client_set_title(client); return &client->common; } @@ -318,6 +332,7 @@ int client_unref(struct pop3_client *cli i_stream_unref(client->input); o_stream_unref(client->output); + i_free(client->apop_challenge); i_free(client->common.virtual_user); i_free(client); diff -udrpN -X /usr/share/dontdiff -x Makefile dovecot-1.0-test23.vanilla/src/pop3-login/client.h dovecot-1.0-test23/src/pop3-login/client.h --- dovecot-1.0-test23.vanilla/src/pop3-login/client.h 2004-05-31 22:04:47.000000000 +0400 +++ dovecot-1.0-test23/src/pop3-login/client.h 2004-07-01 11:38:33.000000000 +0400 @@ -19,6 +19,8 @@ struct pop3_client { char *last_user; + char *apop_challenge; + unsigned int tls:1; unsigned int secured:1; unsigned int input_blocked:1; -------------- 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/20040701/fa9927fc/attachment-0001.bin>
Timo Sirainen
2004-Jul-01 13:00 UTC
[Dovecot] [PATCH, RFC] add APOP authentication mechanism
On Thu, 2004-07-01 at 13:30, Andrey Panin wrote:> this patch add APOP authentication mechanism to dovecot 1.0-test23. > Please take a look.Thanks, looks pretty good to me. I'll just change a few coding style issues. Also: + if (strcmp(auth_request->protocol, "POP3")) { + if (verbose) + i_info("apop(%s): wrong protocol %s", get_log_prefix(auth_request), + auth_request->protocol); + mech_auth_finish(auth_request, NULL, 0, FALSE); + return TRUE; + } I don't think there's really any point in checking this? Maybe someone really wants to use APOP with another protocol :) -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 189 bytes Desc: This is a digitally signed message part URL: <http://dovecot.org/pipermail/dovecot/attachments/20040701/815db02a/attachment-0001.bin>
Timo Sirainen
2004-Jul-01 14:39 UTC
[Dovecot] [PATCH, RFC] add APOP authentication mechanism
On 1.7.2004, at 16:26, Andrey Panin wrote:>> + if (strcmp(auth_request->protocol, "POP3")) { >> + if (verbose) >> + i_info("apop(%s): wrong protocol %s", >> get_log_prefix(auth_request), >> + auth_request->protocol); >> + mech_auth_finish(auth_request, NULL, 0, FALSE); >> + return TRUE; >> + } >> >> I don't think there's really any point in checking this? Maybe someone >> really wants to use APOP with another protocol :) > > This will be RFC violation isn't it ?(I'll send a public reply, the last question could need input from others) Well, it's not compatible with any RFC of course, but I don't think it would violate anything? I'd just rather like to avoid unnecessary code bloat. Well, except now that I thought about the logic instead of just the code, there are problems. APOP prevents replay attacks by sending different timestamp in greeting, but you're sending the greeting to dovecot-auth as user input to APOP authentication mechanism. So, a user could simply say "AUTH APOP" and send wanted timestamp as input. One way to fix this would be to disallow APOP mechanism to be used with AUTH command in pop3-login and require that the request was done with POP3 protocol. But I think that's pretty ugly kludge. It also requires that processes connecting to dovecot-auth are trusted, which is something I've tried to avoid as much as possible. So dovecot-auth doesn't really know if it's pop3-login which connects to it.. Better fix would be to get part of the timestamp from dovecot-auth itself. It could send a growing ID number as handshake which would be used as part of the timestamp, and APOP mechanism processing would check that timestamp really contains the ID. So timestamp would be <authid.unixtime at hostname>. That also makes the PID go away, which was another thing I would have suggested. POP3 RFC also says: It is conjectured that use of the APOP command provides origin identification and replay protection for a POP3 session. Accordingly, a POP3 server which implements both the PASS and APOP commands should not allow both methods of access for a given user; that is, for a given mailbox name, either the USER/PASS command sequence or the APOP command is allowed, but not both. But I don't know if there's any nice way to do it, except perhaps by defining APOP password scheme which would actually be plaintext but wouldn't work with anything but APOP authentication. Do any other servers prevent it either? -------------- next part -------------- A non-text attachment was scrubbed... Name: PGP.sig Type: application/pgp-signature Size: 186 bytes Desc: This is a digitally signed message part URL: <http://dovecot.org/pipermail/dovecot/attachments/20040701/c1dff077/attachment-0001.bin>
On 01.07.2004 13:30, Andrey Panin wrote:> Hello all, > > this patch add APOP authentication mechanism to dovecot 1.0-test23. > Please take a look. > > Best regards. >Hi! We do not accept patches to dovecot 1.0-test23, please consider upgrading. Also APOP is already supported by 2.2 Aki