]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
imap/pop3-login: Use libsasl for authenticating to remote IMAP/POP3 server.
authorTimo Sirainen <tss@iki.fi>
Sun, 9 Jun 2013 03:03:34 +0000 (06:03 +0300)
committerTimo Sirainen <tss@iki.fi>
Sun, 9 Jun 2013 03:03:34 +0000 (06:03 +0300)
Also passdb lookup can return "proxy_mech" extra field to specify which SASL
mechanism to use.

src/imap-login/Makefile.am
src/imap-login/client.h
src/imap-login/imap-proxy.c
src/login-common/Makefile.am
src/login-common/client-common-auth.c
src/login-common/client-common.c
src/login-common/client-common.h
src/login-common/main.c
src/pop3-login/Makefile.am
src/pop3-login/pop3-proxy.c

index ec95a05101586a8a805d9f14f22875e5033c769c..5cc5e8c8896457b77c44d4cdc1867290d50bd3a5 100644 (file)
@@ -6,6 +6,7 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib \
        -I$(top_srcdir)/src/lib-settings \
        -I$(top_srcdir)/src/lib-auth \
+       -I$(top_srcdir)/src/lib-sasl \
        -I$(top_srcdir)/src/lib-imap \
        -I$(top_srcdir)/src/lib-master \
        -I$(top_srcdir)/src/login-common
index 555d7b77d4780c7ad79f0618054e8f3585d60ecb..887c0c217751bae03cc3569393b11d925507fa81 100644 (file)
@@ -19,7 +19,6 @@ struct imap_client {
        unsigned int cmd_finished:1;
        unsigned int proxy_sasl_ir:1;
        unsigned int proxy_seen_banner:1;
-       unsigned int proxy_wait_auth_continue:1;
        unsigned int skip_line:1;
        unsigned int id_logged:1;
        unsigned int client_ignores_capability_resp_code:1;
index 71ca799238511f3a774e9051f4914f89163c60b1..e283d48a1fe294e004fc0e2996ad43f851017ece 100644 (file)
@@ -9,6 +9,7 @@
 #include "str.h"
 #include "str-sanitize.h"
 #include "safe-memset.h"
+#include "sasl-client.h"
 #include "client.h"
 #include "client-authenticate.h"
 #include "imap-resp-code.h"
@@ -55,42 +56,55 @@ static void proxy_free_password(struct client *client)
        i_free_and_null(client->proxy_password);
 }
 
-static void get_plain_auth(struct client *client, string_t *dest)
+static int proxy_write_login(struct imap_client *client, string_t *str)
 {
-       string_t *str;
-
-       str = t_str_new(128);
-       str_append(str, client->proxy_user);
-       str_append_c(str, '\0');
-       str_append(str, client->proxy_master_user);
-       str_append_c(str, '\0');
-       str_append(str, client->proxy_password);
-       base64_encode(str_data(str), str_len(str), dest);
-}
+       struct sasl_client_settings sasl_set;
+       const unsigned char *output;
+       unsigned int len;
+       const char *mech_name, *error;
 
