imap-proxy.c revision 6d24551e169c0808695db35d7a228f1970a84c75
/* Copyright (c) 2004-2016 Dovecot authors, see the included COPYING file */
#include "login-common.h"
#include "array.h"
#include "ioloop.h"
#include "istream.h"
#include "ostream.h"
#include "base64.h"
#include "str.h"
#include "str-sanitize.h"
#include "safe-memset.h"
#include "dsasl-client.h"
#include "imap-login-client.h"
#include "client-authenticate.h"
#include "imap-resp-code.h"
#include "imap-quote.h"
#include "imap-proxy.h"
enum imap_proxy_state {
};
{
"\"x-session-id\" \"%s\" "
"\"x-originating-ip\" \"%s\" "
"\"x-originating-port\" \"%u\" "
"\"x-connected-ip\" \"%s\" "
"\"x-connected-port\" \"%u\" "
"\"x-proxy-ttl\" \"%u\")\r\n",
}
{
return;
}
{
if ((ssl_flags & PROXY_SSL_FLAG_STARTTLS) != 0) {
"proxy: Remote doesn't support STARTTLS");
return -1;
}
return 1;
}
return 0;
}
{
struct dsasl_client_settings sasl_set;
const unsigned char *output;
unsigned int len;
/* Send CAPABILITY command if we don't know the capabilities yet.
Also as kind of a Dovecot-backend workaround if the client insisted
on sending CAPABILITY command (even though our banner already sent
it), send the (unnecessary) CAPABILITY command to backend as well
to avoid sending the CAPABILITY reply twice (untagged and OK resp
code). */
if (!client->proxy_capability_request_sent &&
/* authenticate only after receiving C OK reply. */
return 0;
}
}
/* logging in normally - use LOGIN command */
if (client->proxy_logindisabled &&
return -1;
}
return 0;
}
if (client->proxy_sasl_ir) {
"proxy: SASL mechanism %s init failed: %s",
return -1;
}
if (len == 0)
else
}
return 0;
}
{
const char *const *capabilities = NULL;
int ret;
"proxy: Remote returned invalid banner: %s",
return -1;
}
/* write login or starttls after I OK */
return 0;
}
}
}
return -1;
} else if (ret == 0) {
return -1;
}
return 0;
}
static void
const char *line)
{
const char *capability;
bool tagged_capability;
if (tagged_capability)
/* client has used CAPABILITY command, so it didn't understand
the capabilities in the banner. send the backend's untagged
CAPABILITY reply and hope that the client understands it */
}
if (*line == '[') {
/* we need to send the capability.
skip over this resp-code */
line++;
}
}
}
{
const unsigned char *data;
unsigned int data_len;
const char *error;
int ret;
if (!imap_client->proxy_seen_banner) {
/* this is a banner */
return -1;
}
return 0;
} else if (*line == '+') {
/* AUTHENTICATE started. finish it. */
/* used literals with LOGIN command, just ignore. */
return 0;
}
"proxy: Server sent invalid base64 data in AUTHENTICATE response");
return -1;
}
if (ret == 0) {
}
if (ret < 0) {
"proxy: Server sent invalid authentication data: %s",
error));
return -1;
}
str_truncate(str, 0);
return 0;
/* STARTTLS failed */
"proxy: Remote STARTTLS failed: %s",
return -1;
}
/* STARTTLS successful, begin TLS negotiation. */
return -1;
}
return -1;
}
return 1;
/* Login successful. Send this line to client. */
(void)client_skip_line(imap_client);
return 1;
line += 2;
log_line += 3;
}
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. */
/* remote sent some other resp-code. forward it. */
} 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. */
}
return -1;
return 0;
/* Reply to CAPABILITY command we sent */
/* pipelining was disabled, send the login now. */
return -1;
return 1;
}
return 0;
/* Reply to ID command we sent, ignore it unless
pipelining is disabled, in which case send
either STARTTLS or login */
if (client->proxy_nopipelining) {
return -1;
} else if (ret == 0) {
return -1;
}
return 1;
}
return 0;
/* Reply to ID command we sent, ignore it */
return 0;
/* untagged reply. just forward it. */
return 0;
} else {
/* tagged reply, shouldn't happen. */
"proxy: Unexpected input, ignoring: %s",
return 0;
}
}
{
}
{
}