TLS: do decent PIPELINING under TLS, at least with GnuTLS
[exim.git] / src / src / dkim_transport.c
CommitLineData
42055a33
JH
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
5/* Copyright (c) University of Cambridge 1995 - 2016 */
6/* See the file NOTICE for conditions of use and distribution. */
7
8/* Transport shim for dkim signing */
9
42055a33
JH
10
11#include "exim.h"
12
09d5060c
JH
13#ifndef DISABLE_DKIM /* rest of file */
14
42055a33
JH
15
16static BOOL
17dkt_sign_fail(struct ob_dkim * dkim, int * errp)
18{
19if (dkim->dkim_strict)
20 {
21 uschar * dkim_strict_result = expand_string(dkim->dkim_strict);
22
23 if (dkim_strict_result)
24 if ( (strcmpic(dkim->dkim_strict, US"1") == 0) ||
25 (strcmpic(dkim->dkim_strict, US"true") == 0) )
26 {
27 /* Set errno to something halfway meaningful */
28 *errp = EACCES;
29 log_write(0, LOG_MAIN, "DKIM: message could not be signed,"
30 " and dkim_strict is set. Deferring message delivery.");
31 return FALSE;
32 }
33 }
34return TRUE;
35}
36
d315eda1
JH
37/* Send the file at in_fd down the output fd */
38
42055a33
JH
39static BOOL
40dkt_send_file(int out_fd, int in_fd, off_t off, size_t size)
41{
6c140c01 42DEBUG(D_transport) debug_printf("send file fd=%d size=%u\n", out_fd, (unsigned)(size - off));
42055a33
JH
43
44/*XXX should implement timeout, like transport_write_block_fd() ? */
45
7d758a6a 46#ifdef OS_SENDFILE
42055a33
JH
47/* We can use sendfile() to shove the file contents
48 to the socket. However only if we don't use TLS,
49 as then there's another layer of indirection
50 before the data finally hits the socket. */
51if (tls_out.active != out_fd)
52 {
53 ssize_t copied = 0;
54
55 while(copied >= 0 && off < size)
7d758a6a 56 copied = os_sendfile(out_fd, in_fd, &off, size - off);
42055a33
JH
57 if (copied < 0)
58 return FALSE;
59 }
60else
61
62#endif
63
64 {
65 int sread, wwritten;
66
328c5688 67 /* Rewind file */
d315eda1 68 if (lseek(in_fd, off, SEEK_SET) < 0) return FALSE;
328c5688 69
42055a33 70 /* Send file down the original fd */
d315eda1 71 while((sread = read(in_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) > 0)
42055a33
JH
72 {
73 uschar * p = deliver_out_buffer;
74 /* write the chunk */
75
76 while (sread)
77 {
78#ifdef SUPPORT_TLS
79 wwritten = tls_out.active == out_fd
925ac8e4 80 ? tls_write(FALSE, p, sread, FALSE)
42055a33
JH
81 : write(out_fd, CS p, sread);
82#else
83 wwritten = write(out_fd, CS p, sread);
84#endif
85 if (wwritten == -1)
86 return FALSE;
87 p += wwritten;
88 sread -= wwritten;
89 }
90 }
91
92 if (sread == -1)
93 return FALSE;
94 }
95
96return TRUE;
97}
98
99
100
101
102/* This function is a wrapper around transport_write_message().
103 It is only called from the smtp transport if DKIM or Domainkeys support
104 is active and no transport filter is to be used.
105
106Arguments:
107 As for transport_write_message() in transort.c, with additional arguments
108 for DKIM.
109
110Returns: TRUE on success; FALSE (with errno) for any failure
111*/
112
113static BOOL
114dkt_direct(transport_ctx * tctx, struct ob_dkim * dkim,
115 const uschar ** err)
116{
117int save_fd = tctx->u.fd;
118int save_options = tctx->options;
328c5688 119BOOL save_wireformat = spool_file_wireformat;
42055a33 120uschar * hdrs, * dkim_signature;
d315eda1 121int siglen = 0, hsize;
42055a33
JH
122const uschar * errstr;
123BOOL rc;
124
125DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
126
328c5688
JH
127/* Get headers in string for signing and transmission. Do CRLF
128and dotstuffing (but no body nor dot-termination) */
42055a33
JH
129
130tctx->u.msg = NULL;
131tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat)
132 | topt_output_string | topt_no_body;
133
134rc = transport_write_message(tctx, 0);
135hdrs = tctx->u.msg;
136hdrs[hsize = tctx->msg_ptr] = '\0';
137
138tctx->u.fd = save_fd;
139tctx->options = save_options;
140if (!rc) return FALSE;
141
142/* Get signatures for headers plus spool data file */
143
144dkim->dot_stuffed = !!(save_options & topt_end_dot);
145
146if ((dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET,
147 hdrs, dkim, &errstr)))
148 siglen = Ustrlen(dkim_signature);
149else if (!(rc = dkt_sign_fail(dkim, &errno)))
150 {
151 *err = errstr;
152 return FALSE;
153 }
154
155/* Write the signature and headers into the deliver-out-buffer. This should
156mean they go out in the same packet as the MAIL, RCPT and (first) BDAT commands
157(transport_write_message() sizes the BDAT for the buffered amount) - for short
328c5688
JH
158messages, the BDAT LAST command. We want no dotstuffing expansion here, it
159having already been done - but we have to say we want CRLF output format, and
160temporarily set the marker for possible already-CRLF input. */
42055a33 161
328c5688
JH
162tctx->options &= ~topt_escape_headers;
163spool_file_wireformat = TRUE;
42055a33 164transport_write_reset(0);
d315eda1 165if ( siglen > 0 && !write_chunk(tctx, dkim_signature, siglen)
42055a33
JH
166 || !write_chunk(tctx, hdrs, hsize))
167 return FALSE;
168
328c5688 169spool_file_wireformat = save_wireformat;
42055a33
JH
170tctx->options = save_options | topt_no_headers | topt_continuation;
171
172if (!(transport_write_message(tctx, 0)))
173 return FALSE;
174
175tctx->options = save_options;
176return TRUE;
177}
178
179
180/* This function is a wrapper around transport_write_message().
181 It is only called from the smtp transport if DKIM or Domainkeys support
182 is active and a transport filter is to be used. The function sets up a
183 replacement fd into a -K file, then calls the normal function. This way, the
184 exact bits that exim would have put "on the wire" will end up in the file
185 (except for TLS encapsulation, which is the very very last thing). When we
186 are done signing the file, send the signed message down the original fd (or
187 TLS fd).
188
189Arguments:
190 As for transport_write_message() in transort.c, with additional arguments
191 for DKIM.
192
193Returns: TRUE on success; FALSE (with errno) for any failure
194*/
195
196static BOOL
197dkt_via_kfile(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err)
198{
199int dkim_fd;
200int save_errno = 0;
201BOOL rc;
202uschar * dkim_spool_name, * dkim_signature;
6c140c01 203int sread = 0, wwritten = 0, siglen = 0, options;
42055a33
JH
204off_t k_file_size;
205const uschar * errstr;
206
207dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
208 string_sprintf("-%d-K", (int)getpid()));
209
210DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
211
212if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0)
213 {
214 /* Can't create spool file. Ugh. */
215 rc = FALSE;
216 save_errno = errno;
217 *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
218 goto CLEANUP;
219 }
220
221/* Call transport utility function to write the -K file; does the CRLF expansion
222(but, in the CHUNKING case, neither dot-stuffing nor dot-termination). */
223
224 {
225 int save_fd = tctx->u.fd;
226 tctx->u.fd = dkim_fd;
227 options = tctx->options;
228 tctx->options &= ~topt_use_bdat;
229
230 rc = transport_write_message(tctx, 0);
231
232 tctx->u.fd = save_fd;
233 tctx->options = options;
234 }
235
236/* Save error state. We must clean up before returning. */
237if (!rc)
238 {
239 save_errno = errno;
240 goto CLEANUP;
241 }
242
243/* Feed the file to the goats^W DKIM lib */
244
245dkim->dot_stuffed = !!(options & topt_end_dot);
246if ((dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
247 siglen = Ustrlen(dkim_signature);
248else if (!(rc = dkt_sign_fail(dkim, &save_errno)))
249 {
250 *err = errstr;
251 goto CLEANUP;
252 }
253
7d758a6a 254#ifndef OS_SENDFILE
42055a33
JH
255if (options & topt_use_bdat)
256#endif
d315eda1
JH
257 if ((k_file_size = lseek(dkim_fd, 0, SEEK_END)) < 0)
258 {
259 *err = string_sprintf("dkim spoolfile seek: %s", strerror(errno));
260 goto CLEANUP;
261 }
42055a33
JH
262
263if (options & topt_use_bdat)
264 {
265 /* On big messages output a precursor chunk to get any pipelined
266 MAIL & RCPT commands flushed, then reap the responses so we can
267 error out on RCPT rejects before sending megabytes. */
268
269 if (siglen + k_file_size > DELIVER_OUT_BUFFER_SIZE && siglen > 0)
270 {
271 if ( tctx->chunk_cb(tctx, siglen, 0) != OK
272 || !transport_write_block(tctx, dkim_signature, siglen, FALSE)
273 || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
274 )
275 goto err;
276 siglen = 0;
277 }
278
279 /* Send the BDAT command for the entire message, as a single LAST-marked
280 chunk. */
281
282 if (tctx->chunk_cb(tctx, siglen + k_file_size, tc_chunk_last) != OK)
283 goto err;
284 }
285
286if(siglen > 0 && !transport_write_block(tctx, dkim_signature, siglen, TRUE))
287 goto err;
288
289if (!dkt_send_file(tctx->u.fd, dkim_fd, 0, k_file_size))
290 {
291 save_errno = errno;
292 rc = FALSE;
293 }
294
295CLEANUP:
296 /* unlink -K file */
d315eda1 297 if (dkim_fd >= 0) (void)close(dkim_fd);
42055a33
JH
298 Uunlink(dkim_spool_name);
299 errno = save_errno;
300 return rc;
301
302err:
303 save_errno = errno;
304 rc = FALSE;
305 goto CLEANUP;
306}
307
308
309
310/***************************************************************************************************
311* External interface to write the message, while signing it with DKIM and/or Domainkeys *
312***************************************************************************************************/
313
314/* This function is a wrapper around transport_write_message().
315 It is only called from the smtp transport if DKIM or Domainkeys support
316 is compiled in.
317
318Arguments:
319 As for transport_write_message() in transort.c, with additional arguments
320 for DKIM.
321
322Returns: TRUE on success; FALSE (with errno) for any failure
323*/
324
325BOOL
326dkim_transport_write_message(transport_ctx * tctx,
327 struct ob_dkim * dkim, const uschar ** err)
328{
329/* If we can't sign, just call the original function. */
330
331if (!(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector))
332 return transport_write_message(tctx, 0);
333
334/* If there is no filter command set up, construct the message and calculate
335a dkim signature of it, send the signature and a reconstructed message. This
336avoids using a temprary file. */
337
338if ( !transport_filter_argv
339 || !*transport_filter_argv
340 || !**transport_filter_argv
341 )
342 return dkt_direct(tctx, dkim, err);
343
344/* Use the transport path to write a file, calculate a dkim signature,
345send the signature and then send the file. */
346
347return dkt_via_kfile(tctx, dkim, err);
348}
349
350#endif /* whole file */
351
352/* vi: aw ai sw=2
353*/
354/* End of dkim_transport.c */