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