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