-
Notifications
You must be signed in to change notification settings - Fork 271
/
cmd-data.c
360 lines (304 loc) · 9.5 KB
/
cmd-data.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
#include "submission-common.h"
#include "str.h"
#include "str-sanitize.h"
#include "istream.h"
#include "istream-concat.h"
#include "istream-seekable.h"
#include "mail-storage.h"
#include "imap-url.h"
#include "imap-msgpart.h"
#include "imap-msgpart-url.h"
#include "imap-urlauth.h"
#include "imap-urlauth-fetch.h"
#include "smtp-address.h"
#include "smtp-client.h"
#include "smtp-client-connection.h"
#include "submission-commands.h"
/*
* DATA/BDAT commands
*/
struct cmd_data_context {
struct client *client;
struct smtp_server_cmd_ctx *cmd;
struct smtp_server_transaction *trans;
struct smtp_client_command *cmd_proxied;
};
static void cmd_data_proxy_cb(const struct smtp_reply *proxy_reply,
struct cmd_data_context *data_ctx)
{
struct smtp_server_cmd_ctx *cmd = data_ctx->cmd;
struct smtp_server_transaction *trans = data_ctx->trans;
struct client *client = data_ctx->client;
struct smtp_reply reply;
/* finished proxying message to relay server */
/* check for fatal problems */
if (!client_command_handle_proxy_reply(client, proxy_reply, &reply))
return;
if (proxy_reply->status / 100 == 2) {
i_info("Successfully relayed message: "
"from=<%s>, size=%"PRIuUOFF_T", "
"id=%s, nrcpt=%u, reply=`%s'",
smtp_address_encode(trans->mail_from),
client->state.data_size, trans->id,
array_count(&trans->rcpt_to),
str_sanitize(smtp_reply_log(proxy_reply), 128));
} else {
i_info("Failed to relay message: "
"from=<%s>, size=%"PRIuUOFF_T", nrcpt=%u, reply=`%s'",
smtp_address_encode(trans->mail_from),
client->state.data_size, array_count(&trans->rcpt_to),
str_sanitize(smtp_reply_log(proxy_reply), 128));
}
smtp_server_reply_forward(cmd, &reply);
}
int cmd_data_continue(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
struct smtp_server_transaction *trans)
{
struct client *client = conn_ctx;
struct cmd_data_context *data_ctx = trans->context;
struct istream *data_input = client->state.data_input;
struct istream *inputs[3];
string_t *added_headers;
const unsigned char *data;
size_t size;
int ret;
while ((ret = i_stream_read_more(data_input, &data, &size)) > 0)
i_stream_skip(data_input, size);
if (ret == 0)
return 0;
if (ret < 0 && data_input->stream_errno != 0)
return -1;
/* Done reading DATA stream; remove it from state and continue with
local variable. */
client->state.data_input = NULL;
ret = i_stream_get_size(data_input, TRUE,
&client->state.data_size);
i_assert(ret > 0); // FIXME
/* prepend our own headers */
added_headers = t_str_new(200);
smtp_server_transaction_write_trace_record(added_headers, trans);
i_stream_seek(data_input, 0);
inputs[0] = i_stream_create_copy_from_data(
str_data(added_headers), str_len(added_headers));
inputs[1] = data_input;
inputs[2] = NULL;
data_ctx->cmd = cmd;
data_input = i_stream_create_concat(inputs);
i_stream_unref(&inputs[0]);
i_stream_unref(&inputs[1]);
/* start proxying to relay server */
data_ctx->cmd_proxied = smtp_client_command_data_submit(
client->proxy_conn, 0, data_input,
cmd_data_proxy_cb, data_ctx);
i_stream_unref(&data_input);
return 0;
}
int cmd_data_begin(void *conn_ctx,
struct smtp_server_cmd_ctx *cmd ATTR_UNUSED,
struct smtp_server_transaction *trans,
struct istream *data_input)
{
struct client *client = conn_ctx;
struct cmd_data_context *data_ctx;
struct istream *inputs[2];
string_t *path;
data_ctx = p_new(trans->pool, struct cmd_data_context, 1);
data_ctx->client = client;
data_ctx->trans = trans;
trans->context = (void*)data_ctx;
inputs[0] = data_input;
inputs[1] = NULL;
path = t_str_new(256);
mail_user_set_get_temp_prefix(path, client->user->set);
client->state.data_input = i_stream_create_seekable_path(inputs,
SUBMISSION_MAIL_DATA_MAX_INMEMORY_SIZE, str_c(path));
return 0;
}
/*
* BURL command
*/
/* FIXME: RFC 4468
If the URL argument to BURL refers to binary data, then the submit server
MAY refuse the command or down convert as described in Binary SMTP.
*/
struct cmd_burl_context {
struct client *client;
struct smtp_server_cmd_ctx *cmd;
struct imap_urlauth_fetch *urlauth_fetch;
struct imap_msgpart_url *url_fetch;
bool chunk_last:1;
};
static void
cmd_burl_destroy(struct smtp_server_cmd_ctx *cmd)
{
struct cmd_burl_context *burl_cmd = cmd->context;
if (burl_cmd->urlauth_fetch != NULL)
imap_urlauth_fetch_deinit(&burl_cmd->urlauth_fetch);
if (burl_cmd->url_fetch != NULL)
imap_msgpart_url_free(&burl_cmd->url_fetch);
}
static int
cmd_burl_fetch_cb(struct imap_urlauth_fetch_reply *reply,
bool last, void *context)
{
struct cmd_burl_context *burl_cmd = context;
struct smtp_server_cmd_ctx *cmd = burl_cmd->cmd;
int ret;
i_assert(last);
if (reply == NULL) {
/* fatal failure */
// FIXME: make this an internal error
smtp_server_reply(cmd, 554, "5.6.6",
"IMAP URLAUTH resolution failed");
return -1;
}
if (!reply->succeeded) {
/* URL fetch failed */
if (reply->error != NULL) {
smtp_server_reply(cmd, 554, "5.6.6",
"IMAP URLAUTH resolution failed: %s",
reply->error);
} else {
smtp_server_reply(cmd, 554, "5.6.6",
"IMAP URLAUTH resolution failed");
}
return 1;
}
/* URL fetch succeeded */
ret = smtp_server_connection_data_chunk_add(cmd,
reply->input, reply->size, burl_cmd->chunk_last, FALSE);
if (ret < 0)
return -1;
/* Command is likely not yet complete at this point, so return 0 */
return 0;
}
static int
cmd_burl_fetch_trusted(struct cmd_burl_context *burl_cmd,
struct imap_url *imap_url)
{
struct smtp_server_cmd_ctx *cmd = burl_cmd->cmd;
struct client *client = burl_cmd->client;
const char *host_name = client->set->imap_urlauth_host;
in_port_t host_port = client->set->imap_urlauth_port;
struct imap_msgpart_open_result result;
const char *error;
/* validate host */
if (imap_url->host.name == NULL ||
(strcmp(host_name, URL_HOST_ALLOW_ANY) != 0 &&
strcmp(imap_url->host.name, host_name) != 0)) {
smtp_server_reply(cmd, 554, "5.6.6",
"IMAP URL resolution failed: "
"Inappropriate or missing host name");
return -1;
}
/* validate port */
if ((imap_url->port == 0 && host_port != 143) ||
(imap_url->port != 0 && host_port != imap_url->port)) {
smtp_server_reply(cmd, 554, "5.6.6",
"IMAP URL resolution failed: "
"Inappropriate server port");
return -1;
}
/* retrieve URL */
if (imap_msgpart_url_create
(client->user, imap_url, &burl_cmd->url_fetch, &error) < 0) {
smtp_server_reply(cmd, 554, "5.6.6",
"IMAP URL resolution failed: %s", error);
return -1;
}
if (imap_msgpart_url_read_part(burl_cmd->url_fetch,
&result, &error) <= 0) {
smtp_server_reply(cmd, 554, "5.6.6",
"IMAP URL resolution failed: %s", error);
return -1;
}
return smtp_server_connection_data_chunk_add(cmd,
result.input, result.size, burl_cmd->chunk_last, FALSE);
}
static int
cmd_burl_fetch(struct cmd_burl_context *burl_cmd, const char *url,
struct imap_url *imap_url)
{
struct smtp_server_cmd_ctx *cmd = burl_cmd->cmd;
struct client *client = burl_cmd->client;
if (client->urlauth_ctx == NULL) {
/* RFC5248, Section 2.4:
554 5.7.14 Trust relationship required
The submission server requires a configured trust
relationship with a third-party server in order to access
the message content. This value replaces the prior use of
X.7.8 for this error condition, thereby updating [RFC4468].
*/
smtp_server_reply(cmd, 554, "5.7.14",
"No IMAP URLAUTH access available");
return -1;
}
/* urlauth */
burl_cmd->urlauth_fetch =
imap_urlauth_fetch_init(client->urlauth_ctx,
cmd_burl_fetch_cb, burl_cmd);
if (imap_urlauth_fetch_url_parsed(burl_cmd->urlauth_fetch,
url, imap_url, IMAP_URLAUTH_FETCH_FLAG_BODY) == 0) {
/* wait for URL fetch */
return 0;
}
return 1;
}
void cmd_burl(struct smtp_server_cmd_ctx *cmd, const char *params)
{
struct smtp_server_connection *conn = cmd->conn;
struct client *client = smtp_server_connection_get_context(conn);
struct cmd_burl_context *burl_cmd;
const char *const *argv;
enum imap_url_parse_flags url_parse_flags =
IMAP_URL_PARSE_ALLOW_URLAUTH;
struct imap_url *imap_url;
const char *url, *error;
bool chunk_last = FALSE;
int ret = 1;
smtp_server_connection_data_chunk_init(cmd);
/* burl-cmd = "BURL" SP absolute-URI [SP end-marker] CRLF
end-marker = "LAST"
*/
argv = t_strsplit(params, " ");
url = argv[0];
if (url == NULL) {
smtp_server_reply(cmd, 501, "5.5.4",
"Missing chunk URL parameter");
ret = -1;
} else if (imap_url_parse(url, NULL, url_parse_flags,
&imap_url, &error) < 0) {
smtp_server_reply(cmd, 501, "5.5.4",
"Invalid chunk URL: %s", error);
ret = -1;
} else if (argv[1] != NULL) {
if (strcasecmp(argv[1], "LAST") != 0) {
smtp_server_reply(cmd, 501, "5.5.4",
"Invalid end marker parameter");
ret = -1;
} else if (argv[2] != NULL) {
smtp_server_reply(cmd, 501, "5.5.4",
"Invalid parameters");
ret = -1;
} else {
chunk_last = TRUE;
}
}
if (ret < 0 || !smtp_server_connection_data_check_state(cmd))
return;
burl_cmd = p_new(cmd->pool, struct cmd_burl_context, 1);
burl_cmd->client = client;
burl_cmd->cmd = cmd;
burl_cmd->chunk_last = chunk_last;
cmd->context = burl_cmd;
cmd->hook_destroy = cmd_burl_destroy;
if (imap_url->uauth_rumpurl == NULL) {
/* direct local url */
ret = cmd_burl_fetch_trusted(burl_cmd, imap_url);
} else {
ret = cmd_burl_fetch(burl_cmd, url, imap_url);
}
if (ret == 0 && chunk_last)
smtp_server_command_input_lock(cmd);
}