imap-proxy.c revision 45155bb1250cf5a120278f349465aded513a100f
76b43e4417bab52e913da39b5f5bc2a130d3f149Timo Sirainen/* Copyright (c) 2004-2008 Dovecot authors, see the included COPYING file */
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include "common.h"
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include "ioloop.h"
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen#include "istream.h"
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen#include "ostream.h"
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include "str.h"
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen#include "str-sanitize.h"
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen#include "safe-memset.h"
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include "client.h"
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include "imap-quote.h"
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen#include "imap-proxy.h"
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainenstatic int proxy_input_line(struct imap_client *client,
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen struct ostream *output, const char *line)
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen{
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen string_t *str;
1eff76c5dbd2ff14bbb7e40a164c290931bdf692Timo Sirainen const char *msg;
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
feb665db52583259a1f42037c6e8a22852aa8889Timo Sirainen i_assert(!client->destroyed);
9bc0204ec8bda657ce2e96e6ae715e4034f1538bTimo Sirainen
ab1236617440e654d5c5a043b677512714b788ddTimo Sirainen if (!client->proxy_login_sent) {
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen /* this is a banner */
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen if (strncmp(line, "* OK ", 5) != 0) {
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen client_syslog(&client->common, t_strdup_printf(
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen "proxy: Remote returned invalid banner: %s",
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen str_sanitize(line, 160)));
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen client_destroy_internal_failure(client);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen return -1;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen }
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen /* send LOGIN command */
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen str = t_str_new(128);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen str_append(str, "P LOGIN ");
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen imap_quote_append_string(str, client->proxy_user, FALSE);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen str_append_c(str, ' ');
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen imap_quote_append_string(str, client->proxy_password, FALSE);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen str_append(str, "\r\n");
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen (void)o_stream_send(output, str_data(str), str_len(str));
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen safe_memset(client->proxy_password, 0,
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen strlen(client->proxy_password));
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen i_free(client->proxy_password);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen client->proxy_password = NULL;
ab1236617440e654d5c5a043b677512714b788ddTimo Sirainen client->proxy_login_sent = TRUE;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen return 0;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen } else if (strncmp(line, "P OK ", 5) == 0) {
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen /* Login successful. Send this line to client. */
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen (void)o_stream_send_str(client->output, client->cmd_tag);
00b2227d6ff65629707670b7b8dfd236fced8293Timo Sirainen (void)o_stream_send_str(client->output, line + 1);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen (void)o_stream_send(client->output, "\r\n", 2);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
1eff76c5dbd2ff14bbb7e40a164c290931bdf692Timo Sirainen msg = t_strdup_printf("proxy(%s): started proxying to %s:%u",
1eff76c5dbd2ff14bbb7e40a164c290931bdf692Timo Sirainen client->common.virtual_user,
1eff76c5dbd2ff14bbb7e40a164c290931bdf692Timo Sirainen login_proxy_get_host(client->proxy),
1eff76c5dbd2ff14bbb7e40a164c290931bdf692Timo Sirainen login_proxy_get_port(client->proxy));
1eff76c5dbd2ff14bbb7e40a164c290931bdf692Timo Sirainen
00b2227d6ff65629707670b7b8dfd236fced8293Timo Sirainen (void)client_skip_line(client);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen login_proxy_detach(client->proxy, client->input,
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen client->output);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen client->proxy = NULL;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen client->input = NULL;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen client->output = NULL;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen client->common.fd = -1;
1eff76c5dbd2ff14bbb7e40a164c290931bdf692Timo Sirainen client_destroy(client, msg);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen return -1;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen } else if (strncmp(line, "P ", 2) == 0) {
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen /* Login failed. Send our own failure reply so client can't
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen figure out if user exists or not just by looking at the
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen reply string. */
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen client_send_tagline(client, "NO "AUTH_FAILED_MSG);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen /* allow client input again */
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen i_assert(client->io == NULL);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen client->io = io_add(client->common.fd, IO_READ,
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen client_input, client);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen login_proxy_free(client->proxy);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen client->proxy = NULL;
ab1236617440e654d5c5a043b677512714b788ddTimo Sirainen
ab1236617440e654d5c5a043b677512714b788ddTimo Sirainen i_free(client->proxy_user);
ab1236617440e654d5c5a043b677512714b788ddTimo Sirainen client->proxy_user = NULL;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen return -1;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen } else {
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen /* probably some untagged reply */
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen return 0;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen }
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen}
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainenstatic void proxy_input(struct istream *input, struct ostream *output,
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen struct imap_client *client)
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen{
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen const char *line;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen if (input == NULL) {
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen if (client->io != NULL) {
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen /* remote authentication failed, we're just
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen freeing the proxy */
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen return;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen }
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
d7cd49f01fad7c87c5a0865ebf54a548275e9feeTimo Sirainen if (client->destroyed) {
d7cd49f01fad7c87c5a0865ebf54a548275e9feeTimo Sirainen /* we came here from client_destroy() */
d7cd49f01fad7c87c5a0865ebf54a548275e9feeTimo Sirainen return;
d7cd49f01fad7c87c5a0865ebf54a548275e9feeTimo Sirainen }
d7cd49f01fad7c87c5a0865ebf54a548275e9feeTimo Sirainen
c0594b0763a084d2648e0df8c9b525ef094ccedaTimo Sirainen /* failed for some reason, probably server disconnected */
c0594b0763a084d2648e0df8c9b525ef094ccedaTimo Sirainen client_send_line(client, "* BYE Temporary login failure.");
c0594b0763a084d2648e0df8c9b525ef094ccedaTimo Sirainen client_destroy(client, NULL);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen return;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen }
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
d7cd49f01fad7c87c5a0865ebf54a548275e9feeTimo Sirainen i_assert(!client->destroyed);
d7cd49f01fad7c87c5a0865ebf54a548275e9feeTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen switch (i_stream_read(input)) {
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen case -2:
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen /* buffer full */
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen client_syslog(&client->common,
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen "proxy: Remote input buffer full");
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen client_destroy_internal_failure(client);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen return;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen case -1:
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen /* disconnected */
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen client_destroy(client, "Proxy: Remote disconnected");
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen return;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen }
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen while ((line = i_stream_next_line(input)) != NULL) {
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen if (proxy_input_line(client, output, line) < 0)
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen break;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen }
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen}
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainenint imap_proxy_new(struct imap_client *client, const char *host,
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen unsigned int port, const char *user, const char *password)
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen{
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen i_assert(user != NULL);
feb665db52583259a1f42037c6e8a22852aa8889Timo Sirainen i_assert(!client->destroyed);
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen if (password == NULL) {
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen client_syslog(&client->common, "proxy: password not given");
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen return -1;
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen }
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
71f1783adc89b4fe3588c72b23e059b320da8fadTimo Sirainen i_assert(client->refcount > 1);
71f1783adc89b4fe3588c72b23e059b320da8fadTimo Sirainen connection_queue_add(1);
71f1783adc89b4fe3588c72b23e059b320da8fadTimo Sirainen
feb665db52583259a1f42037c6e8a22852aa8889Timo Sirainen if (client->destroyed) {
feb665db52583259a1f42037c6e8a22852aa8889Timo Sirainen /* connection_queue_add() decided that we were the oldest
feb665db52583259a1f42037c6e8a22852aa8889Timo Sirainen connection and killed us. */
feb665db52583259a1f42037c6e8a22852aa8889Timo Sirainen return -1;
feb665db52583259a1f42037c6e8a22852aa8889Timo Sirainen }
feb665db52583259a1f42037c6e8a22852aa8889Timo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen client->proxy = login_proxy_new(&client->common, host, port,
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen proxy_input, client);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen if (client->proxy == NULL)
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen return -1;
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
ab1236617440e654d5c5a043b677512714b788ddTimo Sirainen client->proxy_login_sent = FALSE;
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen client->proxy_user = i_strdup(user);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen client->proxy_password = i_strdup(password);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen /* disable input until authentication is finished */
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (client->io != NULL)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen io_remove(&client->io);
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen return 0;
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen}