imap-proxy.c revision 4b6ddd3770c8484da7308032b75fc93b91aa1b49
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2004-2008 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "common.h"
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen#include "ioloop.h"
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen#include "istream.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "ostream.h"
1dd875d96ab5640f78250079961c10e99ed4aa79Timo Sirainen#include "str.h"
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen#include "str-sanitize.h"
ffd9a1898a18fadfc5dce399162c25d50548f905Timo Sirainen#include "safe-memset.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "client.h"
89b548af722113acb5d63dfffb44423cb60f91e4Timo Sirainen#include "imap-quote.h"
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen#include "imap-proxy.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
ac26a4607cb12b156f6a42f1ead2881bedd43d94Timo Sirainenstatic int proxy_input_line(struct imap_client *client,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct ostream *output, const char *line)
66ae183b6e895216037bd921367670f4b0665911Timo Sirainen{
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen string_t *str;
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen const char *msg;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
da5d50534cfca45d0aaaf0bdac17b287b4588809Timo Sirainen i_assert(!client->destroyed);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!client->proxy_login_sent) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* this is a banner */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (strncmp(line, "* OK ", 5) != 0) {
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen client_syslog(&client->common, t_strdup_printf(
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen "proxy: Remote returned invalid banner: %s",
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen str_sanitize(line, 160)));
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen client_destroy_internal_failure(client);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* send LOGIN command */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str = t_str_new(128);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_append(str, "P LOGIN ");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen imap_quote_append_string(str, client->proxy_user, FALSE);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_append_c(str, ' ');
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen imap_quote_append_string(str, client->proxy_password, FALSE);
8fa41238067c854435884c459963fde6f8c6436bTimo Sirainen str_append(str, "\r\n");
8fa41238067c854435884c459963fde6f8c6436bTimo Sirainen (void)o_stream_send(output, str_data(str), str_len(str));
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen safe_memset(client->proxy_password, 0,
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen strlen(client->proxy_password));
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen i_free(client->proxy_password);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen client->proxy_password = NULL;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client->proxy_login_sent = TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen } else if (strncmp(line, "P OK ", 5) == 0) {
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen /* Login successful. Send this line to client. */
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen (void)o_stream_send_str(client->output, client->cmd_tag);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen (void)o_stream_send_str(client->output, line + 1);
5aeb15e5817fbd4b1d8de540aa7673e3819a8030Timo Sirainen (void)o_stream_send(client->output, "\r\n", 2);
5aeb15e5817fbd4b1d8de540aa7673e3819a8030Timo Sirainen
41e1c7380edda701719d8ce1fb4d465d2ec4c84dTimo Sirainen msg = t_strdup_printf("proxy(%s): started proxying to %s:%u",
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen client->common.virtual_user,
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen login_proxy_get_host(client->proxy),
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen login_proxy_get_port(client->proxy));
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen (void)client_skip_line(client);
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen login_proxy_detach(client->proxy, client->input,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client->output);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client->proxy = NULL;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen client->input = NULL;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client->output = NULL;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen client->common.fd = -1;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen client_destroy(client, msg);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen return -1;
9f32b9444d2a6db8f556d2c49ffceab1a59791ffTimo Sirainen } else if (strncmp(line, "P ", 2) == 0) {
9f32b9444d2a6db8f556d2c49ffceab1a59791ffTimo Sirainen /* If the backend server isn't Dovecot, the error message may
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen be different from Dovecot's "user doesn't exist" error. This
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen would allow an attacker to find out what users exist in the
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen system.
648d24583c1574441c4fa0331a90bd4d6e7996c5Timo Sirainen
648d24583c1574441c4fa0331a90bd4d6e7996c5Timo Sirainen The optimal way to handle this would be to replace the
ee246b46953e4b94b2f22e093373674fa9155500Timo Sirainen backend's "password failed" error message with Dovecot's
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen AUTH_FAILED_MSG, but this would require a new setting and
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen the sysadmin to actually bother setting it properly.
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen So for now we'll just forward the error message. This
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen shouldn't be a real problem since of course everyone will
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen be using only Dovecot as their backend :) */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_send_tagline(client, line + 2);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* allow client input again */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(client->io == NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client->io = io_add(client->common.fd, IO_READ,
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen client_input, client);
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen login_proxy_free(client->proxy);
80fc743146da5130de34174cdaad2576f103723fTimo Sirainen client->proxy = NULL;
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen i_free(client->proxy_user);
80fc743146da5130de34174cdaad2576f103723fTimo Sirainen client->proxy_user = NULL;
80fc743146da5130de34174cdaad2576f103723fTimo Sirainen return -1;
20a802016205bbcafc90f164f769ea801f88d014Timo Sirainen } else {
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen /* probably some untagged reply */
20a802016205bbcafc90f164f769ea801f88d014Timo Sirainen return 0;
20a802016205bbcafc90f164f769ea801f88d014Timo Sirainen }
20a802016205bbcafc90f164f769ea801f88d014Timo Sirainen}
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainenstatic void proxy_input(struct istream *input, struct ostream *output,
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen struct imap_client *client)
20a802016205bbcafc90f164f769ea801f88d014Timo Sirainen{
20a802016205bbcafc90f164f769ea801f88d014Timo Sirainen const char *line;
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen if (input == NULL) {
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen if (client->io != NULL) {
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen /* remote authentication failed, we're just
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen freeing the proxy */
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen return;
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen }
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen if (client->destroyed) {
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen /* we came here from client_destroy() */
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen return;
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen }
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen /* failed for some reason, probably server disconnected */
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen client_send_line(client, "* BYE Temporary login failure.");
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen client_destroy(client, NULL);
d161e3c2cde2bd8d5917840f68823a2259ed426eTimo Sirainen return;
ffd9a1898a18fadfc5dce399162c25d50548f905Timo Sirainen }
ffd9a1898a18fadfc5dce399162c25d50548f905Timo Sirainen
1e923fcf497665fe071a154c31fb452766b0b2deTimo Sirainen i_assert(!client->destroyed);
d161e3c2cde2bd8d5917840f68823a2259ed426eTimo Sirainen
1e923fcf497665fe071a154c31fb452766b0b2deTimo Sirainen switch (i_stream_read(input)) {
d161e3c2cde2bd8d5917840f68823a2259ed426eTimo Sirainen case -2:
d161e3c2cde2bd8d5917840f68823a2259ed426eTimo Sirainen /* buffer full */
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen client_syslog(&client->common,
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen "proxy: Remote input buffer full");
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen client_destroy_internal_failure(client);
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen return;
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen case -1:
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen /* disconnected */
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen client_destroy(client, "Proxy: Remote disconnected");
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen return;
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen }
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen while ((line = i_stream_next_line(input)) != NULL) {
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen if (proxy_input_line(client, output, line) < 0)
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen break;
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen }
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen}
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainenint imap_proxy_new(struct imap_client *client, const char *host,
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen unsigned int port, const char *user, const char *password)
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen{
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen i_assert(user != NULL);
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen i_assert(!client->destroyed);
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen if (password == NULL) {
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen client_syslog(&client->common, "proxy: password not given");
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen return -1;
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen }
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen i_assert(client->refcount > 1);
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen connection_queue_add(1);
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen if (client->destroyed) {
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen /* connection_queue_add() decided that we were the oldest
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen connection and killed us. */
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen return -1;
da985034a708db2f61394b30d117050ae6829ee5Timo Sirainen }
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen client->proxy = login_proxy_new(&client->common, host, port,
de12ff295bb3d0873b4dced5840612cbacd635efTimo Sirainen proxy_input, client);
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen if (client->proxy == NULL)
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen return -1;
7d7b5c98f086ffa8ac9c90f21db17748ca607202Timo Sirainen
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen client->proxy_login_sent = FALSE;
de12ff295bb3d0873b4dced5840612cbacd635efTimo Sirainen client->proxy_user = i_strdup(user);
da985034a708db2f61394b30d117050ae6829ee5Timo Sirainen client->proxy_password = i_strdup(password);
da985034a708db2f61394b30d117050ae6829ee5Timo Sirainen
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen /* disable input until authentication is finished */
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen if (client->io != NULL)
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen io_remove(&client->io);
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen return 0;
de12ff295bb3d0873b4dced5840612cbacd635efTimo Sirainen}
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen