# HG changeset patch # User Timo Sirainen # Date 1229226391 -7200 # Node ID 50f49805b13bc1a1fabe30431a27e0bbc647398c # Parent ef08feef501ab5bbd37e5575518a13b24b9205ad imap/pop3 proxy: Support master user logins. diff -r ef08feef501a -r 50f49805b13b src/auth/auth-request-handler.c --- a/src/auth/auth-request-handler.c Sun Dec 14 04:57:23 2008 +0200 +++ b/src/auth/auth-request-handler.c Sun Dec 14 05:46:31 2008 +0200 @@ -145,10 +145,19 @@ } } - if (request->proxy && !seen_pass && request->mech_password != NULL) { - /* we're proxying - send back the password that was - sent by user (not the password in passdb). */ - auth_stream_reply_add(reply, "pass", request->mech_password); + if (request->proxy) { + /* we're proxying */ + if (!seen_pass && request->mech_password != NULL) { + /* send back the password that was sent by user + (not the password in passdb). */ + auth_stream_reply_add(reply, "pass", + request->mech_password); + } + if (request->master_user != NULL) { + /* the master username needs to be forwarded */ + auth_stream_reply_add(reply, "master", + request->master_user); + } } } diff -r ef08feef501a -r 50f49805b13b src/imap-login/client-authenticate.c --- a/src/imap-login/client-authenticate.c Sun Dec 14 04:57:23 2008 +0200 +++ b/src/imap-login/client-authenticate.c Sun Dec 14 05:46:31 2008 +0200 @@ -98,6 +98,7 @@ const char *const *args, bool success) { const char *reason = NULL, *host = NULL, *destuser = NULL, *pass = NULL; + const char *master_user = NULL; string_t *reply; unsigned int port = 143; bool proxy = FALSE, temp = FALSE, nologin = !success, proxy_self; @@ -122,6 +123,8 @@ destuser = *args + 9; else if (strncmp(*args, "pass=", 5) == 0) pass = *args + 5; + else if (strncmp(*args, "master=", 7) == 0) + master_user = *args + 7; else if (strncmp(*args, "user=", 5) == 0) { /* already handled in login-common */ } else if (auth_debug) { @@ -143,7 +146,8 @@ proxy host=.. [port=..] [destuser=..] pass=.. */ if (!success) return FALSE; - if (imap_proxy_new(client, host, port, destuser, pass) < 0) + if (imap_proxy_new(client, host, port, destuser, master_user, + pass) < 0) client_destroy_internal_failure(client); return TRUE; } diff -r ef08feef501a -r 50f49805b13b src/imap-login/client.c --- a/src/imap-login/client.c Sun Dec 14 04:57:23 2008 +0200 +++ b/src/imap-login/client.c Sun Dec 14 05:46:31 2008 +0200 @@ -585,8 +585,8 @@ client->proxy_password = NULL; } - i_free(client->proxy_user); - client->proxy_user = NULL; + i_free_and_null(client->proxy_user); + i_free_and_null(client->proxy_master_user); if (client->proxy != NULL) { login_proxy_free(client->proxy); diff -r ef08feef501a -r 50f49805b13b src/imap-login/client.h --- a/src/imap-login/client.h Sun Dec 14 04:57:23 2008 +0200 +++ b/src/imap-login/client.h Sun Dec 14 05:46:31 2008 +0200 @@ -17,7 +17,7 @@ struct timeout *to_idle_disconnect, *to_auth_waiting; struct login_proxy *proxy; - char *proxy_user, *proxy_password; + char *proxy_user, *proxy_master_user, *proxy_password; unsigned int bad_counter; diff -r ef08feef501a -r 50f49805b13b src/imap-login/imap-proxy.c --- a/src/imap-login/imap-proxy.c Sun Dec 14 04:57:23 2008 +0200 +++ b/src/imap-login/imap-proxy.c Sun Dec 14 05:46:31 2008 +0200 @@ -4,6 +4,7 @@ #include "ioloop.h" #include "istream.h" #include "ostream.h" +#include "base64.h" #include "str.h" #include "str-sanitize.h" #include "safe-memset.h" @@ -46,6 +47,66 @@ client->common.local_port); } +static void proxy_free_password(struct imap_client *client) +{ + safe_memset(client->proxy_password, 0, strlen(client->proxy_password)); + i_free_and_null(client->proxy_password); +} + +static void get_plain_auth(struct imap_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 int proxy_input_banner(struct imap_client *client, + struct ostream *output, const char *line) +{ + string_t *str; + + if (strncmp(line, "* OK ", 5) != 0) { + client_syslog(&client->common, t_strdup_printf( + "proxy: Remote returned invalid banner: %s", + str_sanitize(line, 160))); + client_destroy_internal_failure(client); + return -1; + } + + str = t_str_new(128); + if (imap_banner_has_capability(line + 5, "ID")) + proxy_write_id(client, str); + + if (client->proxy_master_user == NULL) { + /* logging in normally - use LOGIN command */ + str_append(str, "L LOGIN "); + imap_quote_append_string(str, client->proxy_user, FALSE); + str_append_c(str, ' '); + imap_quote_append_string(str, client->proxy_password, FALSE); + + proxy_free_password(client); + } else if (imap_banner_has_capability(line + 5, "SASL-IR")) { + /* master user login with SASL initial response support */ + str_append(str, "L AUTHENTICATE PLAIN "); + get_plain_auth(client, str); + proxy_free_password(client); + } else { + /* master user login without SASL initial response */ + str_append(str, "L AUTHENTICATE PLAIN"); + } + + str_append(str, "\r\n"); + (void)o_stream_send(output, str_data(str), str_len(str)); + client->proxy_login_sent = TRUE; + return 0; +} + static int proxy_input_line(struct imap_client *client, struct ostream *output, const char *line) { @@ -55,33 +116,17 @@ if (!client->proxy_login_sent) { /* this is a banner */ - if (strncmp(line, "* OK ", 5) != 0) { - client_syslog(&client->common, t_strdup_printf( - "proxy: Remote returned invalid banner: %s", - str_sanitize(line, 160))); - client_destroy_internal_failure(client); - return -1; - } - + return proxy_input_banner(client, output, line); + } else if (*line == '+') { + /* AUTHENTICATE started. finish it. */ str = t_str_new(128); - if (imap_banner_has_capability(line + 5, "ID")) - proxy_write_id(client, str); + get_plain_auth(client, str); + str_append(str, "\r\n"); + proxy_free_password(client); - /* send LOGIN command */ - str_append(str, "P LOGIN "); - imap_quote_append_string(str, client->proxy_user, FALSE); - str_append_c(str, ' '); - imap_quote_append_string(str, client->proxy_password, FALSE); - str_append(str, "\r\n"); (void)o_stream_send(output, str_data(str), str_len(str)); - - safe_memset(client->proxy_password, 0, - strlen(client->proxy_password)); - i_free(client->proxy_password); - client->proxy_password = NULL; - client->proxy_login_sent = TRUE; return 0; - } else if (strncmp(line, "P OK ", 5) == 0) { + } else if (strncmp(line, "L OK ", 5) == 0) { /* Login successful. Send this line to client. */ str = t_str_new(128); str_append(str, client->cmd_tag); @@ -101,6 +146,10 @@ str_append_c(str, '/'); str_append(str, client->proxy_user); } + if (client->proxy_master_user != NULL) { + str_printfa(str, " (master %s)", + client->proxy_master_user); + } (void)client_skip_line(client); login_proxy_detach(client->proxy, client->common.input, @@ -112,7 +161,7 @@ client->common.fd = -1; client_destroy_success(client, str_c(str)); return -1; - } else if (strncmp(line, "P ", 2) == 0) { + } else if (strncmp(line, "L ", 2) == 0) { /* If the backend server isn't Dovecot, the error message may be different from Dovecot's "user doesn't exist" error. This would allow an attacker to find out what users exist in the @@ -140,6 +189,10 @@ str_append_c(str, '/'); str_append(str, client->proxy_user); } + if (client->proxy_master_user != NULL) { + str_printfa(str, " (master %s)", + client->proxy_master_user); + } str_append(str, ": "); if (strncasecmp(line + 2, "NO ", 3) == 0) str_append(str, line + 2 + 3); @@ -156,8 +209,8 @@ login_proxy_free(client->proxy); client->proxy = NULL; - i_free(client->proxy_user); - client->proxy_user = NULL; + i_free_and_null(client->proxy_user); + i_free_and_null(client->proxy_master_user); return -1; } else { /* probably some untagged reply */ @@ -210,7 +263,8 @@ } int imap_proxy_new(struct imap_client *client, const char *host, - unsigned int port, const char *user, const char *password) + unsigned int port, const char *user, const char *master_user, + const char *password) { i_assert(user != NULL); i_assert(!client->destroyed); @@ -236,6 +290,7 @@ client->proxy_login_sent = FALSE; client->proxy_user = i_strdup(user); + client->proxy_master_user = i_strdup(master_user); client->proxy_password = i_strdup(password); /* disable input until authentication is finished */ diff -r ef08feef501a -r 50f49805b13b src/imap-login/imap-proxy.h --- a/src/imap-login/imap-proxy.h Sun Dec 14 04:57:23 2008 +0200 +++ b/src/imap-login/imap-proxy.h Sun Dec 14 05:46:31 2008 +0200 @@ -4,6 +4,7 @@ #include "login-proxy.h" int imap_proxy_new(struct imap_client *client, const char *host, - unsigned int port, const char *user, const char *password); + unsigned int port, const char *user, const char *master_user, + const char *password); #endif diff -r ef08feef501a -r 50f49805b13b src/pop3-login/client-authenticate.c --- a/src/pop3-login/client-authenticate.c Sun Dec 14 04:57:23 2008 +0200 +++ b/src/pop3-login/client-authenticate.c Sun Dec 14 05:46:31 2008 +0200 @@ -86,6 +86,7 @@ const char *const *args, bool success) { const char *reason = NULL, *host = NULL, *destuser = NULL, *pass = NULL; + const char *master_user = NULL; string_t *reply; unsigned int port = 110; bool proxy = FALSE, temp = FALSE, nologin = !success; @@ -107,6 +108,8 @@ destuser = *args + 9; else if (strncmp(*args, "pass=", 5) == 0) pass = *args + 5; + else if (strncmp(*args, "master=", 7) == 0) + master_user = *args + 7; else if (strncmp(*args, "user=", 5) == 0) { /* already handled in login-common */ } else if (auth_debug) { @@ -127,7 +130,8 @@ proxy host=.. [port=..] [destuser=..] pass=.. */ if (!success) return FALSE; - if (pop3_proxy_new(client, host, port, destuser, pass) < 0) + if (pop3_proxy_new(client, host, port, destuser, master_user, + pass) < 0) client_destroy_internal_failure(client); return TRUE; } diff -r ef08feef501a -r 50f49805b13b src/pop3-login/client.h --- a/src/pop3-login/client.h Sun Dec 14 04:57:23 2008 +0200 +++ b/src/pop3-login/client.h Sun Dec 14 05:46:31 2008 +0200 @@ -17,7 +17,7 @@ struct timeout *to_idle_disconnect; struct login_proxy *proxy; - char *proxy_user, *proxy_password; + char *proxy_user, *proxy_master_user, *proxy_password; int proxy_state; unsigned int bad_counter; diff -r ef08feef501a -r 50f49805b13b src/pop3-login/pop3-proxy.c --- a/src/pop3-login/pop3-proxy.c Sun Dec 14 04:57:23 2008 +0200 +++ b/src/pop3-login/pop3-proxy.c Sun Dec 14 05:46:31 2008 +0200 @@ -11,6 +11,19 @@ #include "client.h" #include "pop3-proxy.h" +static void get_plain_auth(struct pop3_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_input(struct istream *input, struct ostream *output, struct pop3_client *client) { @@ -66,31 +79,43 @@ return; } - /* send USER command */ str = t_str_new(128); - str_append(str, "USER "); - str_append(str, client->proxy_user); - str_append(str, "\r\n"); + if (client->proxy_master_user == NULL) { + /* send USER command */ + str_append(str, "USER "); + str_append(str, client->proxy_user); + str_append(str, "\r\n"); + } else { + /* master user login - use AUTH PLAIN. */ + str_append(str, "AUTH PLAIN\r\n"); + } (void)o_stream_send(output, str_data(str), str_len(str)); client->proxy_state++; return; case 1: - if (strncmp(line, "+OK", 3) != 0) - break; + str = t_str_new(128); + if (client->proxy_master_user == NULL) { + if (strncmp(line, "+OK", 3) != 0) + break; - /* USER successful, send PASS */ - str = t_str_new(128); - str_append(str, "PASS "); - str_append(str, client->proxy_password); - str_append(str, "\r\n"); + /* 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"); + } (void)o_stream_send(output, str_data(str), str_len(str)); safe_memset(client->proxy_password, 0, strlen(client->proxy_password)); - i_free(client->proxy_password); - client->proxy_password = NULL; + i_free_and_null(client->proxy_password); client->proxy_state++; return; @@ -113,6 +138,10 @@ str_append_c(str, '/'); str_append(str, client->proxy_user); } + if (client->proxy_master_user != NULL) { + str_printfa(str, " (master %s)", + client->proxy_master_user); + } login_proxy_detach(client->proxy, client->common.input, client->output); @@ -144,6 +173,10 @@ str_append_c(str, '/'); str_append(str, client->proxy_user); } + if (client->proxy_master_user != NULL) { + str_printfa(str, " (master %s)", + client->proxy_master_user); + } str_append(str, ": "); if (strncmp(line, "-ERR ", 5) == 0) str_append(str, line + 5); @@ -163,16 +196,16 @@ if (client->proxy_password != NULL) { safe_memset(client->proxy_password, 0, strlen(client->proxy_password)); - i_free(client->proxy_password); - client->proxy_password = NULL; + i_free_and_null(client->proxy_password); } - i_free(client->proxy_user); - client->proxy_user = NULL; + i_free_and_null(client->proxy_user); + i_free_and_null(client->proxy_master_user); } int pop3_proxy_new(struct pop3_client *client, const char *host, - unsigned int port, const char *user, const char *password) + unsigned int port, const char *user, const char *master_user, + const char *password) { i_assert(user != NULL); i_assert(!client->destroyed); @@ -198,6 +231,7 @@ client->proxy_state = 0; client->proxy_user = i_strdup(user); + client->proxy_master_user = i_strdup(master_user); client->proxy_password = i_strdup(password); /* disable input until authentication is finished */ diff -r ef08feef501a -r 50f49805b13b src/pop3-login/pop3-proxy.h --- a/src/pop3-login/pop3-proxy.h Sun Dec 14 04:57:23 2008 +0200 +++ b/src/pop3-login/pop3-proxy.h Sun Dec 14 05:46:31 2008 +0200 @@ -4,6 +4,7 @@ #include "login-proxy.h" int pop3_proxy_new(struct pop3_client *client, const char *host, - unsigned int port, const char *user, const char *password); + unsigned int port, const char *user, const char *master_user, + const char *password); #endif