DKIM: support multiple signing, by selector
[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;
9e70917d
JH
120uschar * hdrs;
121blob * dkim_signature;
122int hsize;
42055a33
JH
123const uschar * errstr;
124BOOL rc;
125
126DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
127
328c5688
JH
128/* Get headers in string for signing and transmission. Do CRLF
129and dotstuffing (but no body nor dot-termination) */
42055a33
JH
130
131tctx->u.msg = NULL;
132tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat)
133 | topt_output_string | topt_no_body;
134
135rc = transport_write_message(tctx, 0);
136hdrs = tctx->u.msg;
137hdrs[hsize = tctx->msg_ptr] = '\0';
138
139tctx->u.fd = save_fd;
140tctx->options = save_options;
141if (!rc) return FALSE;
142
143/* Get signatures for headers plus spool data file */
144
145dkim->dot_stuffed = !!(save_options & topt_end_dot);
146
9e70917d 147if (!(dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET,
42055a33 148 hdrs, dkim, &errstr)))
9e70917d
JH
149 if (!(rc = dkt_sign_fail(dkim, &errno)))
150 {
151 *err = errstr;
152 return FALSE;
153 }
42055a33
JH
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);
9e70917d
JH
165if ( ( dkim_signature
166 && dkim_signature->len > 0
167 && !write_chunk(tctx, dkim_signature->data, dkim_signature->len)
168 )
169 || !write_chunk(tctx, hdrs, hsize)
170 )
42055a33
JH
171 return FALSE;
172
328c5688 173spool_file_wireformat = save_wireformat;
42055a33
JH
174tctx->options = save_options | topt_no_headers | topt_continuation;
175
176if (!(transport_write_message(tctx, 0)))
177 return FALSE;
178
179tctx->options = save_options;
180return TRUE;
181}
182
183
184/* This function is a wrapper around transport_write_message().
185 It is only called from the smtp transport if DKIM or Domainkeys support
186 is active and a transport filter is to be used. The function sets up a
187 replacement fd into a -K file, then calls the normal function. This way, the
188 exact bits that exim would have put "on the wire" will end up in the file
189 (except for TLS encapsulation, which is the very very last thing). When we
190 are done signing the file, send the signed message down the original fd (or
191 TLS fd).
192
193Arguments:
194 As for transport_write_message() in transort.c, with additional arguments
195 for DKIM.
196
197Returns: TRUE on success; FALSE (with errno) for any failure
198*/
199
200static BOOL
201dkt_via_kfile(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err)
202{
203int dkim_fd;
204int save_errno = 0;
205BOOL rc;
9e70917d
JH
206uschar * dkim_spool_name;
207blob * dkim_signature;
208int options, dlen;
42055a33
JH
209off_t k_file_size;
210const uschar * errstr;
211
212dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
213 string_sprintf("-%d-K", (int)getpid()));
214
215DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
216
217if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0)
218 {
219 /* Can't create spool file. Ugh. */
220 rc = FALSE;
221 save_errno = errno;
222 *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
223 goto CLEANUP;
224 }
225
226/* Call transport utility function to write the -K file; does the CRLF expansion
227(but, in the CHUNKING case, neither dot-stuffing nor dot-termination). */
228
229 {
230 int save_fd = tctx->u.fd;
231 tctx->u.fd = dkim_fd;
232 options = tctx->options;
233 tctx->options &= ~topt_use_bdat;
234
235 rc = transport_write_message(tctx, 0);
236
237 tctx->u.fd = save_fd;
238 tctx->options = options;
239 }
240
241/* Save error state. We must clean up before returning. */
242if (!rc)
243 {
244 save_errno = errno;
245 goto CLEANUP;
246 }
247
248/* Feed the file to the goats^W DKIM lib */
249
250dkim->dot_stuffed = !!(options & topt_end_dot);
9e70917d 251if (!(dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
42055a33 252 {
9e70917d
JH
253 dlen = 0;
254 if (!(rc = dkt_sign_fail(dkim, &save_errno)))
255 {
256 *err = errstr;
257 goto CLEANUP;
258 }
42055a33 259 }
9e70917d
JH
260else
261 dlen = dkim_signature->len;
42055a33 262
7d758a6a 263#ifndef OS_SENDFILE
42055a33
JH
264if (options & topt_use_bdat)
265#endif
d315eda1
JH
266 if ((k_file_size = lseek(dkim_fd, 0, SEEK_END)) < 0)
267 {
268 *err = string_sprintf("dkim spoolfile seek: %s", strerror(errno));
269 goto CLEANUP;
270 }
42055a33
JH
271
272if (options & topt_use_bdat)
273 {
274 /* On big messages output a precursor chunk to get any pipelined
275 MAIL & RCPT commands flushed, then reap the responses so we can
276 error out on RCPT rejects before sending megabytes. */
277
9e70917d
JH
278 if ( dlen + k_file_size > DELIVER_OUT_BUFFER_SIZE
279 && dlen > 0)
42055a33 280 {
9e70917d
JH
281 if ( tctx->chunk_cb(tctx, dlen, 0) != OK
282 || !transport_write_block(tctx,
283 dkim_signature->data, dlen, FALSE)
42055a33
JH
284 || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
285 )
286 goto err;
9e70917d 287 dlen = 0;
42055a33
JH
288 }
289
290 /* Send the BDAT command for the entire message, as a single LAST-marked
291 chunk. */
292
9e70917d 293 if (tctx->chunk_cb(tctx, dlen + k_file_size, tc_chunk_last) != OK)
42055a33
JH
294 goto err;
295 }
296
9e70917d 297if(dlen > 0 && !transport_write_block(tctx, dkim_signature->data, dlen, TRUE))
42055a33
JH
298 goto err;
299
300if (!dkt_send_file(tctx->u.fd, dkim_fd, 0, k_file_size))
301 {
302 save_errno = errno;
303 rc = FALSE;
304 }
305
306CLEANUP:
307 /* unlink -K file */
d315eda1 308 if (dkim_fd >= 0) (void)close(dkim_fd);
42055a33
JH
309 Uunlink(dkim_spool_name);
310 errno = save_errno;
311 return rc;
312
313err:
314 save_errno = errno;
315 rc = FALSE;
316 goto CLEANUP;
317}
318
319
320
321/***************************************************************************************************
322* External interface to write the message, while signing it with DKIM and/or Domainkeys *
323***************************************************************************************************/
324
325/* This function is a wrapper around transport_write_message().
326 It is only called from the smtp transport if DKIM or Domainkeys support
327 is compiled in.
328
329Arguments:
330 As for transport_write_message() in transort.c, with additional arguments
331 for DKIM.
332
333Returns: TRUE on success; FALSE (with errno) for any failure
334*/
335
336BOOL
337dkim_transport_write_message(transport_ctx * tctx,
338 struct ob_dkim * dkim, const uschar ** err)
339{
340/* If we can't sign, just call the original function. */
341
342if (!(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector))
343 return transport_write_message(tctx, 0);
344
345/* If there is no filter command set up, construct the message and calculate
346a dkim signature of it, send the signature and a reconstructed message. This
347avoids using a temprary file. */
348
349if ( !transport_filter_argv
350 || !*transport_filter_argv
351 || !**transport_filter_argv
352 )
353 return dkt_direct(tctx, dkim, err);
354
355/* Use the transport path to write a file, calculate a dkim signature,
356send the signature and then send the file. */
357
358return dkt_via_kfile(tctx, dkim, err);
359}
360
361#endif /* whole file */
362
363/* vi: aw ai sw=2
364*/
365/* End of dkim_transport.c */