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