tidying: coverity
[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 /* Send the file at in_fd down the output fd */
42
43 static BOOL
44 dkt_send_file(int out_fd, int in_fd, off_t off, size_t size)
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 HAVE_LINUX_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 = 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)
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, * dkim_signature;
125 int siglen = 0, hsize;
126 const uschar * errstr;
127 BOOL rc;
128
129 DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
130
131 /* Get headers in string for signing and transmission.  Do CRLF
132 and dotstuffing (but no body nor dot-termination) */
133
134 tctx->u.msg = NULL;
135 tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat)
136   | topt_output_string | topt_no_body;
137
138 rc = transport_write_message(tctx, 0);
139 hdrs = tctx->u.msg;
140 hdrs[hsize = tctx->msg_ptr] = '\0';
141
142 tctx->u.fd = save_fd;
143 tctx->options = save_options;
144 if (!rc) return FALSE;
145
146 /* Get signatures for headers plus spool data file */
147
148 dkim->dot_stuffed = !!(save_options & topt_end_dot);
149
150 if ((dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET,
151                                     hdrs, dkim, &errstr)))
152   siglen = Ustrlen(dkim_signature);
153 else 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 (  siglen > 0 && !write_chunk(tctx, dkim_signature, siglen)
170    || !write_chunk(tctx, hdrs, hsize))
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, * dkim_signature;
207 int sread = 0, wwritten = 0, siglen = 0, options;
208 off_t k_file_size;
209 const uschar * errstr;
210
211 dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
212                     string_sprintf("-%d-K", (int)getpid()));
213
214 DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
215
216 if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0)
217   {
218   /* Can't create spool file. Ugh. */
219   rc = FALSE;
220   save_errno = errno;
221   *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
222   goto CLEANUP;
223   }
224
225 /* Call transport utility function to write the -K file; does the CRLF expansion
226 (but, in the CHUNKING case, neither dot-stuffing nor dot-termination). */
227
228   {
229   int save_fd = tctx->u.fd;
230   tctx->u.fd = dkim_fd;
231   options = tctx->options;
232   tctx->options &= ~topt_use_bdat;
233
234   rc = transport_write_message(tctx, 0);
235
236   tctx->u.fd = save_fd;
237   tctx->options = options;
238   }
239
240 /* Save error state. We must clean up before returning. */
241 if (!rc)
242   {
243   save_errno = errno;
244   goto CLEANUP;
245   }
246
247 /* Feed the file to the goats^W DKIM lib */
248
249 dkim->dot_stuffed = !!(options & topt_end_dot);
250 if ((dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
251   siglen = Ustrlen(dkim_signature);
252 else if (!(rc = dkt_sign_fail(dkim, &save_errno)))
253   {
254   *err = errstr;
255   goto CLEANUP;
256   }
257
258 #ifndef HAVE_LINUX_SENDFILE
259 if (options & topt_use_bdat)
260 #endif
261   if ((k_file_size = lseek(dkim_fd, 0, SEEK_END)) < 0)
262     {
263     *err = string_sprintf("dkim spoolfile seek: %s", strerror(errno));
264     goto CLEANUP;
265     }
266
267 if (options & topt_use_bdat)
268   {
269   /* On big messages output a precursor chunk to get any pipelined
270   MAIL & RCPT commands flushed, then reap the responses so we can
271   error out on RCPT rejects before sending megabytes. */
272
273   if (siglen + k_file_size > DELIVER_OUT_BUFFER_SIZE && siglen > 0)
274     {
275     if (  tctx->chunk_cb(tctx, siglen, 0) != OK
276        || !transport_write_block(tctx, dkim_signature, siglen, FALSE)
277        || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
278        )
279       goto err;
280     siglen = 0;
281     }
282
283   /* Send the BDAT command for the entire message, as a single LAST-marked
284   chunk. */
285
286   if (tctx->chunk_cb(tctx, siglen + k_file_size, tc_chunk_last) != OK)
287     goto err;
288   }
289
290 if(siglen > 0 && !transport_write_block(tctx, dkim_signature, siglen, TRUE))
291   goto err;
292
293 if (!dkt_send_file(tctx->u.fd, dkim_fd, 0, k_file_size))
294   {
295   save_errno = errno;
296   rc = FALSE;
297   }
298
299 CLEANUP:
300   /* unlink -K file */
301   if (dkim_fd >= 0) (void)close(dkim_fd);
302   Uunlink(dkim_spool_name);
303   errno = save_errno;
304   return rc;
305
306 err:
307   save_errno = errno;
308   rc = FALSE;
309   goto CLEANUP;
310 }
311
312
313
314 /***************************************************************************************************
315 *    External interface to write the message, while signing it with DKIM and/or Domainkeys         *
316 ***************************************************************************************************/
317
318 /* This function is a wrapper around transport_write_message().
319    It is only called from the smtp transport if DKIM or Domainkeys support
320    is compiled in.
321
322 Arguments:
323   As for transport_write_message() in transort.c, with additional arguments
324   for DKIM.
325
326 Returns:       TRUE on success; FALSE (with errno) for any failure
327 */
328
329 BOOL
330 dkim_transport_write_message(transport_ctx * tctx,
331   struct ob_dkim * dkim, const uschar ** err)
332 {
333 /* If we can't sign, just call the original function. */
334
335 if (!(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector))
336   return transport_write_message(tctx, 0);
337
338 /* If there is no filter command set up, construct the message and calculate
339 a dkim signature of it, send the signature and a reconstructed message. This
340 avoids using a temprary file. */
341
342 if (  !transport_filter_argv
343    || !*transport_filter_argv
344    || !**transport_filter_argv
345    )
346   return dkt_direct(tctx, dkim, err);
347
348 /* Use the transport path to write a file, calculate a dkim signature,
349 send the signature and then send the file. */
350
351 return dkt_via_kfile(tctx, dkim, err);
352 }
353
354 #endif  /* whole file */
355
356 /* vi: aw ai sw=2
357 */
358 /* End of dkim_transport.c */