submission-proxy.c (dovecot-2.3.16) | : | submission-proxy.c (dovecot-2.3.17) | ||
---|---|---|---|---|
skipping to change at line 21 | skipping to change at line 21 | |||
#include "strescape.h" | #include "strescape.h" | |||
#include "dsasl-client.h" | #include "dsasl-client.h" | |||
#include "client.h" | #include "client.h" | |||
#include "smtp-syntax.h" | #include "smtp-syntax.h" | |||
#include "submission-login-settings.h" | #include "submission-login-settings.h" | |||
#include "submission-proxy.h" | #include "submission-proxy.h" | |||
#include <ctype.h> | #include <ctype.h> | |||
static const char *submission_proxy_state_names[SUBMISSION_PROXY_STATE_COUNT] = { | static const char *submission_proxy_state_names[SUBMISSION_PROXY_STATE_COUNT] = { | |||
"banner", "ehlo", "starttls", "tls-ehlo", "xclient", "authenticate" | "banner", "ehlo", "starttls", "tls-ehlo", "xclient", "xclient-ehlo", "aut henticate" | |||
}; | }; | |||
static void | ||||
submission_proxy_success_reply_sent( | ||||
struct smtp_server_cmd_ctx *cmd ATTR_UNUSED, | ||||
struct submission_client *subm_client) | ||||
{ | ||||
client_proxy_finish_destroy_client(&subm_client->common); | ||||
} | ||||
static int | ||||
proxy_send_starttls(struct submission_client *client, struct ostream *output) | ||||
{ | ||||
enum login_proxy_ssl_flags ssl_flags; | ||||
ssl_flags = login_proxy_get_ssl_flags(client->common.login_proxy); | ||||
if ((ssl_flags & PROXY_SSL_FLAG_STARTTLS) == 0) | ||||
return 0; | ||||
if ((client->proxy_capability & SMTP_CAPABILITY_STARTTLS) == 0) { | ||||
login_proxy_failed( | ||||
client->common.login_proxy, | ||||
login_proxy_get_event(client->common.login_proxy), | ||||
LOGIN_PROXY_FAILURE_TYPE_REMOTE_CONFIG, | ||||
"STARTTLS not supported"); | ||||
return -1; | ||||
} | ||||
o_stream_nsend_str(output, "STARTTLS\r\n"); | ||||
client->proxy_state = SUBMISSION_PROXY_STARTTLS; | ||||
return 1; | ||||
} | ||||
static buffer_t * | static buffer_t * | |||
proxy_compose_xclient_forward(struct submission_client *client) | proxy_compose_xclient_forward(struct submission_client *client) | |||
{ | { | |||
const char *const *arg; | const char *const *arg; | |||
string_t *str; | string_t *str; | |||
if (*client->common.auth_passdb_args == NULL) | if (*client->common.auth_passdb_args == NULL) | |||
return NULL; | return NULL; | |||
str = t_str_new(128); | str = t_str_new(128); | |||
for (arg = client->common.auth_passdb_args; *arg != NULL; arg++) { | for (arg = client->common.auth_passdb_args; *arg != NULL; arg++) { | |||
if (strncasecmp(*arg, "forward_", 8) == 0) { | if (strncasecmp(*arg, "forward_", 8) == 0) { | |||
if (str_len(str) > 0) | if (str_len(str) > 0) | |||
str_append_c(str, '\t'); | str_append_c(str, '\t'); | |||
str_append_tabescaped(str, (*arg)+8); | str_append_tabescaped(str, (*arg)+8); | |||
} | } | |||
} | } | |||
if (str_len(str) == 0) | ||||
return NULL; | ||||
return t_base64_encode(0, 0, str_data(str), str_len(str)); | return t_base64_encode(0, 0, str_data(str), str_len(str)); | |||
} | } | |||
static void | static void | |||
proxy_send_xclient_more_data(struct submission_client *client, | ||||
struct ostream *output, string_t *buf, | ||||
const char *field, const unsigned char *value, | ||||
size_t value_size) | ||||
{ | ||||
const size_t cmd_len = strlen("XCLIENT"); | ||||
size_t prev_len = str_len(buf); | ||||
str_append_c(buf, ' '); | ||||
str_append(buf, field); | ||||
str_append_c(buf, '='); | ||||
smtp_xtext_encode(buf, value, value_size); | ||||
if (str_len(buf) > 512) { | ||||
if (prev_len <= cmd_len) | ||||
prev_len = str_len(buf); | ||||
o_stream_nsend(output, str_data(buf), prev_len); | ||||
o_stream_nsend(output, "\r\n", 2); | ||||
client->proxy_xclient_replies_expected++; | ||||
str_delete(buf, cmd_len, prev_len - cmd_len); | ||||
} | ||||
} | ||||
static void | ||||
proxy_send_xclient_more(struct submission_client *client, | ||||
struct ostream *output, string_t *buf, | ||||
const char *field, const char *value) | ||||
{ | ||||
proxy_send_xclient_more_data(client, output, buf, field, | ||||
(const unsigned char *)value, | ||||
strlen(value)); | ||||
} | ||||
static int | ||||
proxy_send_xclient(struct submission_client *client, struct ostream *output) | proxy_send_xclient(struct submission_client *client, struct ostream *output) | |||
{ | { | |||
string_t *str; | string_t *str; | |||
if ((client->proxy_capability & SMTP_CAPABILITY_XCLIENT) == 0 || | if ((client->proxy_capability & SMTP_CAPABILITY_XCLIENT) == 0 || | |||
client->common.proxy_not_trusted) | client->common.proxy_not_trusted) | |||
return; | return 0; | |||
struct smtp_proxy_data proxy_data; | ||||
smtp_server_connection_get_proxy_data(client->conn, &proxy_data); | ||||
i_assert(client->common.proxy_ttl > 1); | ||||
/* remote supports XCLIENT, send it */ | /* remote supports XCLIENT, send it */ | |||
client->proxy_xclient_replies_expected = 0; | ||||
str = t_str_new(128); | str = t_str_new(128); | |||
str_append(str, "XCLIENT"); | str_append(str, "XCLIENT"); | |||
if (str_array_icase_find(client->proxy_xclient, "HELO")) { | ||||
if (proxy_data.helo != NULL) { | ||||
proxy_send_xclient_more(client, output, str, "HELO", | ||||
proxy_data.helo); | ||||
} else { | ||||
proxy_send_xclient_more(client, output, str, "HELO", | ||||
"[UNAVAILABLE]"); | ||||
} | ||||
} | ||||
if (str_array_icase_find(client->proxy_xclient, "PROTO")) { | ||||
const char *proto = "[UNAVAILABLE]"; | ||||
switch (proxy_data.proto) { | ||||
case SMTP_PROXY_PROTOCOL_UNKNOWN: | ||||
break; | ||||
case SMTP_PROXY_PROTOCOL_SMTP: | ||||
proto = "SMTP"; | ||||
break; | ||||
case SMTP_PROXY_PROTOCOL_ESMTP: | ||||
proto = "ESMTP"; | ||||
break; | ||||
case SMTP_PROXY_PROTOCOL_LMTP: | ||||
proto = "LMTP"; | ||||
break; | ||||
} | ||||
proxy_send_xclient_more(client, output, str, "PROTO", proto); | ||||
} | ||||
if (client->common.proxy_noauth && | ||||
str_array_icase_find(client->proxy_xclient, "LOGIN")) { | ||||
if (proxy_data.login != NULL) { | ||||
proxy_send_xclient_more(client, output, str, "LOGIN", | ||||
proxy_data.login); | ||||
} else if (client->common.virtual_user != NULL) { | ||||
proxy_send_xclient_more(client, output, str, "LOGIN", | ||||
client->common.virtual_user); | ||||
} else { | ||||
proxy_send_xclient_more(client, output, str, "LOGIN", | ||||
"[UNAVAILABLE]"); | ||||
} | ||||
} | ||||
if (str_array_icase_find(client->proxy_xclient, "TTL")) { | ||||
proxy_send_xclient_more( | ||||
client, output, str, "TTL", | ||||
t_strdup_printf("%u",client->common.proxy_ttl - 1)); | ||||
} | ||||
if (str_array_icase_find(client->proxy_xclient, "PORT")) { | ||||
proxy_send_xclient_more( | ||||
client, output, str, "PORT", | ||||
t_strdup_printf("%u", client->common.remote_port)); | ||||
} | ||||
if (str_array_icase_find(client->proxy_xclient, "ADDR")) { | if (str_array_icase_find(client->proxy_xclient, "ADDR")) { | |||
str_append(str, " ADDR="); | proxy_send_xclient_more(client, output, str, "ADDR", | |||
str_append(str, net_ip2addr(&client->common.ip)); | net_ip2addr(&client->common.ip)); | |||
} | } | |||
if (str_array_icase_find(client->proxy_xclient, "PORT")) | ||||
str_printfa(str, " PORT=%u", client->common.remote_port); | ||||
if (str_array_icase_find(client->proxy_xclient, "SESSION")) { | if (str_array_icase_find(client->proxy_xclient, "SESSION")) { | |||
str_append(str, " SESSION="); | proxy_send_xclient_more(client, output, str, "SESSION", | |||
smtp_xtext_encode_cstr( | client_get_session_id(&client->common)); | |||
str, client_get_session_id(&client->common)); | ||||
} | } | |||
if (str_array_icase_find(client->proxy_xclient, "TTL")) | ||||
str_printfa(str, " TTL=%u", client->common.proxy_ttl - 1); | ||||
if (str_array_icase_find(client->proxy_xclient, "FORWARD")) { | if (str_array_icase_find(client->proxy_xclient, "FORWARD")) { | |||
buffer_t *fwd = proxy_compose_xclient_forward(client); | buffer_t *fwd = proxy_compose_xclient_forward(client); | |||
if (fwd != NULL) { | if (fwd != NULL) { | |||
str_append(str, " FORWARD="); | proxy_send_xclient_more_data( | |||
smtp_xtext_encode(str, fwd->data, fwd->used); | client, output, str, "FORWARD", | |||
fwd->data, fwd->used); | ||||
} | } | |||
} | } | |||
str_append(str, "\r\n"); | str_append(str, "\r\n"); | |||
o_stream_nsend(output, str_data(str), str_len(str)); | o_stream_nsend(output, str_data(str), str_len(str)); | |||
client->proxy_state = SUBMISSION_PROXY_XCLIENT; | client->proxy_state = SUBMISSION_PROXY_XCLIENT; | |||
client->proxy_xclient_replies_expected++; | ||||
return 1; | ||||
} | } | |||
static int | static int | |||
proxy_send_login(struct submission_client *client, struct ostream *output) | proxy_send_login(struct submission_client *client, struct ostream *output) | |||
{ | { | |||
struct dsasl_client_settings sasl_set; | struct dsasl_client_settings sasl_set; | |||
const unsigned char *sasl_output; | const unsigned char *sasl_output; | |||
size_t len; | size_t len; | |||
const char *mech_name, *error; | const char *mech_name, *error; | |||
string_t *str; | string_t *str; | |||
skipping to change at line 102 | skipping to change at line 222 | |||
if ((client->proxy_capability & SMTP_CAPABILITY_AUTH) == 0) { | if ((client->proxy_capability & SMTP_CAPABILITY_AUTH) == 0) { | |||
/* Prevent sending credentials to a server that has login | /* Prevent sending credentials to a server that has login | |||
disabled; i.e., due to the lack of TLS */ | disabled; i.e., due to the lack of TLS */ | |||
login_proxy_failed(client->common.login_proxy, | login_proxy_failed(client->common.login_proxy, | |||
login_proxy_get_event(client->common.login_proxy), | login_proxy_get_event(client->common.login_proxy), | |||
LOGIN_PROXY_FAILURE_TYPE_REMOTE_CONFIG, | LOGIN_PROXY_FAILURE_TYPE_REMOTE_CONFIG, | |||
"Authentication support not advertised (TLS required?)"); | "Authentication support not advertised (TLS required?)"); | |||
return -1; | return -1; | |||
} | } | |||
i_assert(client->common.proxy_ttl > 1); | ||||
proxy_send_xclient(client, output); | ||||
str = t_str_new(128); | str = t_str_new(128); | |||
if (client->common.proxy_mech == NULL) | if (client->common.proxy_mech == NULL) | |||
client->common.proxy_mech = &dsasl_client_mech_plain; | client->common.proxy_mech = &dsasl_client_mech_plain; | |||
i_assert(client->common.proxy_sasl_client == NULL); | i_assert(client->common.proxy_sasl_client == NULL); | |||
i_zero(&sasl_set); | i_zero(&sasl_set); | |||
sasl_set.authid = client->common.proxy_master_user != NULL ? | sasl_set.authid = client->common.proxy_master_user != NULL ? | |||
client->common.proxy_master_user : client->common.proxy_user; | client->common.proxy_master_user : client->common.proxy_user; | |||
sasl_set.authzid = client->common.proxy_user; | sasl_set.authzid = client->common.proxy_user; | |||
skipping to change at line 138 | skipping to change at line 255 | |||
LOGIN_PROXY_FAILURE_TYPE_INTERNAL, reason); | LOGIN_PROXY_FAILURE_TYPE_INTERNAL, reason); | |||
return -1; | return -1; | |||
} | } | |||
if (len == 0) | if (len == 0) | |||
str_append_c(str, '='); | str_append_c(str, '='); | |||
else | else | |||
base64_encode(sasl_output, len, str); | base64_encode(sasl_output, len, str); | |||
str_append(str, "\r\n"); | str_append(str, "\r\n"); | |||
o_stream_nsend(output, str_data(str), str_len(str)); | o_stream_nsend(output, str_data(str), str_len(str)); | |||
if (client->proxy_state != SUBMISSION_PROXY_XCLIENT) | client->proxy_state = SUBMISSION_PROXY_AUTHENTICATE; | |||
client->proxy_state = SUBMISSION_PROXY_AUTHENTICATE; | ||||
return 0; | return 0; | |||
} | } | |||
static int | static int | |||
proxy_handle_ehlo_reply(struct submission_client *client, | ||||
struct ostream *output) | ||||
{ | ||||
struct smtp_server_cmd_ctx *cmd = client->pending_auth; | ||||
int ret; | ||||
switch (client->proxy_state) { | ||||
case SUBMISSION_PROXY_EHLO: | ||||
ret = proxy_send_starttls(client, output); | ||||
if (ret < 0) | ||||
return -1; | ||||
if (ret != 0) | ||||
return 0; | ||||
/* Fall through */ | ||||
case SUBMISSION_PROXY_TLS_EHLO: | ||||
ret = proxy_send_xclient(client, output); | ||||
if (ret < 0) | ||||
return -1; | ||||
if (ret != 0) { | ||||
client->proxy_capability = 0; | ||||
i_free_and_null(client->proxy_xclient); | ||||
o_stream_nsend_str(output, t_strdup_printf( | ||||
"EHLO %s\r\n", | ||||
client->set->hostname)); | ||||
return 0; | ||||
} | ||||
break; | ||||
case SUBMISSION_PROXY_XCLIENT_EHLO: | ||||
break; | ||||
default: | ||||
i_unreached(); | ||||
} | ||||
if (client->common.proxy_noauth) { | ||||
smtp_server_connection_input_lock(cmd->conn); | ||||
smtp_server_command_add_hook( | ||||
cmd->cmd, SMTP_SERVER_COMMAND_HOOK_DESTROY, | ||||
submission_proxy_success_reply_sent, client); | ||||
client->pending_auth = NULL; | ||||
smtp_server_reply(cmd, 235, "2.7.0", "Logged in."); | ||||
return 1; | ||||
} | ||||
return proxy_send_login(client, output); | ||||
} | ||||
static int | ||||
submission_proxy_continue_sasl_auth(struct client *client, struct ostream *outpu t, | submission_proxy_continue_sasl_auth(struct client *client, struct ostream *outpu t, | |||
const char *line) | const char *line) | |||
{ | { | |||
string_t *str; | string_t *str; | |||
const unsigned char *data; | const unsigned char *data; | |||
size_t data_len; | size_t data_len; | |||
const char *error; | const char *error; | |||
int ret; | int ret; | |||
str = t_str_new(128); | str = t_str_new(128); | |||
skipping to change at line 221 | skipping to change at line 386 | |||
p++; | p++; | |||
digits++; | digits++; | |||
} | } | |||
if (*p != ' ') | if (*p != ' ') | |||
return text; | return text; | |||
*enh_code_r = t_strdup_until(text, p); | *enh_code_r = t_strdup_until(text, p); | |||
p++; | p++; | |||
return p; | return p; | |||
} | } | |||
static void | ||||
submission_proxy_success_reply_sent( | ||||
struct smtp_server_cmd_ctx *cmd ATTR_UNUSED, | ||||
struct submission_client *subm_client) | ||||
{ | ||||
client_proxy_finish_destroy_client(&subm_client->common); | ||||
} | ||||
int submission_proxy_parse_line(struct client *client, const char *line) | int submission_proxy_parse_line(struct client *client, const char *line) | |||
{ | { | |||
struct submission_client *subm_client = | struct submission_client *subm_client = | |||
container_of(client, struct submission_client, common); | container_of(client, struct submission_client, common); | |||
struct smtp_server_cmd_ctx *cmd = subm_client->pending_auth; | struct smtp_server_cmd_ctx *cmd = subm_client->pending_auth; | |||
struct smtp_server_command *command = cmd->cmd; | struct smtp_server_command *command = cmd->cmd; | |||
struct ostream *output; | struct ostream *output; | |||
enum login_proxy_ssl_flags ssl_flags; | ||||
bool last_line = FALSE, invalid_line = FALSE; | bool last_line = FALSE, invalid_line = FALSE; | |||
const char *text = NULL, *enh_code = NULL; | const char *text = NULL, *enh_code = NULL; | |||
unsigned int status = 0; | unsigned int status = 0; | |||
i_assert(!client->destroyed); | i_assert(!client->destroyed); | |||
i_assert(cmd != NULL); | i_assert(cmd != NULL); | |||
if ((line[3] != ' ' && line[3] != '-') || | if ((line[3] != ' ' && line[3] != '-') || | |||
str_parse_uint(line, &status, &text) < 0 || | str_parse_uint(line, &status, &text) < 0 || | |||
status < 200 || status >= 560) { | status < 200 || status >= 560) { | |||
skipping to change at line 294 | skipping to change at line 450 | |||
} | } | |||
if (!last_line) | if (!last_line) | |||
return 0; | return 0; | |||
subm_client->proxy_state = SUBMISSION_PROXY_EHLO; | subm_client->proxy_state = SUBMISSION_PROXY_EHLO; | |||
o_stream_nsend_str(output, t_strdup_printf("EHLO %s\r\n", | o_stream_nsend_str(output, t_strdup_printf("EHLO %s\r\n", | |||
subm_client->set->hostname)); | subm_client->set->hostname)); | |||
return 0; | return 0; | |||
case SUBMISSION_PROXY_EHLO: | case SUBMISSION_PROXY_EHLO: | |||
case SUBMISSION_PROXY_TLS_EHLO: | case SUBMISSION_PROXY_TLS_EHLO: | |||
case SUBMISSION_PROXY_XCLIENT_EHLO: | ||||
if (invalid_line || (status / 100) != 2) { | if (invalid_line || (status / 100) != 2) { | |||
const char *reason = t_strdup_printf( | const char *reason = t_strdup_printf( | |||
"Invalid EHLO line: %s", | "Invalid EHLO line: %s", | |||
str_sanitize(line, 160)); | str_sanitize(line, 160)); | |||
login_proxy_failed(client->login_proxy, | login_proxy_failed(client->login_proxy, | |||
login_proxy_get_event(client->login_proxy), | login_proxy_get_event(client->login_proxy), | |||
LOGIN_PROXY_FAILURE_TYPE_PROTOCOL, reason); | LOGIN_PROXY_FAILURE_TYPE_PROTOCOL, reason); | |||
return -1; | return -1; | |||
} | } | |||
skipping to change at line 324 | skipping to change at line 481 | |||
text[4] == ' ' && text[5] != '\0') { | text[4] == ' ' && text[5] != '\0') { | |||
subm_client->proxy_capability |= | subm_client->proxy_capability |= | |||
SMTP_CAPABILITY_AUTH; | SMTP_CAPABILITY_AUTH; | |||
} else if (strcasecmp(text, "ENHANCEDSTATUSCODES") == 0) { | } else if (strcasecmp(text, "ENHANCEDSTATUSCODES") == 0) { | |||
subm_client->proxy_capability |= | subm_client->proxy_capability |= | |||
SMTP_CAPABILITY_ENHANCEDSTATUSCODES; | SMTP_CAPABILITY_ENHANCEDSTATUSCODES; | |||
} | } | |||
if (!last_line) | if (!last_line) | |||
return 0; | return 0; | |||
if (subm_client->proxy_state == SUBMISSION_PROXY_TLS_EHLO) { | return proxy_handle_ehlo_reply(subm_client, output); | |||
if (proxy_send_login(subm_client, output) < 0) | ||||
return -1; | ||||
return 0; | ||||
} | ||||
ssl_flags = login_proxy_get_ssl_flags(client->login_proxy); | ||||
if ((ssl_flags & PROXY_SSL_FLAG_STARTTLS) == 0) { | ||||
if (proxy_send_login(subm_client, output) < 0) | ||||
return -1; | ||||
} else { | ||||
if ((subm_client->proxy_capability & | ||||
SMTP_CAPABILITY_STARTTLS) == 0) { | ||||
login_proxy_failed(client->login_proxy, | ||||
login_proxy_get_event(client->login_proxy | ||||
), | ||||
LOGIN_PROXY_FAILURE_TYPE_REMOTE_CONFIG, | ||||
"STARTTLS not supported"); | ||||
return -1; | ||||
} | ||||
o_stream_nsend_str(output, "STARTTLS\r\n"); | ||||
subm_client->proxy_state = SUBMISSION_PROXY_STARTTLS; | ||||
} | ||||
return 0; | ||||
case SUBMISSION_PROXY_STARTTLS: | case SUBMISSION_PROXY_STARTTLS: | |||
if (invalid_line || status != 220) { | if (invalid_line || status != 220) { | |||
const char *reason = t_strdup_printf( | const char *reason = t_strdup_printf( | |||
"STARTTLS failed: %s", | "STARTTLS failed: %s", | |||
str_sanitize(line, 160)); | str_sanitize(line, 160)); | |||
login_proxy_failed(client->login_proxy, | login_proxy_failed(client->login_proxy, | |||
login_proxy_get_event(client->login_proxy), | login_proxy_get_event(client->login_proxy), | |||
LOGIN_PROXY_FAILURE_TYPE_REMOTE, reason); | LOGIN_PROXY_FAILURE_TYPE_REMOTE, reason); | |||
return -1; | return -1; | |||
} | } | |||
skipping to change at line 381 | skipping to change at line 516 | |||
if (invalid_line || (status / 100) != 2) { | if (invalid_line || (status / 100) != 2) { | |||
const char *reason = t_strdup_printf( | const char *reason = t_strdup_printf( | |||
"XCLIENT failed: %s", str_sanitize(line, 160)); | "XCLIENT failed: %s", str_sanitize(line, 160)); | |||
login_proxy_failed(client->login_proxy, | login_proxy_failed(client->login_proxy, | |||
login_proxy_get_event(client->login_proxy), | login_proxy_get_event(client->login_proxy), | |||
LOGIN_PROXY_FAILURE_TYPE_REMOTE, reason); | LOGIN_PROXY_FAILURE_TYPE_REMOTE, reason); | |||
return -1; | return -1; | |||
} | } | |||
if (!last_line) | if (!last_line) | |||
return 0; | return 0; | |||
subm_client->proxy_state = SUBMISSION_PROXY_AUTHENTICATE; | i_assert(subm_client->proxy_xclient_replies_expected > 0); | |||
if (--subm_client->proxy_xclient_replies_expected > 0) | ||||
return 0; | ||||
subm_client->proxy_state = SUBMISSION_PROXY_XCLIENT_EHLO; | ||||
return 0; | return 0; | |||
case SUBMISSION_PROXY_AUTHENTICATE: | case SUBMISSION_PROXY_AUTHENTICATE: | |||
if (invalid_line) | if (invalid_line) | |||
break; | break; | |||
if (status == 334 && client->proxy_sasl_client != NULL) { | if (status == 334 && client->proxy_sasl_client != NULL) { | |||
/* continue SASL authentication */ | /* continue SASL authentication */ | |||
if (submission_proxy_continue_sasl_auth(client, output, | if (submission_proxy_continue_sasl_auth(client, output, | |||
text) < 0) | text) < 0) | |||
return -1; | return -1; | |||
return 0; | return 0; | |||
End of changes. 21 change blocks. | ||||
52 lines changed or deleted | 189 lines changed or added |