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