/src/dovecot/src/lib-smtp/smtp-server-cmd-starttls.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "istream.h" |
5 | | #include "ostream.h" |
6 | | #include "iostream-ssl.h" |
7 | | #include "master-service.h" |
8 | | #include "master-service-ssl.h" |
9 | | #include "smtp-syntax.h" |
10 | | |
11 | | #include "smtp-server-private.h" |
12 | | |
13 | | /* STARTTLS command (RFC 3207) */ |
14 | | |
15 | | static int cmd_starttls_start(struct smtp_server_connection *conn) |
16 | 0 | { |
17 | 0 | const struct smtp_server_callbacks *callbacks = conn->callbacks; |
18 | |
|
19 | 0 | e_debug(conn->event, "Starting TLS"); |
20 | |
|
21 | 0 | if (callbacks != NULL && callbacks->conn_start_tls != NULL) { |
22 | 0 | struct smtp_server_connection *tmp_conn = conn; |
23 | 0 | struct istream *input = conn->conn.input; |
24 | 0 | struct ostream *output = conn->conn.output; |
25 | 0 | int ret; |
26 | |
|
27 | 0 | smtp_server_connection_ref(tmp_conn); |
28 | 0 | ret = callbacks->conn_start_tls(tmp_conn->context, |
29 | 0 | &input, &output); |
30 | 0 | if (!smtp_server_connection_unref(&tmp_conn) || ret < 0) |
31 | 0 | return -1; |
32 | | |
33 | 0 | smtp_server_connection_set_ssl_streams(conn, input, output); |
34 | 0 | } else if (smtp_server_connection_ssl_init(conn) < 0) { |
35 | 0 | smtp_server_connection_close(&conn, |
36 | 0 | "SSL Initialization failed"); |
37 | 0 | return -1; |
38 | 0 | } |
39 | | |
40 | | /* The command queue must be empty at this point. If anything were to be |
41 | | queued somehow, this connection is vulnerable to STARTTLS command |
42 | | insertion. |
43 | | */ |
44 | 0 | i_assert(conn->command_queue_count == 0 && |
45 | 0 | conn->command_queue_head == NULL); |
46 | | |
47 | | /* RFC 3207, Section 4.2: |
48 | | |
49 | | Upon completion of the TLS handshake, the SMTP protocol is reset to |
50 | | the initial state (the state in SMTP after a server issues a 220 |
51 | | service ready greeting). The server MUST discard any knowledge |
52 | | obtained from the client, such as the argument to the EHLO command, |
53 | | which was not obtained from the TLS negotiation itself. |
54 | | */ |
55 | 0 | smtp_server_connection_clear(conn); |
56 | 0 | smtp_server_connection_input_unlock(conn); |
57 | |
|
58 | 0 | return 0; |
59 | 0 | } |
60 | | |
61 | | static int cmd_starttls_output(struct smtp_server_connection *conn) |
62 | 0 | { |
63 | 0 | int ret; |
64 | |
|
65 | 0 | if ((ret=smtp_server_connection_flush(conn)) < 0) |
66 | 0 | return 1; |
67 | | |
68 | 0 | if (ret > 0) { |
69 | 0 | o_stream_unset_flush_callback(conn->conn.output); |
70 | 0 | if (cmd_starttls_start(conn) < 0) |
71 | 0 | return -1; |
72 | 0 | } |
73 | 0 | return 1; |
74 | 0 | } |
75 | | |
76 | | static void |
77 | | cmd_starttls_destroy(struct smtp_server_cmd_ctx *cmd, void *context ATTR_UNUSED) |
78 | 0 | { |
79 | 0 | struct smtp_server_connection *conn = cmd->conn; |
80 | 0 | struct smtp_server_command *command = cmd->cmd; |
81 | 0 | int ret; |
82 | |
|
83 | 0 | if (conn->conn.output == NULL) |
84 | 0 | return; |
85 | | |
86 | 0 | if (smtp_server_command_replied_success(command)) { |
87 | | /* only one valid success status for STARTTLS command */ |
88 | 0 | i_assert(smtp_server_command_reply_status_equals(command, 220)); |
89 | | |
90 | | /* uncork */ |
91 | 0 | o_stream_uncork(conn->conn.output); |
92 | | |
93 | | /* flush */ |
94 | 0 | if ((ret=smtp_server_connection_flush(conn)) < 0) { |
95 | 0 | return; |
96 | 0 | } else if (ret == 0) { |
97 | | /* the buffer has to be flushed */ |
98 | 0 | i_assert(!conn->conn.output->closed); |
99 | 0 | o_stream_set_flush_callback(conn->conn.output, |
100 | 0 | cmd_starttls_output, |
101 | 0 | conn); |
102 | 0 | o_stream_set_flush_pending(conn->conn.output, TRUE); |
103 | 0 | } else { |
104 | 0 | cmd_starttls_start(conn); |
105 | 0 | } |
106 | 0 | } |
107 | 0 | } |
108 | | |
109 | | static void |
110 | | cmd_starttls_next(struct smtp_server_cmd_ctx *cmd, void *context ATTR_UNUSED) |
111 | 0 | { |
112 | 0 | struct smtp_server_connection *conn = cmd->conn; |
113 | 0 | struct smtp_server_command *command = cmd->cmd; |
114 | 0 | const struct smtp_server_callbacks *callbacks = conn->callbacks; |
115 | 0 | int ret; |
116 | | |
117 | | /* The command queue can only contain the STARTTLS command at this |
118 | | point. If anything beyond the STARTTLS were queued somehow, this |
119 | | connection is vulnerable to STARTTLS command insertion. |
120 | | */ |
121 | 0 | i_assert(conn->command_queue_count == 1 && |
122 | 0 | conn->command_queue_tail == command); |
123 | | |
124 | 0 | smtp_server_connection_set_state(conn, SMTP_SERVER_STATE_STARTTLS, |
125 | 0 | NULL); |
126 | |
|
127 | 0 | smtp_server_command_ref(command); |
128 | 0 | if (callbacks != NULL && callbacks->conn_cmd_starttls != NULL) |
129 | 0 | ret = callbacks->conn_cmd_starttls(conn->context, cmd); |
130 | 0 | else |
131 | 0 | ret = 1; |
132 | |
|
133 | 0 | smtp_server_command_add_hook(command, SMTP_SERVER_COMMAND_HOOK_DESTROY, |
134 | 0 | cmd_starttls_destroy, NULL); |
135 | |
|
136 | 0 | if (ret <= 0) { |
137 | 0 | i_assert(ret == 0 || smtp_server_command_is_replied(command)); |
138 | | /* command is waiting for external event or it failed */ |
139 | 0 | smtp_server_command_unref(&command); |
140 | 0 | return; |
141 | 0 | } |
142 | 0 | if (!smtp_server_command_is_replied(command)) { |
143 | 0 | smtp_server_reply(cmd, |
144 | 0 | 220, "2.0.0", "Begin TLS negotiation now."); |
145 | 0 | } |
146 | 0 | smtp_server_command_unref(&command); |
147 | 0 | } |
148 | | |
149 | | void smtp_server_cmd_starttls(struct smtp_server_cmd_ctx *cmd, |
150 | | const char *params) |
151 | 327 | { |
152 | 327 | struct smtp_server_connection *conn = cmd->conn; |
153 | 327 | struct smtp_server_command *command = cmd->cmd; |
154 | 327 | enum smtp_capability capabilities = conn->set.capabilities; |
155 | | |
156 | 327 | if (conn->ssl_secured) { |
157 | 0 | i_assert((capabilities & SMTP_CAPABILITY_STARTTLS) == 0); |
158 | 0 | smtp_server_reply(cmd, |
159 | 0 | 502, "5.5.1", "TLS is already active."); |
160 | 0 | return; |
161 | 327 | } else if ((capabilities & SMTP_CAPABILITY_STARTTLS) == 0) { |
162 | 327 | smtp_server_reply(cmd, |
163 | 327 | 502, "5.5.1", "TLS support is not enabled."); |
164 | 327 | return; |
165 | 327 | } |
166 | | |
167 | | /* "STARTTLS" CRLF */ |
168 | 0 | if (*params != '\0') { |
169 | 0 | smtp_server_reply(cmd, |
170 | 0 | 501, "5.5.4", "Invalid parameters"); |
171 | 0 | return; |
172 | 0 | } |
173 | | |
174 | 0 | smtp_server_command_input_lock(cmd); |
175 | 0 | smtp_server_connection_input_lock(conn); |
176 | |
|
177 | 0 | smtp_server_command_add_hook(command, SMTP_SERVER_COMMAND_HOOK_NEXT, |
178 | 0 | cmd_starttls_next, NULL); |
179 | 0 | } |