Coverage Report

Created: 2023-06-07 06:18

/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
}