String handling: refactor the expanding-string routines and users to use a descriptor...
[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
16 static BOOL
17 dkt_sign_fail(struct ob_dkim * dkim, int * errp)
18 {
19 if (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   }
34 return TRUE;
35 }
36
37 /* Send the file at in_fd down the output fd */
38
39 static BOOL
40 dkt_send_file(int out_fd, int in_fd, off_t off, size_t size)
41 {
42 DEBUG(D_transport) debug_printf("send file fd=%d size=%u\n", out_fd, (unsigned)(size - off));
43
44 /*XXX should implement timeout, like transport_write_block_fd() ? */
45
46 #ifdef OS_SENDFILE
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. */
51 if (tls_out.active != out_fd)
52   {
53   ssize_t copied = 0;
54
55   while(copied >= 0 && off < size)
56     copied = os_sendfile(out_fd, in_fd, &off, size - off);
57   if (copied < 0)
58     return FALSE;
59   }
60 else
61
62 #endif
63
64   {
65   int sread, wwritten;
66
67   /* Rewind file */
68   if (lseek(in_fd, off, SEEK_SET) < 0) return FALSE;
69
70   /* Send file down the original fd */
71   while((sread = read(in_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) > 0)
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
80         ? tls_write(FALSE, p, sread, FALSE)
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
96 return 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
106 Arguments:
107   As for transport_write_message() in transort.c, with additional arguments
108   for DKIM.
109
110 Returns:       TRUE on success; FALSE (with errno) for any failure
111 */
112
113 static BOOL
114 dkt_direct(transport_ctx * tctx, struct ob_dkim * dkim,
115   const uschar ** err)
116 {
117 int save_fd = tctx->u.fd;
118 int save_options = tctx->options;
119 BOOL save_wireformat = spool_file_wireformat;
120 uschar * hdrs;
121 gstring * dkim_signature;
122 int hsize;
123 const uschar * errstr;
124 BOOL rc;
125
126 DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
127
128 /* Get headers in string for signing and transmission.  Do CRLF
129 and dotstuffing (but no body nor dot-termination) */
130
131 tctx->u.msg = NULL;
132 tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat)
133   | topt_output_string | topt_no_body;
134
135 rc = transport_write_message(tctx, 0);
136 hdrs = string_from_gstring(tctx->u.msg);
137 hsize = tctx->u.msg->ptr;
138
139 tctx->u.fd = save_fd;
140 tctx->options = save_options;
141 if (!rc) return FALSE;
142
143 /* Get signatures for headers plus spool data file */
144
145 dkim->dot_stuffed = !!(save_options & topt_end_dot);
146
147 if (!(dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET,
148                                     hdrs, dkim, &errstr)))
149   if (!(rc = dkt_sign_fail(dkim, &errno)))
150     {
151     *err = errstr;
152     return FALSE;
153     }
154
155 /* Write the signature and headers into the deliver-out-buffer.  This should
156 mean 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
158 messages, the BDAT LAST command.  We want no dotstuffing expansion here, it
159 having already been done - but we have to say we want CRLF output format, and
160 temporarily set the marker for possible already-CRLF input. */
161
162 tctx->options &= ~topt_escape_headers;
163 spool_file_wireformat = TRUE;
164 transport_write_reset(0);
165 if (  (  dkim_signature
166       && dkim_signature->ptr > 0
167       && !write_chunk(tctx, dkim_signature->s, dkim_signature->ptr)
168       )
169    || !write_chunk(tctx, hdrs, hsize)
170    )
171   return FALSE;
172
173 spool_file_wireformat = save_wireformat;
174 tctx->options = save_options | topt_no_headers | topt_continuation;
175
176 if (!(transport_write_message(tctx, 0)))
177   return FALSE;
178
179 tctx->options = save_options;
180 return 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
193 Arguments:
194   As for transport_write_message() in transort.c, with additional arguments
195   for DKIM.
196
197 Returns:       TRUE on success; FALSE (with errno) for any failure
198 */
199
200 static BOOL
201 dkt_via_kfile(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err)
202 {
203 int dkim_fd;
204 int save_errno = 0;
205 BOOL rc;
206 uschar * dkim_spool_name;
207 gstring * dkim_signature;
208 int options, dlen;
209 off_t k_file_size;
210 const uschar * errstr;
211
212 dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
213                     string_sprintf("-%d-K", (int)getpid()));
214
215 DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
216
217 if ((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. */
242 if (!rc)
243   {
244   save_errno = errno;
245   goto CLEANUP;
246   }
247
248 /* Feed the file to the goats^W DKIM lib */
249
250 dkim->dot_stuffed = !!(options & topt_end_dot);
251 if (!(dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
252   {
253   dlen = 0;
254   if (!(rc = dkt_sign_fail(dkim, &save_errno)))
255     {
256     *err = errstr;
257     goto CLEANUP;
258     }
259   }
260 else
261   dlen = dkim_signature->ptr;
262
263 #ifndef OS_SENDFILE
264 if (options & topt_use_bdat)
265 #endif
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     }
271
272 if (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
278   if (  dlen + k_file_size > DELIVER_OUT_BUFFER_SIZE
279      && dlen > 0)
280     {
281     if (  tctx->chunk_cb(tctx, dlen, 0) != OK
282        || !transport_write_block(tctx,
283                     dkim_signature->s, dlen, FALSE)
284        || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
285        )
286       goto err;
287     dlen = 0;
288     }
289
290   /* Send the BDAT command for the entire message, as a single LAST-marked
291   chunk. */
292
293   if (tctx->chunk_cb(tctx, dlen + k_file_size, tc_chunk_last) != OK)
294     goto err;
295   }
296
297 if(dlen > 0 && !transport_write_block(tctx, dkim_signature->s, dlen, TRUE))
298   goto err;
299
300 if (!dkt_send_file(tctx->u.fd, dkim_fd, 0, k_file_size))
301   {
302   save_errno = errno;
303   rc = FALSE;
304   }
305
306 CLEANUP:
307   /* unlink -K file */
308   if (dkim_fd >= 0) (void)close(dkim_fd);
309   Uunlink(dkim_spool_name);
310   errno = save_errno;
311   return rc;
312
313 err:
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
329 Arguments:
330   As for transport_write_message() in transort.c, with additional arguments
331   for DKIM.
332
333 Returns:       TRUE on success; FALSE (with errno) for any failure
334 */
335
336 BOOL
337 dkim_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
342 if (!(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
346 a dkim signature of it, send the signature and a reconstructed message. This
347 avoids using a temprary file. */
348
349 if (  !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,
356 send the signature and then send the file. */
357
358 return 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 */