imap-proxy.c revision 0af9ef2e9bb71a426bba236e74ceec30be699fb7
89a126810703c666309310d0f3189e9834d70b5bTimo Sirainen/* Copyright (c) 2004-2016 Dovecot authors, see the included COPYING file */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "login-common.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "array.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "ioloop.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "istream.h"
00d58fcfe8191d6ce7efa801d289a5c0fe88d1aeTimo Sirainen#include "ostream.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "base64.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "str.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "str-sanitize.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "safe-memset.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "dsasl-client.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "imap-login-client.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "client-authenticate.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "imap-resp-code.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "imap-quote.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "imap-proxy.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainenenum imap_proxy_state {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen IMAP_PROXY_STATE_NONE,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen IMAP_PROXY_STATE_BANNER,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen IMAP_PROXY_STATE_ID,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen IMAP_PROXY_STATE_STARTTLS,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen IMAP_PROXY_STATE_CAPABILITY,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen IMAP_PROXY_STATE_AUTH_CONTINUE,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen IMAP_PROXY_STATE_LOGIN
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen};
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainenstatic void proxy_write_id(struct imap_client *client, string_t *str)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen{
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen i_assert(client->common.proxy_ttl > 1);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen str_printfa(str, "I ID ("
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen "\"x-session-id\" \"%s\" "
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen "\"x-originating-ip\" \"%s\" "
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen "\"x-originating-port\" \"%u\" "
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen "\"x-connected-ip\" \"%s\" "
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen "\"x-connected-port\" \"%u\" "
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen "\"x-proxy-ttl\" \"%u\")\r\n",
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_get_session_id(&client->common),
f6c1297c26b355c4aec2a08978f51ec3efecb351Timo Sirainen net_ip2addr(&client->common.ip),
f6c1297c26b355c4aec2a08978f51ec3efecb351Timo Sirainen client->common.remote_port,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen net_ip2addr(&client->common.local_ip),
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen client->common.local_port,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen client->common.proxy_ttl - 1);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen}
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainenstatic void proxy_free_password(struct client *client)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen{
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (client->proxy_password == NULL)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen safe_memset(client->proxy_password, 0, strlen(client->proxy_password));
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen i_free_and_null(client->proxy_password);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen}
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainenstatic int proxy_write_login(struct imap_client *client, string_t *str)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen{
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen struct dsasl_client_settings sasl_set;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen const unsigned char *output;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen unsigned int len;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen const char *mech_name, *error;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen /* Send CAPABILITY command if we don't know the capabilities yet.
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen Also as kind of a Dovecot-backend workaround if the client insisted
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen on sending CAPABILITY command (even though our banner already sent
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen it), send the (unnecessary) CAPABILITY command to backend as well
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen to avoid sending the CAPABILITY reply twice (untagged and OK resp
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen code). */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (!client->proxy_capability_request_sent &&
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen (client->proxy_backend_capability == NULL ||
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client->client_ignores_capability_resp_code)) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client->proxy_capability_request_sent = TRUE;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str_append(str, "C CAPABILITY\r\n");
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (client->common.proxy_nopipelining) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen /* authenticate only after receiving C OK reply. */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return 0;
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen }
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen }
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen if (client->common.proxy_mech == NULL) {
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen /* logging in normally - use LOGIN command */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str_append(str, "L LOGIN ");
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen imap_append_string(str, client->common.proxy_user);
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen str_append_c(str, ' ');
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen imap_append_string(str, client->common.proxy_password);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str_append(str, "\r\n");
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen proxy_free_password(&client->common);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return 0;
b04e691711fd026fc82ba3e0b411420e7da4ec7eTimo Sirainen }
4d2211dac61c615c5bdfd501ea54d46c89d41b0fTimo Sirainen
4d2211dac61c615c5bdfd501ea54d46c89d41b0fTimo Sirainen i_assert(client->common.proxy_sasl_client == NULL);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen memset(&sasl_set, 0, sizeof(sasl_set));
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen sasl_set.authid = client->common.proxy_master_user != NULL ?
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client->common.proxy_master_user : client->common.proxy_user;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen sasl_set.authzid = client->common.proxy_user;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen sasl_set.password = client->common.proxy_password;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen client->common.proxy_sasl_client =
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen dsasl_client_new(client->common.proxy_mech, &sasl_set);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen mech_name = dsasl_client_mech_get_name(client->common.proxy_mech);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str_append(str, "L AUTHENTICATE ");
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen str_append(str, mech_name);
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen if (client->proxy_sasl_ir) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (dsasl_client_output(client->common.proxy_sasl_client,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen &output, &len, &error) < 0) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_log_err(&client->common, t_strdup_printf(
4d2211dac61c615c5bdfd501ea54d46c89d41b0fTimo Sirainen "proxy: SASL mechanism %s init failed: %s",
63f36c2b47217fc2dc4ed49cfc1907311d5ed366Timo Sirainen mech_name, error));
b04e691711fd026fc82ba3e0b411420e7da4ec7eTimo Sirainen return -1;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str_append_c(str, ' ');
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (len == 0)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str_append_c(str, '=');
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen else
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen base64_encode(output, len, str);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen }
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen str_append(str, "\r\n");
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen proxy_free_password(&client->common);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen return 0;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen}
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainenstatic int proxy_input_banner(struct imap_client *client,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen struct ostream *output, const char *line)
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen{
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen enum login_proxy_ssl_flags ssl_flags;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen const char *const *capabilities = NULL;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen string_t *str;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (strncmp(line, "* OK ", 5) != 0) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_log_err(&client->common, t_strdup_printf(
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen "proxy: Remote returned invalid banner: %s",
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str_sanitize(line, 160)));
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen return -1;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen }
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen str = t_str_new(128);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (strncmp(line + 5, "[CAPABILITY ", 12) == 0) {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen capabilities = t_strsplit(t_strcut(line + 5 + 12, ']'), " ");
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (str_array_icase_find(capabilities, "ID"))
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen proxy_write_id(client, str);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (str_array_icase_find(capabilities, "SASL-IR"))
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client->proxy_sasl_ir = TRUE;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen i_free(client->proxy_backend_capability);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client->proxy_backend_capability =
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen i_strdup(t_strcut(line + 5 + 12, ']'));
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen ssl_flags = login_proxy_get_ssl_flags(client->common.login_proxy);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if ((ssl_flags & PROXY_SSL_FLAG_STARTTLS) != 0) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (capabilities != NULL &&
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen !str_array_icase_find(capabilities, "STARTTLS")) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_log_err(&client->common,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen "proxy: Remote doesn't support STARTTLS");
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen return -1;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str_append(str, "S STARTTLS\r\n");
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen } else {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (proxy_write_login(client, str) < 0)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return -1;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen o_stream_nsend(output, str_data(str), str_len(str));
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return 0;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen}
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainenstatic void
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainenclient_send_login_reply(struct imap_client *client, string_t *str,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen const char *line)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen{
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen const char *capability;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen bool tagged_capability;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen capability = client->proxy_backend_capability;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen tagged_capability = strncasecmp(line, "[CAPABILITY ", 12) == 0;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (tagged_capability)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen capability = t_strcut(line + 12, ']');
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (client->client_ignores_capability_resp_code && capability != NULL) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen /* client has used CAPABILITY command, so it didn't understand
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen the capabilities in the banner. send the backend's untagged
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen CAPABILITY reply and hope that the client understands it */
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen str_printfa(str, "* CAPABILITY %s\r\n", capability);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str_append(str, client->cmd_tag);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str_append(str, " OK ");
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (!client->client_ignores_capability_resp_code &&
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen !tagged_capability && capability != NULL) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str_printfa(str, "[CAPABILITY %s] ", capability);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (*line == '[') {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen /* we need to send the capability.
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen skip over this resp-code */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen while (*line != ']' && *line != '\0')
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen line++;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (*line == ' ') line++;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str_append(str, line);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str_append(str, "\r\n");
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen}
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainenint imap_proxy_parse_line(struct client *client, const char *line)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen{
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen struct ostream *output;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen string_t *str;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen const unsigned char *data;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen unsigned int data_len;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen const char *error;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen int ret;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen i_assert(!client->destroyed);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen output = login_proxy_get_ostream(client->login_proxy);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (!imap_client->proxy_seen_banner) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen /* this is a banner */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client->proxy_state = IMAP_PROXY_STATE_BANNER;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen imap_client->proxy_seen_banner = TRUE;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (proxy_input_banner(imap_client, output, line) < 0) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen client_proxy_failed(client, TRUE);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return -1;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return 0;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen } else if (*line == '+') {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen /* AUTHENTICATE started. finish it. */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (client->proxy_sasl_client == NULL) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen /* used literals with LOGIN command, just ignore. */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return 0;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client->proxy_state = IMAP_PROXY_STATE_AUTH_CONTINUE;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str = t_str_new(128);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (line[1] != ' ' ||
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen base64_decode(line+2, strlen(line+2), NULL, str) < 0) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_log_err(client,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen "proxy: Server sent invalid base64 data in AUTHENTICATE response");
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_proxy_failed(client, TRUE);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return -1;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen ret = dsasl_client_input(client->proxy_sasl_client,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen str_data(str), str_len(str), &error);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (ret == 0) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen ret = dsasl_client_output(client->proxy_sasl_client,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen &data, &data_len, &error);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (ret < 0) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_log_err(client, t_strdup_printf(
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen "proxy: Server sent invalid authentication data: %s",
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen error));
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_proxy_failed(client, TRUE);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return -1;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen i_assert(ret == 0);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str_truncate(str, 0);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen base64_encode(data, data_len, str);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str_append(str, "\r\n");
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen o_stream_nsend(output, str_data(str), str_len(str));
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return 0;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen } else if (strncmp(line, "S ", 2) == 0) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (strncmp(line, "S OK ", 5) != 0) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen /* STARTTLS failed */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_log_err(client, t_strdup_printf(
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen "proxy: Remote STARTTLS failed: %s",
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen str_sanitize(line + 5, 160)));
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_proxy_failed(client, TRUE);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return -1;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen /* STARTTLS successful, begin TLS negotiation. */
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen client->proxy_state = IMAP_PROXY_STATE_STARTTLS;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (login_proxy_starttls(client->login_proxy) < 0) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_proxy_failed(client, TRUE);
00d58fcfe8191d6ce7efa801d289a5c0fe88d1aeTimo Sirainen return -1;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen /* i/ostreams changed. */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen output = login_proxy_get_ostream(client->login_proxy);
00d58fcfe8191d6ce7efa801d289a5c0fe88d1aeTimo Sirainen str = t_str_new(128);
00d58fcfe8191d6ce7efa801d289a5c0fe88d1aeTimo Sirainen if (proxy_write_login(imap_client, str) < 0) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_proxy_failed(client, TRUE);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return -1;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen o_stream_nsend(output, str_data(str), str_len(str));
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return 1;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen } else if (strncmp(line, "L OK ", 5) == 0) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen /* Login successful. Send this line to client. */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client->proxy_state = IMAP_PROXY_STATE_LOGIN;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen str = t_str_new(128);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_send_login_reply(imap_client, str, line + 5);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen o_stream_nsend(client->output, str_data(str), str_len(str));
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen (void)client_skip_line(imap_client);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen client_proxy_finish_destroy_client(client);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return 1;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen } else if (strncmp(line, "L ", 2) == 0) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen line += 2;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (client->set->auth_verbose) {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen const char *log_line = line;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (strncasecmp(log_line, "NO ", 3) == 0)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen log_line += 3;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_proxy_log_failure(client, log_line);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#define STR_NO_IMAP_RESP_CODE_AUTHFAILED "NO ["IMAP_RESP_CODE_AUTHFAILED"]"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (strncmp(line, STR_NO_IMAP_RESP_CODE_AUTHFAILED,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen strlen(STR_NO_IMAP_RESP_CODE_AUTHFAILED)) == 0) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen /* the remote sent a generic "authentication failed"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen error. replace it with our one, so that in case
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen the remote is sending a different error message
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen an attacker can't find out what users exist in
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen the system. */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_send_reply_code(client, IMAP_CMD_REPLY_NO,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen IMAP_RESP_CODE_AUTHFAILED,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen AUTH_FAILED_MSG);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen } else if (strncmp(line, "NO [", 4) == 0) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen /* remote sent some other resp-code. forward it. */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_send_raw(client, t_strconcat(
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen imap_client->cmd_tag, " ", line, "\r\n", NULL));
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen } else {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen /* there was no [resp-code], so remote isn't Dovecot
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen v1.2+. we could either forward the line as-is and
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen leak information about what users exist in this
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen system, or we could hide other errors than password
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen failures. since other errors are pretty rare,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen it's safer to just hide them. they're still
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen available in logs though. */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen client_send_reply_code(client, IMAP_CMD_REPLY_NO,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen IMAP_RESP_CODE_AUTHFAILED,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen AUTH_FAILED_MSG);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
client->proxy_auth_failed = TRUE;
client_proxy_failed(client, FALSE);
return -1;
} else if (strncasecmp(line, "* CAPABILITY ", 13) == 0) {
i_free(imap_client->proxy_backend_capability);
imap_client->proxy_backend_capability = i_strdup(line + 13);
return 0;
} else if (strncmp(line, "C ", 2) == 0) {
/* Reply to CAPABILITY command we sent */
client->proxy_state = IMAP_PROXY_STATE_CAPABILITY;
if (strncmp(line, "C OK ", 5) == 0 &&
client->proxy_password != NULL) {
/* pipelining was disabled, send the login now. */
str = t_str_new(128);
if (proxy_write_login(imap_client, str) < 0)
return -1;
o_stream_nsend(output, str_data(str), str_len(str));
return 1;
}
return 0;
} else if (strncasecmp(line, "I ", 2) == 0 ||
strncasecmp(line, "* ID ", 5) == 0) {
/* Reply to ID command we sent, ignore it */
client->proxy_state = IMAP_PROXY_STATE_ID;
return 0;
} else if (strncmp(line, "* ", 2) == 0) {
/* untagged reply. just foward it. */
client_send_raw(client, t_strconcat(line, "\r\n", NULL));
return 0;
} else {
/* tagged reply, shouldn't happen. */
client_log_err(client, t_strdup_printf(
"proxy: Unexpected input, ignoring: %s",
str_sanitize(line, 160)));
return 0;
}
}
void imap_proxy_reset(struct client *client)
{
struct imap_client *imap_client = (struct imap_client *)client;
imap_client->proxy_sasl_ir = FALSE;
imap_client->proxy_seen_banner = FALSE;
imap_client->proxy_capability_request_sent = FALSE;
client->proxy_state = IMAP_PROXY_STATE_NONE;
}
void imap_proxy_error(struct client *client, const char *text)
{
client_send_reply_code(client, IMAP_CMD_REPLY_NO,
IMAP_RESP_CODE_UNAVAILABLE, text);
}