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