bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschstatic const char *submission_proxy_state_names[SUBMISSION_PROXY_STATE_COUNT] = {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch "banner", "ehlo", "starttls", "tls-ehlo", "xclient", "authenticate"
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschstatic void proxy_free_password(struct client *client)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch safe_memset(client->proxy_password, 0, strlen(client->proxy_password));
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschproxy_send_xclient(struct submission_client *client, struct ostream *output)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if ((client->proxy_capability & SMTP_CAPABILITY_XCLIENT) == 0 ||
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch /* remote supports XCLIENT, send it */
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch for(arg = client->common.auth_passdb_args; *arg != NULL; arg++) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (str_array_icase_find(client->proxy_xclient, "ADDR")) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch str_append(str, net_ip2addr(&client->common.ip));
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (str_array_icase_find(client->proxy_xclient, "PORT"))
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch str_printfa(str, "PORT=%u", client->common.remote_port);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (str_array_icase_find(client->proxy_xclient, "SESSION")) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch str_append(str, client_get_session_id(&client->common));
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (str_array_icase_find(client->proxy_xclient, "TTL"))
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch str_printfa(str, "TTL=%u", client->common.proxy_ttl - 1);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (str_array_icase_find(client->proxy_xclient, "FORWARD") &&
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch base64_encode(str_data(fwd), str_len(fwd), str);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch o_stream_nsend(output, str_data(str), str_len(str));
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch client->proxy_state = SUBMISSION_PROXY_XCLIENT;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschproxy_send_login(struct submission_client *client, struct ostream *output)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if ((client->proxy_capability & SMTP_CAPABILITY_AUTH) == 0) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch /* Prevent sending credentials to a server that has login disabled;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch i.e., due to the lack of TLS */
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch "Server has disabled authentication (TLS required?)");
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch client->common.proxy_mech = &dsasl_client_mech_plain;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch i_assert(client->common.proxy_sasl_client == NULL);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch sasl_set.authid = client->common.proxy_master_user != NULL ?
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch client->common.proxy_master_user : client->common.proxy_user;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch sasl_set.password = client->common.proxy_password;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch dsasl_client_new(client->common.proxy_mech, &sasl_set);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch mech_name = dsasl_client_mech_get_name(client->common.proxy_mech);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (dsasl_client_output(client->common.proxy_sasl_client,
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch client_log_err(&client->common, t_strdup_printf(
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch "proxy: SASL mechanism %s init failed: %s",
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch o_stream_nsend(output, str_data(str), str_len(str));
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (client->proxy_state != SUBMISSION_PROXY_XCLIENT)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch client->proxy_state = SUBMISSION_PROXY_AUTHENTICATE;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschsubmission_proxy_continue_sasl_auth(struct client *client, struct ostream *output,
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (base64_decode(line, strlen(line), NULL, str) < 0) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch "proxy: Server sent invalid base64 data in AUTH response");
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch ret = dsasl_client_input(client->proxy_sasl_client,
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch ret = dsasl_client_output(client->proxy_sasl_client,
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch "proxy: Server sent invalid authentication data: %s",
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch o_stream_nsend(output, str_data(str), str_len(str));
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschstatic const char *
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschstrip_enhanced_code(const char *text, const char **enh_code_r)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch const char *p = text;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (*p != '.')
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (*p != '.')
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (*p != ' ')
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschsubmission_proxy_success_reply_sent(struct smtp_server_cmd_ctx *cmd)
0afd9a9acab584e770ffcd6a0e1e02e2d18d360aJosef 'Jeff' Sipek struct submission_client *subm_client = cmd->context;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch client_proxy_finish_destroy_client(&subm_client->common);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschint submission_proxy_parse_line(struct client *client, const char *line)
c4588e66e80482994cf0fbc45251cb8e9db6404dJosef 'Jeff' Sipek container_of(client, struct submission_client, common);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch struct smtp_server_cmd_ctx *cmd = subm_client->pending_auth;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch struct smtp_server_command *command = cmd->cmd;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch "proxy: Remote returned inconsistent SMTP reply: %s "
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch output = login_proxy_get_ostream(client->login_proxy);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch /* this is a banner */
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch "proxy: Remote returned invalid banner: %s",
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch subm_client->proxy_state = SUBMISSION_PROXY_EHLO;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch o_stream_nsend_str(output, t_strdup_printf("EHLO %s\r\n",
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch "proxy: Remote returned invalid EHLO line: %s",
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch default_pool, t_strsplit_spaces(text + 8, " "));
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch } else if (strncasecmp(text, "STARTTLS", 9) == 0) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch } else if (strncasecmp(text, "AUTH", 4) == 0 &&
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch } else if (strcasecmp(text, "ENHANCEDSTATUSCODES") == 0) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (subm_client->proxy_state == SUBMISSION_PROXY_TLS_EHLO) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (proxy_send_login(subm_client, output) < 0) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch ssl_flags = login_proxy_get_ssl_flags(client->login_proxy);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if ((ssl_flags & PROXY_SSL_FLAG_STARTTLS) == 0) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (proxy_send_login(subm_client, output) < 0) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch "proxy: Remote doesn't support STARTTLS");
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch subm_client->proxy_state = SUBMISSION_PROXY_STARTTLS;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch "proxy: Remote STARTTLS failed: %s",
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (login_proxy_starttls(client->login_proxy) < 0) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch /* i/ostreams changed. */
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch output = login_proxy_get_ostream(client->login_proxy);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch subm_client->proxy_state = SUBMISSION_PROXY_TLS_EHLO;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch o_stream_nsend_str(output, t_strdup_printf("EHLO %s\r\n",
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch "proxy: Remote XCLIENT failed: %s",
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch subm_client->proxy_state = SUBMISSION_PROXY_AUTHENTICATE;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (status == 334 && client->proxy_sasl_client != NULL) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch /* continue SASL authentication */
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (submission_proxy_continue_sasl_auth(client, output,
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch smtp_server_reply_create(command, status, enh_code);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch smtp_server_reply_add_text(subm_client->proxy_reply, text);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch cmd->hook_destroy = submission_proxy_success_reply_sent;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch /* Login successful. Send this reply to client. */
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch smtp_server_reply_submit(subm_client->proxy_reply);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch /* Login failed. Pass through the error message to client.
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch If the backend server isn't Dovecot, the error message may
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch be different from Dovecot's "user doesn't exist" error. This
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch would allow an attacker to find out what users exist in the
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch The optimal way to handle this would be to replace the
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch backend's "password failed" error message with Dovecot's
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch AUTH_FAILED_MSG, but this would require a new setting and
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch the sysadmin to actually bother setting it properly.
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch So for now we'll just forward the error message. This
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch shouldn't be a real problem since of course everyone will
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch be using only Dovecot as their backend :) */
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch submission_proxy_error(client, AUTH_FAILED_MSG);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch smtp_server_reply_submit(subm_client->proxy_reply);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschvoid submission_proxy_reset(struct client *client)
c4588e66e80482994cf0fbc45251cb8e9db6404dJosef 'Jeff' Sipek container_of(client, struct submission_client, common);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch subm_client->proxy_state = SUBMISSION_PROXY_BANNER;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschvoid submission_proxy_error(struct client *client, const char *text)
c4588e66e80482994cf0fbc45251cb8e9db6404dJosef 'Jeff' Sipek container_of(client, struct submission_client, common);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch struct smtp_server_cmd_ctx *cmd = subm_client->pending_auth;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch smtp_server_reply(cmd, 535, "5.7.8", "%s", text);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschconst char *submission_proxy_get_state(struct client *client)
c4588e66e80482994cf0fbc45251cb8e9db6404dJosef 'Jeff' Sipek container_of(client, struct submission_client, common);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch i_assert(subm_client->proxy_state < SUBMISSION_PROXY_STATE_COUNT);