pop3-proxy.c revision 6e8ad595d0603295f57bef576da8a3a00b55c5e2
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2004-2008 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "common.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "ioloop.h"
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen#include "istream.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "ostream.h"
5666a3d6a7ea89362b8d9e8b39b15424cd9d6388Timo Sirainen#include "base64.h"
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen#include "safe-memset.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "str.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "str-sanitize.h"
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen#include "client.h"
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen#include "pop3-proxy.h"
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainenstatic void proxy_input(struct istream *input, struct ostream *output,
65f8fb656051f1059f7b5a2da9c5555adcc30439Timo Sirainen struct pop3_client *client)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen{
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen string_t *str;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen const char *line;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (input == NULL) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (client->io != NULL) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen /* remote authentication failed, we're just
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen freeing the proxy */
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
f7539a17ea306191b53b8f5e752e228937df9ec3Timo Sirainen if (client->destroyed) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen /* we came here from client_destroy() */
2dd39e478269d6fb0bb26d12b394aa30ee965e38Timo Sirainen return;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen /* failed for some reason, probably server disconnected */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_send_line(client,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen "-ERR [IN-USE] Temporary login failure.");
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen client_destroy_success(client, NULL);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen i_assert(!client->destroyed);
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen switch (i_stream_read(input)) {
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen case -2:
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen /* buffer full */
3e564425db51f3921ce4de11859777135fdedd15Timo Sirainen client_syslog(&client->common,
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen "proxy: Remote input buffer full");
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen client_destroy_internal_failure(client);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen case -1:
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen /* disconnected */
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen client_destroy_success(client, "Proxy: Remote disconnected");
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return;
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen }
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen line = i_stream_next_line(input);
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen if (line == NULL)
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen return;
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen switch (client->proxy_state) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen case 0:
57a8c6a95e4bce3eeaba36985adb81c07dd683ffTimo Sirainen /* this is a banner */
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen if (strncmp(line, "+OK", 3) != 0) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen client_syslog(&client->common, t_strdup_printf(
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen "proxy: Remote returned invalid banner: %s",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_sanitize(line, 160)));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_destroy_internal_failure(client);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen return;
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen }
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen /* send USER command */
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen str = t_str_new(128);
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen str_append(str, "USER ");
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen str_append(str, client->proxy_user);
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen str_append(str, "\r\n");
f1743785713e7632459d623d5df2108f4b93accbTimo Sirainen (void)o_stream_send(output, str_data(str), str_len(str));
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
70ead6466f9baa8294e71fc2fba0a4f54f488b5eTimo Sirainen client->proxy_state++;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen return;
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen case 1:
8d630c15a8ed6f85553467c3a231a273defca5f6Timo Sirainen if (strncmp(line, "+OK", 3) != 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen break;
ee116df08d0fdab703483e18fe8076b2ef9fd9d7Timo Sirainen
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen /* USER successful, send PASS */
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen str = t_str_new(128);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen str_append(str, "PASS ");
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen str_append(str, client->proxy_password);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_append(str, "\r\n");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (void)o_stream_send(output, str_data(str),
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen str_len(str));
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen safe_memset(client->proxy_password, 0,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen strlen(client->proxy_password));
a2f250a332dfc1e6cd4ffd196c621eb9dbf7b8a1Timo Sirainen i_free(client->proxy_password);
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen client->proxy_password = NULL;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client->proxy_state++;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen return;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen case 2:
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen if (strncmp(line, "+OK", 3) != 0)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen break;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen /* Login successful. Send this line to client. */
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen line = t_strconcat(line, "\r\n", NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (void)o_stream_send_str(client->output, line);
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen str = t_str_new(128);
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen str_printfa(str, "proxy(%s): started proxying to %s:%u",
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen client->common.virtual_user,
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen login_proxy_get_host(client->proxy),
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen login_proxy_get_port(client->proxy));
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen if (strcmp(client->common.virtual_user,
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen client->proxy_user) != 0) {
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen /* remote username is different, log it */
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainen str_append_c(str, '/');
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen str_append(str, client->proxy_user);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen }
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen login_proxy_detach(client->proxy, client->common.input,
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen client->output);
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen
2584e86cc2d8c31ba30a4109cf4ba09d1e37e28aTimo Sirainen client->proxy = NULL;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen client->common.input = NULL;
4b41116563110d00330896a568eff1078c382827Timo Sirainen client->output = NULL;
4b41116563110d00330896a568eff1078c382827Timo Sirainen client->common.fd = -1;
4b41116563110d00330896a568eff1078c382827Timo Sirainen client_destroy_success(client, str_c(str));
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen return;
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen }
5137d2d80255938a0f5fb8f3c1a21b34cf11ada3Timo Sirainen
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen /* Login failed. Pass through the error message to client
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (see imap-proxy code for potential problems with this) */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (strncmp(line, "-ERR ", 5) != 0)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen client_send_line(client, "-ERR "AUTH_FAILED_MSG);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen else
b2c1349cf07410aefab0f5b17153af9e5cfcf48fTimo Sirainen client_send_line(client, line);
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (verbose_auth) {
48270badadd82279bfe50ae3d187aea8b0b2b30eTimo Sirainen str = t_str_new(128);
48270badadd82279bfe50ae3d187aea8b0b2b30eTimo Sirainen str_printfa(str, "proxy(%s): Login failed to %s:%u",
48270badadd82279bfe50ae3d187aea8b0b2b30eTimo Sirainen client->common.virtual_user,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen login_proxy_get_host(client->proxy),
61f5256ef248d35459b53534ae428bf6d016e1c5Timo Sirainen login_proxy_get_port(client->proxy));
cb05ecbd96ddb5e53c1850d27434541138a3f284Timo Sirainen if (strcmp(client->common.virtual_user,
cb05ecbd96ddb5e53c1850d27434541138a3f284Timo Sirainen client->proxy_user) != 0) {
cb05ecbd96ddb5e53c1850d27434541138a3f284Timo Sirainen /* remote username is different, log it */
14ab4610b6038da6c5d0814fecabc6b74bc81a6bTimo Sirainen str_append_c(str, '/');
14ab4610b6038da6c5d0814fecabc6b74bc81a6bTimo Sirainen str_append(str, client->proxy_user);
14ab4610b6038da6c5d0814fecabc6b74bc81a6bTimo Sirainen }
84ed9f8f3d0e5ed47607ef417618e49e4f865557Timo Sirainen str_append(str, ": ");
84ed9f8f3d0e5ed47607ef417618e49e4f865557Timo Sirainen if (strncmp(line, "-ERR ", 5) == 0)
e3796bfd2bc0fd5ba664893d346df9334a5b3af0Timo Sirainen str_append(str, line + 5);
e3796bfd2bc0fd5ba664893d346df9334a5b3af0Timo Sirainen else
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen str_append(str, line);
5afa8e2edf4f313cd56e5909f92f39c3b5b7b4d3Timo Sirainen i_info("%s", str_c(str));
5afa8e2edf4f313cd56e5909f92f39c3b5b7b4d3Timo Sirainen }
408e5be344c9131fdebe771718a5bf49f88cc51cTimo Sirainen
408e5be344c9131fdebe771718a5bf49f88cc51cTimo Sirainen /* allow client input again */
408e5be344c9131fdebe771718a5bf49f88cc51cTimo Sirainen i_assert(client->io == NULL);
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen client->io = io_add(client->common.fd, IO_READ,
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen client_input, client);
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen login_proxy_free(client->proxy);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen client->proxy = NULL;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (client->proxy_password != NULL) {
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen safe_memset(client->proxy_password, 0,
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen strlen(client->proxy_password));
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen i_free(client->proxy_password);
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen client->proxy_password = NULL;
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen }
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_free(client->proxy_user);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen client->proxy_user = NULL;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen}
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainenint pop3_proxy_new(struct pop3_client *client, const char *host,
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen unsigned int port, const char *user, const char *password)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(user != NULL);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(!client->destroyed);
de58be41126e5d68008d2ea706d62ccdc1f29337Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (password == NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_syslog(&client->common, "proxy: password not given");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return -1;
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen }
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(client->refcount > 1);
f3bb2fbe87425dc89a839908985af496f7f65702Timo Sirainen connection_queue_add(1);
f3bb2fbe87425dc89a839908985af496f7f65702Timo Sirainen
a3ee5ce6ecc8e228ee69300fdd562d7ac8be89a7Timo Sirainen if (client->destroyed) {
bd1b2615928a1e8be190cb0405754f0aec8cac2fTimo Sirainen /* connection_queue_add() decided that we were the oldest
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen connection and killed us. */
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen return -1;
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen }
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen client->proxy = login_proxy_new(&client->common, host, port,
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen proxy_input, client);
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen if (client->proxy == NULL)
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen client->proxy_state = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client->proxy_user = i_strdup(user);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client->proxy_password = i_strdup(password);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen /* disable input until authentication is finished */
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (client->io != NULL)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen io_remove(&client->io);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return 0;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen}
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen