submission-proxy.c revision 2cbbe9b4829adb184c83dbf780316f4144559054
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainenstatic const char *submission_proxy_state_names[SUBMISSION_PROXY_STATE_COUNT] = {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "banner", "ehlo", "starttls", "tls-ehlo", "xclient", "authenticate"
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainenstatic void proxy_free_password(struct client *client)
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen safe_memset(client->proxy_password, 0, strlen(client->proxy_password));
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainenproxy_send_xclient(struct submission_client *client, struct ostream *output)
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen const char *const *arg;
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen if ((client->proxy_capability & SMTP_CAPABILITY_XCLIENT) == 0 ||
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen /* remote supports XCLIENT, send it */
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen for(arg = client->common.auth_passdb_args; *arg != NULL; arg++) {
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen if (str_array_icase_find(client->proxy_xclient, "ADDR")) {
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen str_append(str, net_ip2addr(&client->common.ip));
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen if (str_array_icase_find(client->proxy_xclient, "PORT"))
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen str_printfa(str, "PORT=%u", client->common.remote_port);
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen if (str_array_icase_find(client->proxy_xclient, "SESSION")) {
eac3948d67eff8623d51aeaea9eca582f3aec677Timo Sirainen str_append(str, client_get_session_id(&client->common));
eac3948d67eff8623d51aeaea9eca582f3aec677Timo Sirainen if (str_array_icase_find(client->proxy_xclient, "TTL"))
eac3948d67eff8623d51aeaea9eca582f3aec677Timo Sirainen str_printfa(str, "TTL=%u", client->common.proxy_ttl - 1);
eac3948d67eff8623d51aeaea9eca582f3aec677Timo Sirainen if (str_array_icase_find(client->proxy_xclient, "FORWARD") &&
eac3948d67eff8623d51aeaea9eca582f3aec677Timo Sirainen base64_encode(str_data(fwd), str_len(fwd), str);
eac3948d67eff8623d51aeaea9eca582f3aec677Timo Sirainen o_stream_nsend(output, str_data(str), str_len(str));
eac3948d67eff8623d51aeaea9eca582f3aec677Timo Sirainen client->proxy_state = SUBMISSION_PROXY_XCLIENT;
eac3948d67eff8623d51aeaea9eca582f3aec677Timo Sirainenproxy_send_login(struct submission_client *client, struct ostream *output)
eac3948d67eff8623d51aeaea9eca582f3aec677Timo Sirainen const unsigned char *sasl_output;
e8acc691a14a6d0884c5ca9aa4d8507f1e082040Timo Sirainen if ((client->proxy_capability & SMTP_CAPABILITY_AUTH) == 0) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen /* Prevent sending credentials to a server that has login disabled;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i.e., due to the lack of TLS */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Server has disabled authentication (TLS required?)");
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen client->common.proxy_mech = &dsasl_client_mech_plain;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen i_assert(client->common.proxy_sasl_client == NULL);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen sasl_set.authid = client->common.proxy_master_user != NULL ?
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen client->common.proxy_master_user : client->common.proxy_user;
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen sasl_set.password = client->common.proxy_password;
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen dsasl_client_new(client->common.proxy_mech, &sasl_set);
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen mech_name = dsasl_client_mech_get_name(client->common.proxy_mech);
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen if (dsasl_client_output(client->common.proxy_sasl_client,
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen client_log_err(&client->common, t_strdup_printf(
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen "proxy: SASL mechanism %s init failed: %s",
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen o_stream_nsend(output, str_data(str), str_len(str));
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen if (client->proxy_state != SUBMISSION_PROXY_XCLIENT)
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen client->proxy_state = SUBMISSION_PROXY_AUTHENTICATE;
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainensubmission_proxy_continue_sasl_auth(struct client *client, struct ostream *output,
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen const unsigned char *data;
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen if (base64_decode(line, strlen(line), NULL, str) < 0) {
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen "proxy: Server sent invalid base64 data in AUTH response");
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen ret = dsasl_client_input(client->proxy_sasl_client,
a4962562bb77afe1d058124762b7ecea5028a528Timo Sirainen ret = dsasl_client_output(client->proxy_sasl_client,
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen "proxy: Server sent invalid authentication data: %s",
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen o_stream_nsend(output, str_data(str), str_len(str));
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainenstatic const char *
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainenstrip_enhanced_code(const char *text, const char **enh_code_r)
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen const char *p = text;
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen unsigned int digits;
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen if (*p != '.')
659fe5d24825b160cae512538088020d97a60239Timo Sirainen if (*p != '.')
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen if (*p != ' ')
a6745c1336427f5641715baee3d2369d3e91ebe0Timo Sirainensubmission_proxy_success_reply_sent(struct smtp_server_cmd_ctx *cmd)
a6745c1336427f5641715baee3d2369d3e91ebe0Timo Sirainen client_proxy_finish_destroy_client(&subm_client->common);
a6745c1336427f5641715baee3d2369d3e91ebe0Timo Sirainenint submission_proxy_parse_line(struct client *client, const char *line)
659fe5d24825b160cae512538088020d97a60239Timo Sirainen struct smtp_server_cmd_ctx *cmd = subm_client->pending_auth;
659fe5d24825b160cae512538088020d97a60239Timo Sirainen struct smtp_server_command *command = cmd->cmd;
e6f0cbdb1eb604f21a65cd45072febe678187054Timo Sirainen unsigned int status = 0;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen "proxy: Remote returned inconsistent SMTP reply: %s "
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen output = login_proxy_get_ostream(client->login_proxy);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen /* this is a banner */
e6f0cbdb1eb604f21a65cd45072febe678187054Timo Sirainen "proxy: Remote returned invalid banner: %s",
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen subm_client->proxy_state = SUBMISSION_PROXY_EHLO;
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen o_stream_nsend_str(output, t_strdup_printf("EHLO %s\r\n",
406393bc328f056c49df0804f894ac2070aa5846Timo Sirainen "proxy: Remote returned invalid EHLO line: %s",
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen default_pool, t_strsplit_spaces(text + 8, " "));
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen } else if (strncasecmp(text, "STARTTLS", 9) == 0) {
985fa802913c96ce6f2e25bbc788ee39c416a7e0Timo Sirainen } else if (strncasecmp(text, "AUTH", 4) == 0 &&
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen } else if (strcasecmp(text, "ENHANCEDSTATUSCODES") == 0) {
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen if (subm_client->proxy_state == SUBMISSION_PROXY_TLS_EHLO) {
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen if (proxy_send_login(subm_client, output) < 0) {
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen ssl_flags = login_proxy_get_ssl_flags(client->login_proxy);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen if ((ssl_flags & PROXY_SSL_FLAG_STARTTLS) == 0) {
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen if (proxy_send_login(subm_client, output) < 0) {
3d3bde73024116f6d739eaf9f3638f267e97bd66Timo Sirainen "proxy: Remote doesn't support STARTTLS");
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen subm_client->proxy_state = SUBMISSION_PROXY_STARTTLS;
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen "proxy: Remote STARTTLS failed: %s",
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen if (login_proxy_starttls(client->login_proxy) < 0) {
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen /* i/ostreams changed. */
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen output = login_proxy_get_ostream(client->login_proxy);
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen subm_client->proxy_state = SUBMISSION_PROXY_TLS_EHLO;
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen o_stream_nsend_str(output, t_strdup_printf("EHLO %s\r\n",
5ada3f57a970f226eb29956d30f66afc3537200dTimo Sirainen "proxy: Remote XCLIENT failed: %s",
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen subm_client->proxy_state = SUBMISSION_PROXY_AUTHENTICATE;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (status == 334 && client->proxy_sasl_client != NULL) {
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* continue SASL authentication */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (submission_proxy_continue_sasl_auth(client, output,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen smtp_server_reply_create(command, status, enh_code);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen smtp_server_reply_add_text(subm_client->proxy_reply, text);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen cmd->hook_destroy = submission_proxy_success_reply_sent;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen /* Login successful. Send this reply to client. */
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen smtp_server_reply_submit(subm_client->proxy_reply);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen /* Login failed. Pass through the error message to client.
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen If the backend server isn't Dovecot, the error message may
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen be different from Dovecot's "user doesn't exist" error. This
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen would allow an attacker to find out what users exist in the
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen The optimal way to handle this would be to replace the
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen backend's "password failed" error message with Dovecot's
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen AUTH_FAILED_MSG, but this would require a new setting and
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen the sysadmin to actually bother setting it properly.
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen So for now we'll just forward the error message. This
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen shouldn't be a real problem since of course everyone will
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen be using only Dovecot as their backend :) */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen submission_proxy_error(client, AUTH_FAILED_MSG);
b44a50ea4123f21dfc8e1b6c602f690fd9721b67Timo Sirainen smtp_server_reply_submit(subm_client->proxy_reply);
73e7998716853b5b7621c06aea0022dccda70ad1Timo Sirainenvoid submission_proxy_reset(struct client *client)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen subm_client->proxy_state = SUBMISSION_PROXY_BANNER;
406393bc328f056c49df0804f894ac2070aa5846Timo Sirainenvoid submission_proxy_error(struct client *client, const char *text)
406393bc328f056c49df0804f894ac2070aa5846Timo Sirainen struct smtp_server_cmd_ctx *cmd = subm_client->pending_auth;
406393bc328f056c49df0804f894ac2070aa5846Timo Sirainen smtp_server_reply(cmd, 535, "5.7.8", "%s", text);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenconst char *submission_proxy_get_state(struct client *client)
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen i_assert(subm_client->proxy_state < SUBMISSION_PROXY_STATE_COUNT);