]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added APOP authentication for POP3. Patch by Andrey Panin.
authorTimo Sirainen <tss@iki.fi>
Fri, 2 Jul 2004 22:03:36 +0000 (01:03 +0300)
committerTimo Sirainen <tss@iki.fi>
Fri, 2 Jul 2004 22:03:36 +0000 (01:03 +0300)
This required some changes in auth APIs.

--HG--
branch : HEAD

18 files changed:
AUTHORS
dovecot-example.conf
src/auth/Makefile.am
src/auth/auth-client-connection.c
src/auth/auth-client-connection.h
src/auth/auth-client-interface.h
src/auth/auth-master-connection.c
src/auth/mech.c
src/imap-login/client-authenticate.c
src/lib-auth/auth-client.c
src/lib-auth/auth-client.h
src/lib-auth/auth-server-connection.c
src/lib-auth/auth-server-connection.h
src/lib-auth/auth-server-request.c
src/pop3-login/client-authenticate.c
src/pop3-login/client-authenticate.h
src/pop3-login/client.c
src/pop3-login/client.h

diff --git a/AUTHORS b/AUTHORS
index 26e915059f75d1c364be4e6e1055248342f00cb0..910784687fff95e8a7dfd1638e7bf113486db7c3 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -2,12 +2,12 @@ Timo Sirainen <tss@iki.fi>
 
 Solar Designer <solar@openwall.com> (src/auth/userinfo-passwd|shadow|pam.c)
 
-Damian Ivereigh <damian@cisco.com> (src/lib-index/mail-tree-redblack.c)
-
 Alex Howansky <alex@wankwood.com> (src/auth/*-pgsql.c)
 
 Matthew Reimer <mreimer@vpop.net> (src/auth/*-mysql.c)
 
+Andrey Panin <pazke@donpac.ru> (src/auth/mech-apop.c)
+
 This product includes software developed by Computing Services
 at Carnegie Mellon University (http://www.cmu.edu/computing/).
 (src/lib/base64.c, src/lib/mkgmtime.c)
index a359fcf3b701753d0533529fdc7c278f0e042b89..ed5e43e05761fb699d46e3befc936a05162cc50b 100644 (file)
@@ -419,7 +419,7 @@ protocol pop3 {
 
 auth default {
   # Space separated list of wanted authentication mechanisms:
-  #   plain digest-md5 cram-md5 anonymous
+  #   plain digest-md5 cram-md5 apop anonymous
   mechanisms = plain
 
   # Where user database is kept:
index 515a25c4b3717886d895134aeb17477cca743105..1658bfe0aefcd198457c1ec8cb8363201890dd88 100644 (file)
@@ -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 \
index ead211fc3a4e7ce5be21f479a58a1903fbd507a2..9995831c5e41c9a574b9229c2f0ca5955f43f1de 100644 (file)
@@ -185,7 +185,10 @@ static void auth_client_input(void *context)
 struct auth_client_connection *
 auth_client_connection_create(struct auth_master_connection *master, int fd)
 {
+       static unsigned int connect_uid_counter = 0;
        struct auth_client_connection *conn;
+       struct auth_client_handshake_reply handshake_reply;
+
        pool_t pool;
 
        pool = pool_alloconly_create("Auth client", 4096);
@@ -193,6 +196,7 @@ auth_client_connection_create(struct auth_master_connection *master, int fd)
        conn->pool = pool;
        conn->master = master;
        conn->refcount = 1;
+       conn->connect_uid = ++connect_uid_counter;
 
        conn->fd = fd;
        conn->input = i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE,
@@ -207,9 +211,13 @@ auth_client_connection_create(struct auth_master_connection *master, int fd)
        conn->next = master->clients;
        master->clients = conn;
 
-       if (o_stream_send(conn->output, master->handshake_reply,
-                         sizeof(*master->handshake_reply) +
-                         master->handshake_reply->data_size) < 0) {
+       handshake_reply = *master->handshake_reply;
+       handshake_reply.connect_uid = conn->connect_uid;
+
+       if (o_stream_send(conn->output, &handshake_reply,
+                         sizeof(handshake_reply)) < 0 ||
+           o_stream_send(conn->output, master->handshake_reply + 1,
+                         handshake_reply.data_size) < 0) {
                auth_client_connection_destroy(conn);
                conn = NULL;
        }
index 4ca0534a2efce48e6a3c331a66d34ed722800721..b90b4bbd5aa3ee2d5df2f798fdcbf87009e05139 100644 (file)
@@ -18,6 +18,7 @@ struct auth_client_connection {
        struct hash_table *auth_requests;
 
        unsigned int pid;
+       unsigned int connect_uid;
 };
 
 struct auth_client_connection *
index bd074d639cb66451952d79a5ebec68b7de70cf08..be12ac47a813372e7f5a3825b807122ed66cfa05 100644 (file)
@@ -37,6 +37,7 @@ struct auth_client_handshake_mech_desc {
 /* Server -> Client */
 struct auth_client_handshake_reply {
        unsigned int server_pid; /* unique auth process identifier */
+       unsigned int connect_uid; /* unique connection identifier */
 
        uint32_t mech_count;
        uint32_t data_size;
index f56b4d0f688ca4a9544c7a157e11843af1988e60..f3b72691c35fcb5d74d6d80d23aed2dfed71a85e 100644 (file)
@@ -226,7 +226,7 @@ static void master_get_handshake_reply(struct auth_master_connection *master)
                mech_desc_offset += sizeof(mech_desc);
        }
 
-       reply.data_size = buffer_get_used_size(buf);
+       reply.data_size = buffer_get_used_size(buf) - sizeof(reply);
        memcpy(buffer_get_space_unsafe(buf, 0, sizeof(reply)),
               &reply, sizeof(reply));
 
index 8271fc4a67759f0211aaa014772fc49b63fab53c..5a1d175e32e023fb435af4561038a0e0890f64e9 100644 (file)
@@ -384,6 +384,7 @@ static void auth_failure_timeout(void *context __attr_unused__)
 }
 
 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);
