Fix dkim_strict expansion. Bug 2413
[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)
9fa4d5b4
RJ
24 if ( strcmpic(dkim_strict_result, US"1") == 0
25 || strcmpic(dkim_strict_result, US"true") == 0)
42055a33
JH
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. */
74f1a423 59if (tls_out.active.sock != out_fd)
42055a33
JH
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 {
01603eec 86#ifndef DISABLE_TLS
74f1a423
JH
87 wwritten = tls_out.active.sock == out_fd
88 ? tls_write(tls_out.active.tls_ctx, 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;
8768d548 127BOOL save_wireformat = f.spool_file_wireformat;
9e70917d 128uschar * hdrs;
acec9514 129gstring * dkim_signature;
9e70917d 130int hsize;
42055a33
JH
131const uschar * errstr;
132BOOL rc;
133
134DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
135
328c5688
JH
136/* Get headers in string for signing and transmission. Do CRLF
137and dotstuffing (but no body nor dot-termination) */
42055a33
JH
138
139tctx->u.msg = NULL;
140tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat)
141 | topt_output_string | topt_no_body;
142
143rc = transport_write_message(tctx, 0);
acec9514
JH
144hdrs = string_from_gstring(tctx->u.msg);
145hsize = tctx->u.msg->ptr;
42055a33
JH
146
147tctx->u.fd = save_fd;
148tctx->options = save_options;
149if (!rc) return FALSE;
150
151/* Get signatures for headers plus spool data file */
152
b3d9ebf5
JH
153#ifdef EXPERIMENTAL_ARC
154arc_sign_init();
155#endif
42055a33 156
49e56fb3
JH
157/* The dotstuffed status of the datafile depends on whether it was stored
158in wireformat. */
159
8768d548 160dkim->dot_stuffed = f.spool_file_wireformat;
9e70917d 161if (!(dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET,
42055a33 162 hdrs, dkim, &errstr)))
9e70917d
JH
163 if (!(rc = dkt_sign_fail(dkim, &errno)))
164 {
165 *err = errstr;
166 return FALSE;
167 }
42055a33 168
617d3932
JH
169#ifdef EXPERIMENTAL_ARC
170if (dkim->arc_signspec) /* Prepend ARC headers */
0e8aed8a
JH
171 {
172 uschar * e;
173 if (!(dkim_signature = arc_sign(dkim->arc_signspec, dkim_signature, &e)))
617d3932 174 {
0e8aed8a 175 *err = e;
617d3932
JH
176 return FALSE;
177 }
0e8aed8a 178 }
617d3932
JH
179#endif
180
42055a33
JH
181/* Write the signature and headers into the deliver-out-buffer. This should
182mean they go out in the same packet as the MAIL, RCPT and (first) BDAT commands
183(transport_write_message() sizes the BDAT for the buffered amount) - for short
328c5688
JH
184messages, the BDAT LAST command. We want no dotstuffing expansion here, it
185having already been done - but we have to say we want CRLF output format, and
186temporarily set the marker for possible already-CRLF input. */
42055a33 187
328c5688 188tctx->options &= ~topt_escape_headers;
8768d548 189f.spool_file_wireformat = TRUE;
42055a33 190transport_write_reset(0);
9e70917d 191if ( ( dkim_signature
acec9514
JH
192 && dkim_signature->ptr > 0
193 && !write_chunk(tctx, dkim_signature->s, dkim_signature->ptr)
9e70917d
JH
194 )
195 || !write_chunk(tctx, hdrs, hsize)
196 )
42055a33
JH
197 return FALSE;
198
8768d548 199f.spool_file_wireformat = save_wireformat;
42055a33
JH
200tctx->options = save_options | topt_no_headers | topt_continuation;
201
202if (!(transport_write_message(tctx, 0)))
203 return FALSE;
204
205tctx->options = save_options;
206return TRUE;
207}
208
209
210/* This function is a wrapper around transport_write_message().
211 It is only called from the smtp transport if DKIM or Domainkeys support
212 is active and a transport filter is to be used. The function sets up a
213 replacement fd into a -K file, then calls the normal function. This way, the
214 exact bits that exim would have put "on the wire" will end up in the file
215 (except for TLS encapsulation, which is the very very last thing). When we
216 are done signing the file, send the signed message down the original fd (or
217 TLS fd).
218
219Arguments:
220 As for transport_write_message() in transort.c, with additional arguments
221 for DKIM.
222
223Returns: TRUE on success; FALSE (with errno) for any failure
224*/
225
226static BOOL
227dkt_via_kfile(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err)
228{
229int dkim_fd;
230int save_errno = 0;
231BOOL rc;
9e70917d 232uschar * dkim_spool_name;
acec9514 233gstring * dkim_signature;
9e70917d 234int options, dlen;
42055a33
JH
235off_t k_file_size;
236const uschar * errstr;
237
238dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
239 string_sprintf("-%d-K", (int)getpid()));
240
241DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
242
243if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0)
244 {
245 /* Can't create spool file. Ugh. */
246 rc = FALSE;
247 save_errno = errno;
248 *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
249 goto CLEANUP;
250 }
251
252/* Call transport utility function to write the -K file; does the CRLF expansion
253(but, in the CHUNKING case, neither dot-stuffing nor dot-termination). */
254
255 {
256 int save_fd = tctx->u.fd;
257 tctx->u.fd = dkim_fd;
258 options = tctx->options;
259 tctx->options &= ~topt_use_bdat;
260
261 rc = transport_write_message(tctx, 0);
262
263 tctx->u.fd = save_fd;
264 tctx->options = options;
265 }
266
267/* Save error state. We must clean up before returning. */
268if (!rc)
269 {
270 save_errno = errno;
271 goto CLEANUP;
272 }
273
b3d9ebf5
JH
274#ifdef EXPERIMENTAL_ARC
275arc_sign_init();
276#endif
277
49e56fb3
JH
278/* Feed the file to the goats^W DKIM lib. At this point the dotstuffed
279status of the file depends on the output of transport_write_message() just
280above, which should be the result of the end_dot flag in tctx->options. */
42055a33
JH
281
282dkim->dot_stuffed = !!(options & topt_end_dot);
9e70917d 283if (!(dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
42055a33 284 {
9e70917d
JH
285 dlen = 0;
286 if (!(rc = dkt_sign_fail(dkim, &save_errno)))
287 {
288 *err = errstr;
289 goto CLEANUP;
290 }
42055a33 291 }
9e70917d 292else
acec9514 293 dlen = dkim_signature->ptr;
42055a33 294
617d3932
JH
295#ifdef EXPERIMENTAL_ARC
296if (dkim->arc_signspec) /* Prepend ARC headers */
297 {
298 if (!(dkim_signature = arc_sign(dkim->arc_signspec, dkim_signature, USS err)))
299 goto CLEANUP;
300 dlen = dkim_signature->ptr;
301 }
302#endif
303
7d758a6a 304#ifndef OS_SENDFILE
42055a33
JH
305if (options & topt_use_bdat)
306#endif
d315eda1
JH
307 if ((k_file_size = lseek(dkim_fd, 0, SEEK_END)) < 0)
308 {
309 *err = string_sprintf("dkim spoolfile seek: %s", strerror(errno));
310 goto CLEANUP;
311 }
42055a33
JH
312
313if (options & topt_use_bdat)
314 {
315 /* On big messages output a precursor chunk to get any pipelined
316 MAIL & RCPT commands flushed, then reap the responses so we can
317 error out on RCPT rejects before sending megabytes. */
318
9e70917d
JH
319 if ( dlen + k_file_size > DELIVER_OUT_BUFFER_SIZE
320 && dlen > 0)
42055a33 321 {
9e70917d
JH
322 if ( tctx->chunk_cb(tctx, dlen, 0) != OK
323 || !transport_write_block(tctx,
acec9514 324 dkim_signature->s, dlen, FALSE)
42055a33
JH
325 || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
326 )
327 goto err;
9e70917d 328 dlen = 0;
42055a33
JH
329 }
330
331 /* Send the BDAT command for the entire message, as a single LAST-marked
332 chunk. */
333
9e70917d 334 if (tctx->chunk_cb(tctx, dlen + k_file_size, tc_chunk_last) != OK)
42055a33
JH
335 goto err;
336 }
337
acec9514 338if(dlen > 0 && !transport_write_block(tctx, dkim_signature->s, dlen, TRUE))
42055a33
JH
339 goto err;
340
bb07bcd3
JH
341if (!dkt_send_file(tctx->u.fd, dkim_fd, 0
342#ifdef OS_SENDFILE
343 , k_file_size
344#endif
345 ))
42055a33
JH
346 {
347 save_errno = errno;
348 rc = FALSE;
349 }
350
351CLEANUP:
352 /* unlink -K file */
d315eda1 353 if (dkim_fd >= 0) (void)close(dkim_fd);
42055a33
JH
354 Uunlink(dkim_spool_name);
355 errno = save_errno;
356 return rc;
357
358err:
359 save_errno = errno;
360 rc = FALSE;
361 goto CLEANUP;
362}
363
364
365
366/***************************************************************************************************
367* External interface to write the message, while signing it with DKIM and/or Domainkeys *
368***************************************************************************************************/
369
370/* This function is a wrapper around transport_write_message().
371 It is only called from the smtp transport if DKIM or Domainkeys support
372 is compiled in.
373
374Arguments:
375 As for transport_write_message() in transort.c, with additional arguments
376 for DKIM.
377
378Returns: TRUE on success; FALSE (with errno) for any failure
379*/
380
381BOOL
382dkim_transport_write_message(transport_ctx * tctx,
383 struct ob_dkim * dkim, const uschar ** err)
384{
385/* If we can't sign, just call the original function. */
386
617d3932
JH
387if ( !(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector)
388 && !dkim->force_bodyhash)
42055a33
JH
389 return transport_write_message(tctx, 0);
390
391/* If there is no filter command set up, construct the message and calculate
392a dkim signature of it, send the signature and a reconstructed message. This
393avoids using a temprary file. */
394
395if ( !transport_filter_argv
396 || !*transport_filter_argv
397 || !**transport_filter_argv
398 )
399 return dkt_direct(tctx, dkim, err);
400
401/* Use the transport path to write a file, calculate a dkim signature,
402send the signature and then send the file. */
403
404return dkt_via_kfile(tctx, dkim, err);
405}
406
407#endif /* whole file */
408
409/* vi: aw ai sw=2
410*/
411/* End of dkim_transport.c */