bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2004-2018 Dovecot authors, see the included COPYING file */
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainenstatic const char *imap_proxy_sent_state_names[IMAP_PROXY_SENT_STATE_COUNT] = {
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainenstatic const char *imap_proxy_rcvd_state_names[IMAP_PROXY_RCVD_STATE_COUNT] = {
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen "none", "banner", "id", "starttls", "capability",
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainenstatic void proxy_write_id(struct imap_client *client, string_t *str)
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen "\"x-originating-ip\" \"%s\" "
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen "\"x-originating-port\" \"%u\" "
493123e38ca1f27b07ac30dcbc59663c5fcdcba2Timo Sirainen "\"x-connected-ip\" \"%s\" "
ddbdc644a15f56f4b43596f1b8c0fc196c101445Timo Sirainen "\"x-connected-port\" \"%u\" "
998eadc15aabe598cc9301fdb28c0fef5225b3f7Aki Tuomi "\"x-proxy-ttl\" \"%u\"",
998eadc15aabe598cc9301fdb28c0fef5225b3f7Aki Tuomi /* append any forward_ variables to request */
998eadc15aabe598cc9301fdb28c0fef5225b3f7Aki Tuomi for(const char *const *ptr = client->common.auth_passdb_args; *ptr != NULL; ptr++) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void proxy_free_password(struct client *client)
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen safe_memset(client->proxy_password, 0, strlen(client->proxy_password));
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomistatic int proxy_write_starttls(struct imap_client *client, string_t *str)
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi enum login_proxy_ssl_flags ssl_flags = login_proxy_get_ssl_flags(client->common.login_proxy);
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi !str_array_icase_find(t_strsplit(client->proxy_backend_capability, " "), "STARTTLS")) {
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi "proxy: Remote doesn't support STARTTLS");
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen client->proxy_sent_state |= IMAP_PROXY_SENT_STATE_STARTTLS;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainenstatic int proxy_write_login(struct imap_client *client, string_t *str)
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen /* Send CAPABILITY command if we don't know the capabilities yet.
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen Also as kind of a Dovecot-backend workaround if the client insisted
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen on sending CAPABILITY command (even though our banner already sent
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen it), send the (unnecessary) CAPABILITY command to backend as well
3bc9d91f987261e989aa653fd412b0e8095e4810Timo Sirainen to avoid sending the CAPABILITY reply twice (untagged and OK resp
41783dcf1bcd7118440c9c40a691a09fb98a9460Timo Sirainen client->client_ignores_capability_resp_code)) {
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen client->proxy_sent_state |= IMAP_PROXY_SENT_STATE_CAPABILITY;
ab281fc992907b6cf6c730f672dc3aa4c6685015Timo Sirainen /* authenticate only after receiving C OK reply. */
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen /* logging in normally - use LOGIN command */
52fe791133ad838c3aca3f1c88f96aab755950f8Timo Sirainen login_proxy_get_ssl_flags(client->common.login_proxy) == 0) {
52fe791133ad838c3aca3f1c88f96aab755950f8Timo Sirainen "proxy: Remote advertised LOGINDISABLED and SSL/TLS not enabled");
686ad6d723004b807fd558f3ef9d1f88afa7e127Timo Sirainen imap_append_string(str, client->common.proxy_user);
686ad6d723004b807fd558f3ef9d1f88afa7e127Timo Sirainen imap_append_string(str, client->common.proxy_password);
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen client->proxy_sent_state |= IMAP_PROXY_SENT_STATE_LOGIN;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen i_assert(client->common.proxy_sasl_client == NULL);
26681e71837ebbb3eb92455ec4e3cadefa710f82Timo Sirainen sasl_set.authid = client->common.proxy_master_user != NULL ?
26681e71837ebbb3eb92455ec4e3cadefa710f82Timo Sirainen client->common.proxy_master_user : client->common.proxy_user;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen sasl_set.password = client->common.proxy_password;
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen dsasl_client_new(client->common.proxy_mech, &sasl_set);
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen mech_name = dsasl_client_mech_get_name(client->common.proxy_mech);
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen if (dsasl_client_output(client->common.proxy_sasl_client,
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen client_log_err(&client->common, t_strdup_printf(
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen "proxy: SASL mechanism %s init failed: %s",
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen client->proxy_sent_state |= IMAP_PROXY_SENT_STATE_AUTHENTICATE;
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainenstatic int proxy_input_banner(struct imap_client *client,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_log_err(&client->common, t_strdup_printf(
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen "proxy: Remote returned invalid banner: %s",
53d564c421ca7292d7b1bd945f86894a34b75370Timo Sirainen if (strncmp(line + 5, "[CAPABILITY ", 12) == 0) {
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen capabilities = t_strsplit(t_strcut(line + 5 + 12, ']'), " ");
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen if (str_array_icase_find(capabilities, "SASL-IR"))
52fe791133ad838c3aca3f1c88f96aab755950f8Timo Sirainen if (str_array_icase_find(capabilities, "LOGINDISABLED"))
6d24551e169c0808695db35d7a228f1970a84c75Timo Sirainen if (str_array_icase_find(capabilities, "ID") &&
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen client->proxy_sent_state |= IMAP_PROXY_SENT_STATE_ID;
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi /* write login or starttls after I OK */
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi o_stream_nsend(output, str_data(str), str_len(str));
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi if ((ret = proxy_write_starttls(client, str)) < 0) {
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi } else if (ret == 0) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(output, str_data(str), str_len(str));
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainenclient_send_login_reply(struct imap_client *client, string_t *str,
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen capability = client->proxy_backend_capability;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen tagged_capability = strncasecmp(line, "[CAPABILITY ", 12) == 0;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen if (client->client_ignores_capability_resp_code && capability != NULL) {
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen /* client has used CAPABILITY command, so it didn't understand
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen the capabilities in the banner. send the backend's untagged
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen CAPABILITY reply and hope that the client understands it */
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen str_printfa(str, "* CAPABILITY %s\r\n", capability);
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen if (!client->client_ignores_capability_resp_code &&
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen str_printfa(str, "[CAPABILITY %s] ", capability);
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen /* we need to send the capability.
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen skip over this resp-code */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenint imap_proxy_parse_line(struct client *client, const char *line)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen output = login_proxy_get_ostream(client->login_proxy);
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen /* this is a banner */
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen imap_client->proxy_rcvd_state = IMAP_PROXY_RCVD_STATE_BANNER;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (proxy_input_banner(imap_client, output, line) < 0) {
ccef83820a01bb37ad48653a05a9c5aa6560826aTimo Sirainen /* AUTHENTICATE started. finish it. */
27d50b3aa143964143e4bef66c0bfe3c72aea233Timo Sirainen /* used literals with LOGIN command, just ignore. */
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen imap_client->proxy_sent_state &= ~IMAP_PROXY_SENT_STATE_AUTHENTICATE;
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen imap_client->proxy_rcvd_state = IMAP_PROXY_RCVD_STATE_AUTH_CONTINUE;
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen base64_decode(line+2, strlen(line+2), NULL, str) < 0) {
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen "proxy: Server sent invalid base64 data in AUTHENTICATE response");
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen ret = dsasl_client_input(client->proxy_sasl_client,
de754cb78f75e8b3b994cddafe41c9ed1467c33dTimo Sirainen ret = dsasl_client_output(client->proxy_sasl_client,
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen "proxy: Server sent invalid authentication data: %s",
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen imap_client->proxy_sent_state |= IMAP_PROXY_SENT_STATE_AUTH_CONTINUE;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(output, str_data(str), str_len(str));
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen imap_client->proxy_sent_state &= ~IMAP_PROXY_SENT_STATE_STARTTLS;
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen imap_client->proxy_rcvd_state = IMAP_PROXY_RCVD_STATE_STARTTLS;
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen /* STARTTLS failed */
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen "proxy: Remote STARTTLS failed: %s",
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen /* STARTTLS successful, begin TLS negotiation. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (login_proxy_starttls(client->login_proxy) < 0) {
e95dba8921087afebb8a92c592af3b8ca22ae796Timo Sirainen /* i/ostreams changed. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen output = login_proxy_get_ostream(client->login_proxy);
1093de32efb2a231949566d4bd8aa55a8f43fb70Timo Sirainen if (proxy_write_login(imap_client, str) < 0) {
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(output, str_data(str), str_len(str));
f30577ff7cf29858f1878abe963b4f40a436434fTimo Sirainen /* Login successful. Send this line to client. */
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen imap_client->proxy_sent_state &= ~IMAP_PROXY_SENT_STATE_LOGIN;
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen imap_client->proxy_rcvd_state = IMAP_PROXY_RCVD_STATE_LOGIN;
be71a9de88d1266597eb8c5e0b6f519d90e14397Timo Sirainen client_send_login_reply(imap_client, str, line + 5);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(client->output, str_data(str), str_len(str));
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen imap_client->proxy_sent_state &= ~IMAP_PROXY_SENT_STATE_LOGIN;
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen imap_client->proxy_rcvd_state = IMAP_PROXY_RCVD_STATE_LOGIN;
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen#define STR_NO_IMAP_RESP_CODE_AUTHFAILED "NO ["IMAP_RESP_CODE_AUTHFAILED"]"
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen if (strncmp(line, STR_NO_IMAP_RESP_CODE_AUTHFAILED,
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen strlen(STR_NO_IMAP_RESP_CODE_AUTHFAILED)) == 0) {
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen /* the remote sent a generic "authentication failed"
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen error. replace it with our one, so that in case
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen the remote is sending a different error message
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen an attacker can't find out what users exist in
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen the system. */
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen client_send_reply_code(client, IMAP_CMD_REPLY_NO,
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen /* remote sent some other resp-code. forward it. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen imap_client->cmd_tag, " ", line, "\r\n", NULL));
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen /* there was no [resp-code], so remote isn't Dovecot
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen v1.2+. we could either forward the line as-is and
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen leak information about what users exist in this
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen system, or we could hide other errors than password
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen failures. since other errors are pretty rare,
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen it's safer to just hide them. they're still
e3a838c80f54f024115fade93c6c87a0998f1fabTimo Sirainen available in logs though. */
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen client_send_reply_code(client, IMAP_CMD_REPLY_NO,
3cb26db7f4756b71ba06c6e4950fa4f8ce7fad66Timo Sirainen } else if (strncasecmp(line, "* CAPABILITY ", 13) == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_free(imap_client->proxy_backend_capability);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen imap_client->proxy_backend_capability = i_strdup(line + 13);
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen /* Reply to CAPABILITY command we sent */
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen imap_client->proxy_sent_state &= ~IMAP_PROXY_SENT_STATE_CAPABILITY;
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen imap_client->proxy_rcvd_state = IMAP_PROXY_RCVD_STATE_CAPABILITY;
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen /* pipelining was disabled, send the login now. */
737d994fb6bb0f3e87f7412e35874694013d2fc3Timo Sirainen o_stream_nsend(output, str_data(str), str_len(str));
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi /* Reply to ID command we sent, ignore it unless
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi pipelining is disabled, in which case send
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi either STARTTLS or login */
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen imap_client->proxy_sent_state &= ~IMAP_PROXY_SENT_STATE_ID;
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen imap_client->proxy_rcvd_state = IMAP_PROXY_RCVD_STATE_ID;
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi if ((ret = proxy_write_starttls(imap_client, str)) < 0) {
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi } else if (ret == 0) {
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi o_stream_nsend(output, str_data(str), str_len(str));
abd4203d97d6db19550fc45d6b6806b2deadb625Aki Tuomi /* Reply to ID command we sent, ignore it */
c24150b68efb24c12e95ec101f382d5c40cd15c9Aki Tuomi /* untagged reply. just forward it. */
00fa8dcbc66f56daa737487c9dec7166c37de79eTimo Sirainen client_send_raw(client, t_strconcat(line, "\r\n", NULL));
087eb3d719a5667631cc7ce9de6c372ddea19f4dTimo Sirainen /* tagged reply, shouldn't happen. */
9c47edf0d1aa8afa6d05dde93e7aa5169059c94aTimo Sirainen "proxy: Unexpected input, ignoring: %s",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
c3ad163c5d78e6c6f9dbe90ab949506c038d3275Timo Sirainen imap_client->proxy_capability_request_sent = FALSE;
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen imap_client->proxy_rcvd_state = IMAP_PROXY_RCVD_STATE_NONE;
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainenvoid imap_proxy_error(struct client *client, const char *text)
9f627b360ed38fdc54cb02ec5e67246c3f0d5b0fTimo Sirainen client_send_reply_code(client, IMAP_CMD_REPLY_NO,
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainenconst char *imap_proxy_get_state(struct client *client)
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen for (unsigned int i = 0; i < IMAP_PROXY_SENT_STATE_COUNT; i++) {
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen if ((imap_client->proxy_sent_state & (1 << i)) != 0)
b4bf60434dcfc23433fc09e1899bba4a8b20022eTimo Sirainen str_append(str, imap_proxy_sent_state_names[i]);