tidying: coverity
[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 15#ifdef HAVE_LINUX_SENDFILE
09d5060c 16# include <sys/sendfile.h>
42055a33
JH
17#endif
18
19
20static BOOL
21dkt_sign_fail(struct ob_dkim * dkim, int * errp)
22{
23if (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 }
38return TRUE;
39}
40
d315eda1
JH
41/* Send the file at in_fd down the output fd */
42
42055a33
JH
43static BOOL
44dkt_send_file(int out_fd, int in_fd, off_t off, size_t size)
45{
6c140c01 46DEBUG(D_transport) debug_printf("send file fd=%d size=%u\n", out_fd, (unsigned)(size - off));
42055a33
JH
47
48/*XXX should implement timeout, like transport_write_block_fd() ? */
49
42055a33
JH
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. */
55if (tls_out.active != out_fd)
56 {
57 ssize_t copied = 0;
58
59 while(copied >= 0 && off < size)
eeb35890 60 copied = sendfile(out_fd, in_fd, &off, size - off);
42055a33
JH
61 if (copied < 0)
62 return FALSE;
63 }
64else
65
66#endif
67
68 {
69 int sread, wwritten;
70
328c5688 71 /* Rewind file */
d315eda1 72 if (lseek(in_fd, off, SEEK_SET) < 0) return FALSE;
328c5688 73
42055a33 74 /* Send file down the original fd */
d315eda1 75 while((sread = read(in_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) > 0)
42055a33
JH
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
100return 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
110Arguments:
111 As for transport_write_message() in transort.c, with additional arguments
112 for DKIM.
113
114Returns: TRUE on success; FALSE (with errno) for any failure
115*/
116
117static BOOL
118dkt_direct(transport_ctx * tctx, struct ob_dkim * dkim,
119 const uschar ** err)
120{
121int save_fd = tctx->u.fd;
122int save_options = tctx->options;
328c5688 123BOOL save_wireformat = spool_file_wireformat;
42055a33 124uschar * hdrs, * dkim_signature;
d315eda1 125int siglen = 0, hsize;
42055a33
JH
126const uschar * errstr;
127BOOL rc;
128
129DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
130
328c5688
JH
131/* Get headers in string for signing and transmission. Do CRLF
132and dotstuffing (but no body nor dot-termination) */
42055a33
JH
133
134tctx->u.msg = NULL;
135tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat)
136 | topt_output_string | topt_no_body;
137
138rc = transport_write_message(tctx, 0);
139hdrs = tctx->u.msg;
140hdrs[hsize = tctx->msg_ptr] = '\0';
141
142tctx->u.fd = save_fd;
143tctx->options = save_options;
144if (!rc) return FALSE;
145
146/* Get signatures for headers plus spool data file */
147
148dkim->dot_stuffed = !!(save_options & topt_end_dot);
149
150if ((dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET,
151 hdrs, dkim, &errstr)))
152 siglen = Ustrlen(dkim_signature);
153else 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
160mean 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
328c5688
JH
162messages, the BDAT LAST command. We want no dotstuffing expansion here, it
163having already been done - but we have to say we want CRLF output format, and
164temporarily set the marker for possible already-CRLF input. */
42055a33 165
328c5688
JH
166tctx->options &= ~topt_escape_headers;
167spool_file_wireformat = TRUE;
42055a33 168transport_write_reset(0);
d315eda1 169if ( siglen > 0 && !write_chunk(tctx, dkim_signature, siglen)
42055a33
JH
170 || !write_chunk(tctx, hdrs, hsize))
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;
206uschar * dkim_spool_name, * dkim_signature;
6c140c01 207int sread = 0, wwritten = 0, siglen = 0, options;
42055a33
JH
208off_t k_file_size;
209const uschar * errstr;
210
211dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
212 string_sprintf("-%d-K", (int)getpid()));
213
214DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
215
216if ((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. */
241if (!rc)
242 {
243 save_errno = errno;
244 goto CLEANUP;
245 }
246
247/* Feed the file to the goats^W DKIM lib */
248
249dkim->dot_stuffed = !!(options & topt_end_dot);
250if ((dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
251 siglen = Ustrlen(dkim_signature);
252else if (!(rc = dkt_sign_fail(dkim, &save_errno)))
253 {
254 *err = errstr;
255 goto CLEANUP;
256 }
257
258#ifndef HAVE_LINUX_SENDFILE
259if (options & topt_use_bdat)
260#endif
d315eda1
JH
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 }
42055a33
JH
266
267if (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
290if(siglen > 0 && !transport_write_block(tctx, dkim_signature, siglen, TRUE))
291 goto err;
292
293if (!dkt_send_file(tctx->u.fd, dkim_fd, 0, k_file_size))
294 {
295 save_errno = errno;
296 rc = FALSE;
297 }
298
299CLEANUP:
300 /* unlink -K file */
d315eda1 301 if (dkim_fd >= 0) (void)close(dkim_fd);
42055a33
JH
302 Uunlink(dkim_spool_name);
303 errno = save_errno;
304 return rc;
305
306err:
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
322Arguments:
323 As for transport_write_message() in transort.c, with additional arguments
324 for DKIM.
325
326Returns: TRUE on success; FALSE (with errno) for any failure
327*/
328
329BOOL
330dkim_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
335if (!(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
339a dkim signature of it, send the signature and a reconstructed message. This
340avoids using a temprary file. */
341
342if ( !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,
349send the signature and then send the file. */
350
351return 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 */