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