index be652a13b7ff5012ab1635726a7357b373a4bd50..fe70e3716e4b610114c83ad71c7289ae31c06e27 100644 (file)
@@ -182,7 +182,7 @@ int cmd_login(struct imap_client *client, struct imap_arg *args)
        client_ref(client);
 
        client->common.auth_request =
-               auth_client_request_new(auth_client, &info,
+               auth_client_request_new(auth_client, NULL, &info,
                                        login_callback, client, &error);
        if (client->common.auth_request == NULL) {
                client_send_tagline(client, t_strconcat(
@@ -315,7 +315,7 @@ int cmd_authenticate(struct imap_client *client, struct imap_arg *args)
 
        client_ref(client);
        client->common.auth_request =
-               auth_client_request_new(auth_client, &info,
+               auth_client_request_new(auth_client, NULL, &info,
                                        authenticate_callback, client, &error);
        if (client->common.auth_request != NULL) {
                /* following input data will go to authentication */
index 0fbdf733248eec9eca2322a97c655c419e5689cb..73afa5e5f94453d00c0497d71d117314b1fc2a87 100644 (file)
@@ -87,6 +87,22 @@ auth_client_find_mech(struct auth_client *client, const char *name)
        return NULL;
 }
 
+int auth_client_reserve_connection(struct auth_client *client, const char *mech,
+                                  struct auth_connect_id *id_r)
+{
+       struct auth_server_connection *conn;
+       const char *error;
+
+       conn = auth_server_connection_find_mech(client, mech, &error);
+       if (conn == NULL)
+               return FALSE;
+
+       id_r->server_pid = conn->server_pid;
+       id_r->connect_uid = conn->connect_uid;
+
+       return TRUE;
+}
+
 int auth_client_is_connected(struct auth_client *client)
 {
        return !client->reconnect &&
index 7b359f2a63423802f3228fe47a1603a954ec4a8e..ce4b12d76bed9a627befd5a31780a6265ae3c627 100644 (file)
@@ -13,6 +13,11 @@ struct auth_mech_desc {
        unsigned int advertise:1;
 };
 
+struct auth_connect_id {
+       unsigned int server_pid;
+       unsigned int connect_uid;
+};
+
 struct auth_request_info {
        const char *mech;
        const char *protocol;
@@ -46,10 +51,17 @@ auth_client_get_available_mechs(struct auth_client *client,
 const struct auth_mech_desc *
 auth_client_find_mech(struct auth_client *client, const char *name);
 
+/* Reserve connection for specific mechanism. The id can be given to
+   auth_client_request_new() to force it to use the same connection, or fail.
+   This is currently useful only for APOP authentication. Returns TRUE if
+   successfull. */
+int auth_client_reserve_connection(struct auth_client *client, const char *mech,
+                                  struct auth_connect_id *id_r);
+
 /* Create a new authentication request. callback is called whenever something
-   happens for the request. */
+   happens for the request. id can be NULL. */
 struct auth_request *
-auth_client_request_new(struct auth_client *client,
+auth_client_request_new(struct auth_client *client, struct auth_connect_id *id,
                        const struct auth_request_info *request_info,
                        auth_request_callback_t *callback, void *context,
                        const char **error_r);
index b9cf6f39946d50bd911ea8bb9faec6133e3d2b99..eaff14146d961b17c22c0c4cbf52be65a5e8304f 100644 (file)
@@ -80,7 +80,8 @@ static void auth_handle_handshake(struct auth_server_connection *conn,
                        conn->has_plain_mech = TRUE;
        }
 
-       conn->pid = handshake->server_pid;
+       conn->server_pid = handshake->server_pid;
+       conn->connect_uid = handshake->connect_uid;
        conn->available_auth_mechs_count =
                buffer_get_used_size(buf) / sizeof(mech_desc);
        conn->available_auth_mechs = buffer_free_without_data(buf);
index fbfb17f5b7e2f4b1f2c72576c3c043ae2704b710..72623914d039d9d652dac0bebd0e1857a0124085 100644 (file)
@@ -37,7 +37,9 @@ struct auth_server_connection {
        struct istream *input;
        struct ostream *output;
 
-       unsigned int pid;
+       unsigned int server_pid;
+       unsigned int connect_uid;
+
        const struct auth_mech_desc *available_auth_mechs;
        unsigned int available_auth_mechs_count;
         struct auth_client_request_reply reply;
index 20dad41598d17bb5899baef890f70e9a8d9802b3..76cb8f16e3bbd33cd1cc9729e0b240e3a670aaab 100644 (file)
@@ -247,7 +247,7 @@ void auth_server_requests_remove_all(struct auth_server_connection *conn)
 }
 
 struct auth_request *
-auth_client_request_new(struct auth_client *client,
+auth_client_request_new(struct auth_client *client, struct auth_connect_id *id,
                        const struct auth_request_info *request_info,
                        auth_request_callback_t *callback, void *context,
                        const char **error_r)
@@ -255,8 +255,20 @@ auth_client_request_new(struct auth_client *client,
        struct auth_server_connection *conn;
        struct auth_request *request;
 
-       conn = auth_server_connection_find_mech(client, request_info->mech,
-                                               error_r);
+       if (id == NULL) {
+               conn = auth_server_connection_find_mech(client,
+                                                       request_info->mech,
+                                                       error_r);
+       } else {
+               *error_r = NULL;
+               conn = client->connections;
+               for (; conn != NULL; conn = conn->next) {
+                       if (conn->connect_uid == id->connect_uid &&
+                           conn->server_pid == id->server_pid)
+                               break;
+               }
+       }
+
        if (conn == NULL)
                return NULL;
 
@@ -324,5 +336,5 @@ unsigned int auth_client_request_get_id(struct auth_request *request)
 
 unsigned int auth_client_request_get_server_pid(struct auth_request *request)
 {
-       return request->conn->pid;
+       return request->conn->server_pid;
 }
index 3260b7dc9c4d464c0a4fc483a6461f1ef7ca12cf..eb41d69b50b95eed13b04b4d14652faaaee01cc2 100644 (file)
@@ -3,6 +3,7 @@
 #include "common.h"
 #include "base64.h"
 #include "buffer.h"
+#include "hex-binary.h"
 #include "ioloop.h"
 #include "istream.h"
 #include "ostream.h"
@@ -184,7 +185,7 @@ int cmd_pass(struct pop3_client *client, const char *args)
 
        client_ref(client);
        client->common.auth_request =
-               auth_client_request_new(auth_client, &info,
+               auth_client_request_new(auth_client, NULL, &info,
                                        login_callback, client, &error);
 
        if (client->common.auth_request != NULL) {
@@ -322,7 +323,7 @@ int cmd_auth(struct pop3_client *client, const char *args)
 
        client_ref(client);
        client->common.auth_request =
-               auth_client_request_new(auth_client, &info,
+               auth_client_request_new(auth_client, NULL, &info,
                                        authenticate_callback, client, &error);
        if (client->common.auth_request != NULL) {
                /* following input data will go to authentication */
@@ -338,3 +339,69 @@ int cmd_auth(struct pop3_client *client, const char *args)
 
        return TRUE;
 }
+
+int cmd_apop(struct pop3_client *client, const char *args)
+{
+       struct auth_request_info info;
+       const char *error, *p;
+       buffer_t *apop_data;
+
+       if (client->apop_challenge == NULL) {
+               client_send_line(client, "-ERR APOP not enabled.");
+               return TRUE;
+       }
+
+       /* <username> <md5 sum in hex> */
+       p = strchr(args, ' ');
+       if (p == NULL || strlen(p+1) != 32) {
+               client_send_line(client, "-ERR Invalid parameters.");
+               return TRUE;
+       }
+
+       /* APOP challenge \0 username \0 APOP response */
+       apop_data = buffer_create_dynamic(pool_datastack_create(),
+                                         128, (size_t)-1);
+       buffer_append(apop_data, client->apop_challenge,
+                     strlen(client->apop_challenge)+1);
+       buffer_append(apop_data, args, (size_t)(p-args));
+       buffer_append_c(apop_data, '\0');
+
+       if (hex_to_binary(p+1, apop_data) <= 0) {
+               client_send_line(client,
+                                "-ERR Invalid characters in MD5 response.");
+               return TRUE;
+       }
+
+       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 =
+               buffer_get_data(apop_data, &info.initial_resp_size);
+
+       client_ref(client);
+       client->common.auth_request =
+               auth_client_request_new(auth_client, &client->auth_id, &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;
+               }
+       } else if (error == NULL) {
+               /* the auth connection was lost. we have no choice
+                  but to fail the APOP logins completely since the
+                  challenge is auth connection-specific. disconnect. */
+               client_destroy(client, "APOP auth connection lost");
+               client_unref(client);
+       } else {
+               client_send_line(client,
+                       t_strconcat("-ERR Login failed: ", error, NULL));
+               client_unref(client);
+       }
+       return TRUE;
+}
index bc8e252c926e70af539906332244eab589848e10..0dfde7f63d90ffa4bee2ed12e4ec65b43d6946ff 100644 (file)
@@ -5,5 +5,6 @@ int cmd_capa(struct pop3_client *client, const char *args);
 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
index 74a7fa6ec16ee346ae04d3d44c13e75e2720763f..ae0e833d07fdd17db52a21cb98d26b2f28bfd707 100644 (file)
@@ -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 pop3_client *client, const char *cmd,
                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,19 @@ static void client_destroy_oldest(void)
        }
 }
 
+static char *get_apop_challenge(void)
+{
+       struct auth_connect_id id;
+
+       /* FIXME: breaks if we're not connected! */
+
+       if (!auth_client_reserve_connection(auth_client, "APOP", &id))
+               return NULL;
+
+       return i_strdup_printf("<%x.%x.%s@%s>", id.server_pid, id.connect_uid,
+                              dec2str(ioloop_time), my_hostname);
+}
+
 struct client *client_create(int fd, int ssl, const struct ip_addr *local_ip,
                             const struct ip_addr *ip)
 {
@@ -265,7 +282,9 @@ struct client *client_create(int fd, int ssl, const struct ip_addr *local_ip,
 
        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 +337,7 @@ int client_unref(struct pop3_client *client)
        i_stream_unref(client->input);
        o_stream_unref(client->output);
 
+       i_free(client->apop_challenge);
        i_free(client->common.virtual_user);
        i_free(client);
 
index cd789339c0d552f592732aa7eab26d0d4ef4a0e9..bb22b8a617071ab57ae86b461f66bf71d4dcb872 100644 (file)
@@ -4,6 +4,7 @@
 #include "network.h"
 #include "master.h"
 #include "client-common.h"
+#include "auth-client.h"
 
 struct pop3_client {
        struct client common;
@@ -19,6 +20,9 @@ struct pop3_client {
 
        char *last_user;
 
+       char *apop_challenge;
+       struct auth_connect_id auth_id;
+
        unsigned int tls:1;
        unsigned int secured:1;
        unsigned int input_blocked:1;