-static void proxy_write_login(struct imap_client *client, string_t *str)
-{
        str_append(str, "C CAPABILITY\r\n");
 
-       if (client->common.proxy_master_user == NULL) {
+       if (client->common.proxy_mech == NULL) {
                /* logging in normally - use LOGIN command */
                str_append(str, "L LOGIN ");
                imap_append_string(str, client->common.proxy_user);
                str_append_c(str, ' ');
                imap_append_string(str, client->common.proxy_password);
+               str_append(str, "\r\n");
 
                proxy_free_password(&client->common);
-       } else if (client->proxy_sasl_ir) {
-               /* master user login with SASL initial response support */
-               str_append(str, "L AUTHENTICATE PLAIN ");
-               get_plain_auth(&client->common, str);
-               proxy_free_password(&client->common);
-       } else {
-               /* master user login without SASL initial response */
-               str_append(str, "L AUTHENTICATE PLAIN");
-               client->proxy_wait_auth_continue = TRUE;
+               return 0;
+       }
+
+       i_assert(client->common.proxy_sasl_client == NULL);
+       memset(&sasl_set, 0, sizeof(sasl_set));
+       sasl_set.authid = client->common.proxy_user;
+       sasl_set.authzid = client->common.proxy_master_user;
+       sasl_set.password = client->common.proxy_password;
+       client->common.proxy_sasl_client =
+               sasl_client_new(client->common.proxy_mech, &sasl_set);
+       mech_name = sasl_client_mech_get_name(client->common.proxy_mech);
+
+       str_append(str, "L AUTHENTICATE ");
+       str_append(str, mech_name);
+       if (client->proxy_sasl_ir) {
+               if (sasl_client_output(client->common.proxy_sasl_client,
+                                      &output, &len, &error) < 0) {
+                       client_log_err(&client->common, t_strdup_printf(
+                               "proxy: SASL mechanism %s init failed: %s",
+                               mech_name, error));
+                       return -1;
+               }
+               str_append_c(str, ' ');
+               if (len == 0)
+                       str_append_c(str, '=');
+               else
+                       base64_encode(output, len, str);
        }
        str_append(str, "\r\n");
+       proxy_free_password(&client->common);
+       return 0;
 }
 
 static int proxy_input_banner(struct imap_client *client,
@@ -126,7 +140,8 @@ static int proxy_input_banner(struct imap_client *client,
                }
                str_append(str, "S STARTTLS\r\n");
        } else {
-               proxy_write_login(client, str);
+               if (proxy_write_login(client, str) < 0)
+                       return -1;
        }
 
        o_stream_nsend(output, str_data(str), str_len(str));
@@ -173,6 +188,10 @@ int imap_proxy_parse_line(struct client *client, const char *line)
        struct imap_client *imap_client = (struct imap_client *)client;
        struct ostream *output;
        string_t *str;
+       const unsigned char *data;
+       unsigned int data_len;
+       const char *error;
+       int ret;
 
        i_assert(!client->destroyed);
 
@@ -188,17 +207,37 @@ int imap_proxy_parse_line(struct client *client, const char *line)
                return 0;
        } else if (*line == '+') {
                /* AUTHENTICATE started. finish it. */
-               if (!imap_client->proxy_wait_auth_continue) {
+               if (client->proxy_sasl_client == NULL) {
                        /* used literals with LOGIN command, just ignore. */
                        return 0;
                }
                client->proxy_state = IMAP_PROXY_STATE_AUTH_CONTINUE;
-               imap_client->proxy_wait_auth_continue = FALSE;
 
                str = t_str_new(128);
-               get_plain_auth(client, str);
+               if (line[1] != ' ' ||
+                   base64_decode(line+2, strlen(line+2), NULL, str) < 0) {
+                       client_log_err(client,
+                               "proxy: Server sent invalid base64 data in AUTHENTICATE response");
+                       client_proxy_failed(client, TRUE);
+                       return -1;
+               }
+               ret = sasl_client_input(client->proxy_sasl_client,
+                                       str_data(str), str_len(str), &error);
+               if (ret == 0) {
+                       ret = sasl_client_output(client->proxy_sasl_client,
+                                                &data, &data_len, &error);
+               }
+               if (ret < 0) {
+                       client_log_err(client, t_strdup_printf(
+                               "proxy: Server sent invalid authentication data: %s",
+                               error));
+                       client_proxy_failed(client, TRUE);
+                       return -1;
+               }
+
+               str_truncate(str, 0);
+               base64_encode(data, data_len, str);
                str_append(str, "\r\n");
-               proxy_free_password(client);
 
                o_stream_nsend(output, str_data(str), str_len(str));
                return 0;
@@ -220,7 +259,10 @@ int imap_proxy_parse_line(struct client *client, const char *line)
                /* i/ostreams changed. */
                output = login_proxy_get_ostream(client->login_proxy);
                str = t_str_new(128);
-               proxy_write_login(imap_client, str);
+               if (proxy_write_login(imap_client, str) < 0) {
+                       client_proxy_failed(client, TRUE);
+                       return -1;
+               }
                o_stream_nsend(output, str_data(str), str_len(str));
                return 1;
        } else if (strncmp(line, "L OK ", 5) == 0) {
@@ -305,7 +347,6 @@ void imap_proxy_reset(struct client *client)
 
        imap_client->proxy_sasl_ir = FALSE;
        imap_client->proxy_seen_banner = FALSE;
-       imap_client->proxy_wait_auth_continue = FALSE;
        client->proxy_state = IMAP_PROXY_STATE_NONE;
 }
 
index 6db44e7c58a55d2d2d47bdf382d40f2a78994022..402ae742d3ab25f9b7bab8123fff09398912b306 100644 (file)
@@ -4,6 +4,7 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib \
        -I$(top_srcdir)/src/lib-settings \
        -I$(top_srcdir)/src/lib-auth \
+       -I$(top_srcdir)/src/lib-sasl \
        -I$(top_srcdir)/src/lib-master \
        -I$(top_srcdir)/src/lib-ssl-iostream \
        -I$(top_srcdir)/src/lib-mail \
index 99c7f347324f314054813da6d5f4cd668876a85b..604c1648f33c12f2ed4d64d275fc1816551b0a5e 100644 (file)
@@ -9,6 +9,7 @@
 #include "time-util.h"
 #include "login-proxy.h"
 #include "auth-client.h"
+#include "sasl-client.h"
 #include "master-service-ssl-settings.h"
 #include "client-common.h"
 
@@ -104,6 +105,8 @@ static void client_auth_parse_args(struct client *client,
                        reply_r->proxy_timeout_msecs = 1000*atoi(value);
                else if (strcmp(key, "proxy_refresh") == 0)
                        reply_r->proxy_refresh_secs = atoi(value);
+               else if (strcmp(key, "proxy_mech") == 0)
+                       reply_r->proxy_mech = value;
                else if (strcmp(key, "master") == 0)
                        reply_r->master_user = value;
                else if (strcmp(key, "ssl") == 0) {
@@ -198,6 +201,8 @@ void client_proxy_failed(struct client *client, bool send_line)
                client_proxy_error(client, PROXY_FAILURE_MSG);
        }
 
+       if (client->proxy_sasl_client != NULL)
+               sasl_client_free(&client->proxy_sasl_client);
        login_proxy_free(&client->login_proxy);
        proxy_free_password(client);
        i_free_and_null(client->proxy_user);
@@ -270,10 +275,13 @@ static int proxy_start(struct client *client,
                       const struct client_auth_reply *reply)
 {
        struct login_proxy_settings proxy_set;
+       const struct sasl_client_mech *sasl_mech = NULL;
 
        i_assert(reply->destuser != NULL);
        i_assert(!client->destroyed);
+       i_assert(client->proxy_sasl_client == NULL);
 
+       client->proxy_mech = NULL;
        client->v.proxy_reset(client);
 
        if (reply->password == NULL) {
@@ -287,6 +295,20 @@ static int proxy_start(struct client *client,
                return -1;
        }
 
+       if (reply->proxy_mech != NULL) {
+               sasl_mech = sasl_client_mech_find(reply->proxy_mech);
+               if (sasl_mech == NULL) {
+                       client_log_err(client, t_strdup_printf(
+                               "proxy: Unsupported SASL mechanism %s",
+                               reply->proxy_mech));
+                       client_proxy_error(client, PROXY_FAILURE_MSG);
+                       return -1;
+               }
+       } else if (reply->master_user != NULL) {
+               /* have to use PLAIN authentication with master user logins */
+               sasl_mech = &sasl_client_mech_plain;
+       }
+
        i_assert(client->refcount > 1);
 
        if (client->destroyed) {
@@ -318,6 +340,7 @@ static int proxy_start(struct client *client,
                return -1;
        }
 
+       client->proxy_mech = sasl_mech;
        client->proxy_user = i_strdup(reply->destuser);
        client->proxy_master_user = i_strdup(reply->master_user);
        client->proxy_password = i_strdup(reply->password);
index e117bea2680f2161a3885105caabd8bd02355437..454b28eae0500123f55561c33da575e2e0cdcf4e 100644 (file)
@@ -18,6 +18,7 @@
 #include "master-service-ssl-settings.h"
 #include "master-auth.h"
 #include "auth-client.h"
+#include "sasl-client.h"
 #include "login-proxy.h"
 #include "ssl-proxy.h"
 #include "client-common.h"
@@ -209,6 +210,8 @@ void client_destroy(struct client *client, const char *reason)
                i_free_and_null(client->proxy_password);
        }
 
+       if (client->proxy_sasl_client != NULL)
+               sasl_client_free(&client->proxy_sasl_client);
        if (client->login_proxy != NULL)
                login_proxy_free(&client->login_proxy);
        if (client->v.destroy != NULL)
index 2758808375e754d478d25bfec0e1659c76fdf452..a73e9faab850b22f02ff638f6a17dd08c93a89c4 100644 (file)
@@ -55,7 +55,7 @@ enum client_auth_result {
 struct client_auth_reply {
        const char *master_user, *reason;
        /* for proxying */
-       const char *host, *hostip, *destuser, *password;
+       const char *host, *hostip, *destuser, *password, *proxy_mech;
        unsigned int port;
        unsigned int proxy_timeout_msecs;
        unsigned int proxy_refresh_secs;
@@ -122,6 +122,8 @@ struct client {
 
        struct login_proxy *login_proxy;
        char *proxy_user, *proxy_master_user, *proxy_password;
+       const struct sasl_client_mech *proxy_mech;
+       struct sasl_client *proxy_sasl_client;
        unsigned int proxy_state;
        unsigned int proxy_ttl;
 
index 0c959491882b8a03a458868be0270fb13c30ed40..24b83ede37fa58db34417a9580c0433f0ca44831 100644 (file)
@@ -13,6 +13,7 @@
 #include "access-lookup.h"
 #include "anvil-client.h"
 #include "auth-client.h"
+#include "sasl-client.h"
 #include "master-service-ssl-settings.h"
 #include "ssl-proxy.h"
 #include "login-proxy.h"
@@ -281,6 +282,7 @@ static void main_preinit(bool allow_core_dumps)
        /* Initialize SSL proxy so it can read certificate and private
           key file. */
        ssl_proxy_init();
+       sasl_clients_init();
 
        /* set the number of fds we want to use. it may get increased or
           decreased. leave a couple of extra fds for auth sockets and such.
@@ -356,6 +358,7 @@ static void main_deinit(void)
                anvil_client_deinit(&anvil);
        if (auth_client_to != NULL)
                timeout_remove(&auth_client_to);
+       sasl_clients_deinit();
        login_settings_deinit();
 }
 
index 6d2ad736393df331e5ebbeb32154626d790763d6..a3f831c7d81bf1b6c9ec5687aeb7e195f2279ede 100644 (file)
@@ -6,6 +6,7 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib \
        -I$(top_srcdir)/src/lib-settings \
        -I$(top_srcdir)/src/lib-auth \
+       -I$(top_srcdir)/src/lib-sasl \
        -I$(top_srcdir)/src/lib-master \
        -I$(top_srcdir)/src/login-common
 
index e0d3f25f5ce47d6a441be5dafff3e9eba991229f..dd5c7dc9662d032dfcde41369965c514d6c78557 100644 (file)
@@ -8,6 +8,7 @@
 #include "safe-memset.h"
 #include "str.h"
 #include "str-sanitize.h"
+#include "sasl-client.h"
 #include "client.h"
 #include "pop3-proxy.h"
 
@@ -20,21 +21,12 @@ static void proxy_free_password(struct client *client)
        i_free_and_null(client->proxy_password);
 }
 
-static void get_plain_auth(struct client *client, string_t *dest)
-{
-       string_t *str;
-
-       str = t_str_new(128);
-       str_append(str, client->proxy_user);
-       str_append_c(str, '\0');
-       str_append(str, client->proxy_master_user);
-       str_append_c(str, '\0');
-       str_append(str, client->proxy_password);
-       base64_encode(str_data(str), str_len(str), dest);
-}
-
-static void proxy_send_login(struct pop3_client *client, struct ostream *output)
+static int proxy_send_login(struct pop3_client *client, struct ostream *output)
 {
+       struct sasl_client_settings sasl_set;
+       const unsigned char *sasl_output;
+       unsigned int len;
+       const char *mech_name, *error;
        string_t *str;
 
        i_assert(client->common.proxy_ttl > 1);
@@ -52,16 +44,79 @@ static void proxy_send_login(struct pop3_client *client, struct ostream *output)
        }
 
        str = t_str_new(128);
-       if (client->common.proxy_master_user == NULL) {
+       if (client->common.proxy_mech == NULL) {
                /* send USER command */
                str_append(str, "USER ");
                str_append(str, client->common.proxy_user);
                str_append(str, "\r\n");
-       } else {
-               /* master user login - use AUTH PLAIN. */
-               str_append(str, "AUTH PLAIN\r\n");
+               o_stream_nsend(output, str_data(str), str_len(str));
+               return 0;
        }
+
+       i_assert(client->common.proxy_sasl_client == NULL);
+       memset(&sasl_set, 0, sizeof(sasl_set));
+       sasl_set.authid = client->common.proxy_user;
+       sasl_set.authzid = client->common.proxy_master_user;
+       sasl_set.password = client->common.proxy_password;
+       client->common.proxy_sasl_client =
+               sasl_client_new(client->common.proxy_mech, &sasl_set);
+       mech_name = sasl_client_mech_get_name(client->common.proxy_mech);
+
+       str_printfa(str, "AUTH %s ", mech_name);
+       if (sasl_client_output(client->common.proxy_sasl_client,
+                              &sasl_output, &len, &error) < 0) {
+               client_log_err(&client->common, t_strdup_printf(
+                       "proxy: SASL mechanism %s init failed: %s",
+                       mech_name, error));
+               return -1;
+       }
+       if (len == 0)
+               str_append_c(str, '=');
+       else
+               base64_encode(sasl_output, len, str);
+       str_append(str, "\r\n");
        o_stream_nsend(output, str_data(str), str_len(str));
+
+       proxy_free_password(&client->common);
+       if (client->common.proxy_state != POP3_PROXY_XCLIENT)
+               client->common.proxy_state = POP3_PROXY_LOGIN2;
+       return 0;
+}
+
+static int
+pop3_proxy_continue_sasl_auth(struct client *client, struct ostream *output,
+                             const char *line)
+{
+       string_t *str;
+       const unsigned char *data;
+       unsigned int data_len;
+       const char *error;
+       int ret;
+
+       str = t_str_new(128);
+       if (base64_decode(line, strlen(line), NULL, str) < 0) {
+               client_log_err(client, "proxy: Server sent invalid base64 data in AUTH response");
+               return -1;
+       }
+       ret = sasl_client_input(client->proxy_sasl_client,
+                               str_data(str), str_len(str), &error);
+       if (ret == 0) {
+               ret = sasl_client_output(client->proxy_sasl_client,
+                                        &data, &data_len, &error);
+       }
+       if (ret < 0) {
+               client_log_err(client, t_strdup_printf(
+                       "proxy: Server sent invalid authentication data: %s",
+                       error));
+               return -1;
+       }
+
+       str_truncate(str, 0);
+       base64_encode(data, data_len, str);
+       str_append(str, "\r\n");
+
+       o_stream_nsend(output, str_data(str), str_len(str));
+       return 0;
 }
 
 int pop3_proxy_parse_line(struct client *client, const char *line)
@@ -69,7 +124,6 @@ int pop3_proxy_parse_line(struct client *client, const char *line)
        struct pop3_client *pop3_client = (struct pop3_client *)client;
        struct ostream *output;
        enum login_proxy_ssl_flags ssl_flags;
-       string_t *str;
 
        i_assert(!client->destroyed);
 
@@ -89,7 +143,10 @@ int pop3_proxy_parse_line(struct client *client, const char *line)
 
                ssl_flags = login_proxy_get_ssl_flags(client->login_proxy);
                if ((ssl_flags & PROXY_SSL_FLAG_STARTTLS) == 0) {
-                       proxy_send_login(pop3_client, output);
+                       if (proxy_send_login(pop3_client, output) < 0) {
+                               client_proxy_failed(client, TRUE);
+                               return -1;
+                       }
                } else {
                        o_stream_nsend_str(output, "STLS\r\n");
                        client->proxy_state = POP3_PROXY_STARTTLS;
@@ -109,7 +166,10 @@ int pop3_proxy_parse_line(struct client *client, const char *line)
                }
                /* i/ostreams changed. */
                output = login_proxy_get_ostream(client->login_proxy);
-               proxy_send_login(pop3_client, output);
+               if (proxy_send_login(pop3_client, output) < 0) {
+                       client_proxy_failed(client, TRUE);
+                       return -1;
+               }
                return 1;
        case POP3_PROXY_XCLIENT:
                if (strncmp(line, "+OK", 3) != 0) {
@@ -119,30 +179,31 @@ int pop3_proxy_parse_line(struct client *client, const char *line)
                        client_proxy_failed(client, TRUE);
                        return -1;
                }
-               client->proxy_state = POP3_PROXY_LOGIN1;
+               client->proxy_state = client->proxy_sasl_client == NULL ?
+                       POP3_PROXY_LOGIN1 : POP3_PROXY_LOGIN2;
                return 0;
        case POP3_PROXY_LOGIN1:
-               str = t_str_new(128);
-               if (client->proxy_master_user == NULL) {
-                       if (strncmp(line, "+OK", 3) != 0)
-                               break;
-
-                       /* USER successful, send PASS */
-                       str_append(str, "PASS ");
-                       str_append(str, client->proxy_password);
-                       str_append(str, "\r\n");
-               } else {
-                       if (*line != '+')
-                               break;
-                       /* AUTH successful, send the authentication data */
-                       get_plain_auth(client, str);
-                       str_append(str, "\r\n");
-               }
-               o_stream_nsend(output, str_data(str), str_len(str));
+               i_assert(client->proxy_sasl_client == NULL);
+               if (strncmp(line, "+OK", 3) != 0)
+                       break;
+
+               /* USER successful, send PASS */
+               o_stream_nsend_str(output, t_strdup_printf(
+                       "PASS %s\r\n", client->proxy_password));
                proxy_free_password(client);
                client->proxy_state = POP3_PROXY_LOGIN2;
                return 0;
        case POP3_PROXY_LOGIN2:
+               if (strncmp(line, "+ ", 2) == 0 &&
+                   client->proxy_sasl_client != NULL) {
+                       /* continue SASL authentication */
+                       if (pop3_proxy_continue_sasl_auth(client, output,
+                                                         line+2) < 0) {
+                               client_proxy_failed(client, TRUE);
+                               return -1;
+                       }
+                       return 0;
+               }
                if (strncmp(line, "+OK", 3) != 0)
                        break;