smtp-server-cmd-starttls.c revision 9249c80276766fdc4d31fc3eebc22fba7d53d77c
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen#include "lib.h"
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen#include "istream.h"
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen#include "ostream.h"
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen#include "iostream-ssl.h"
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen#include "master-service.h"
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen#include "master-service-ssl.h"
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen#include "smtp-syntax.h"
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen#include "smtp-server-private.h"
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen/* STARTTLS command (RFC 3207) */
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenstatic int cmd_starttls_start(struct smtp_server_connection *conn)
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen{
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen const struct smtp_server_callbacks *callbacks = conn->callbacks;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen smtp_server_connection_debug(conn, "Starting TLS");
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen if (callbacks != NULL && callbacks->conn_start_tls != NULL) {
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen struct smtp_server_connection *tmp_conn = conn;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen struct istream *input = conn->conn.input;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen struct ostream *output = conn->conn.output;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen int ret;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen smtp_server_connection_ref(tmp_conn);
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen ret = callbacks->conn_start_tls(tmp_conn->context,
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen &input, &output);
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen if (!smtp_server_connection_unref(&tmp_conn) || ret < 0)
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen return -1;
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen smtp_server_connection_set_streams(conn, input, output);
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen } else if (smtp_server_connection_ssl_init(conn) < 0) {
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen smtp_server_connection_close(&conn,
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen "SSL Initialization failed");
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen return -1;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen }
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen /* RFC 3207, Section 4.2:
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen Upon completion of the TLS handshake, the SMTP protocol is reset to
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen the initial state (the state in SMTP after a server issues a 220
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen service ready greeting). The server MUST discard any knowledge
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen obtained from the client, such as the argument to the EHLO command,
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen which was not obtained from the TLS negotiation itself.
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen */
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen smtp_server_connection_clear(conn);
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen smtp_server_connection_input_unlock(conn);
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen return 0;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen}
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainenstatic int cmd_starttls_output(struct smtp_server_connection *conn)
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen{
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen int ret;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen if ((ret=smtp_server_connection_flush(conn)) < 0)
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen return 1;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen if (ret > 0) {
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen o_stream_unset_flush_callback(conn->conn.output);
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen if (cmd_starttls_start(conn) < 0)
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen return -1;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen }
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen return 1;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen}
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainenstatic void cmd_starttls_destroy(struct smtp_server_cmd_ctx *cmd)
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen{
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen struct smtp_server_connection *conn = cmd->conn;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen struct smtp_server_command *command = cmd->cmd;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen if (conn->conn.output == NULL)
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen return;
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen if (smtp_server_command_replied_success(command)) {
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen /* only one valid success status for STARTTLS command */
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen i_assert(smtp_server_command_reply_status_equals(command, 220));
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen /* uncork */
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen o_stream_uncork(conn->conn.output);
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen if (o_stream_flush(conn->conn.output) <= 0) {
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen /* the buffer has to be flushed */
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen o_stream_set_flush_callback(conn->conn.output,
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen cmd_starttls_output,
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen conn);
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen o_stream_set_flush_pending(conn->conn.output, TRUE);
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen } else {
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen cmd_starttls_start(conn);
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen }
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen }
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen}
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenstatic void cmd_starttls_next(struct smtp_server_cmd_ctx *cmd)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen{
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen struct smtp_server_connection *conn = cmd->conn;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen struct smtp_server_command *command = cmd->cmd;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen const struct smtp_server_callbacks *callbacks = conn->callbacks;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen int ret;
597dba3488c648ffb375ee4a552bd52ac4346979Timo Sirainen
74f810327aca91b3375d3fc963bce8076785b1cbTimo Sirainen smtp_server_connection_set_state(conn, SMTP_SERVER_STATE_STARTTLS);
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen smtp_server_command_ref(command);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen if (callbacks != NULL && callbacks->conn_cmd_starttls != NULL)
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen ret = callbacks->conn_cmd_starttls(conn->context, cmd);
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen else
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen ret = 1;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen command->hook_destroy = cmd_starttls_destroy;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen if (ret <= 0) {
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen i_assert(ret == 0 || smtp_server_command_is_replied(command));
f8512649cd4db427a8dc5dc8f45c8e379ffd4d76Timo Sirainen /* command is waiting for external event or it failed */
f8512649cd4db427a8dc5dc8f45c8e379ffd4d76Timo Sirainen smtp_server_command_unref(&command);
f8512649cd4db427a8dc5dc8f45c8e379ffd4d76Timo Sirainen return;
f8512649cd4db427a8dc5dc8f45c8e379ffd4d76Timo Sirainen }
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen if (!smtp_server_command_is_replied(command)) {
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen smtp_server_reply(cmd,
f8512649cd4db427a8dc5dc8f45c8e379ffd4d76Timo Sirainen 220, "2.0.0", "Begin TLS negotiation now.");
f8512649cd4db427a8dc5dc8f45c8e379ffd4d76Timo Sirainen }
f8512649cd4db427a8dc5dc8f45c8e379ffd4d76Timo Sirainen smtp_server_command_unref(&command);
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen}
f8512649cd4db427a8dc5dc8f45c8e379ffd4d76Timo Sirainen
63a2f941c1ad029061129ba6384d1d2a02382220Timo Sirainenvoid smtp_server_cmd_starttls(struct smtp_server_cmd_ctx *cmd,
f8512649cd4db427a8dc5dc8f45c8e379ffd4d76Timo Sirainen const char *params)
63a2f941c1ad029061129ba6384d1d2a02382220Timo Sirainen{
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen struct smtp_server_connection *conn = cmd->conn;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen struct smtp_server_command *command = cmd->cmd;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen enum smtp_capability capabilities = conn->set.capabilities;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen if (conn->ssl_secured) {
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen i_assert((capabilities & SMTP_CAPABILITY_STARTTLS) == 0);
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen smtp_server_reply(cmd,
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen 502, "5.5.1", "TLS is already active.");
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen return;
0a70a2e71658d168f75d1f075661d7936701343cTimo Sirainen } else if ((capabilities & SMTP_CAPABILITY_STARTTLS) == 0) {
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen smtp_server_reply(cmd,
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen 502, "5.5.1", "TLS support is not enabled.");
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen return;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen }
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen /* "STARTTLS" CRLF */
f8512649cd4db427a8dc5dc8f45c8e379ffd4d76Timo Sirainen if (*params != '\0') {
63a2f941c1ad029061129ba6384d1d2a02382220Timo Sirainen smtp_server_reply(cmd,
f8512649cd4db427a8dc5dc8f45c8e379ffd4d76Timo Sirainen 501, "5.5.4", "Invalid parameters");
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen return;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen }
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen
4fdf60e22b5340fe35f083b9ea5eb3fd1cf2e742Timo Sirainen smtp_server_command_input_lock(cmd);
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen smtp_server_connection_input_lock(conn);
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen command->hook_next = cmd_starttls_next;
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen}
ea37a9aedfc3a6ff5f4ea10bc4eff4ca23f62a15Timo Sirainen