1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2016 */
6 /* See the file NOTICE for conditions of use and distribution. */
8 /* Transport shim for dkim signing */
13 #ifndef DISABLE_DKIM /* rest of file */
15 #ifdef HAVE_LINUX_SENDFILE
16 # include <sys/sendfile.h>
21 dkt_sign_fail(struct ob_dkim * dkim, int * errp)
23 if (dkim->dkim_strict)
25 uschar * dkim_strict_result = expand_string(dkim->dkim_strict);
27 if (dkim_strict_result)
28 if ( (strcmpic(dkim->dkim_strict, US"1") == 0) ||
29 (strcmpic(dkim->dkim_strict, US"true") == 0) )
31 /* Set errno to something halfway meaningful */
33 log_write(0, LOG_MAIN, "DKIM: message could not be signed,"
34 " and dkim_strict is set. Deferring message delivery.");
42 dkt_send_file(int out_fd, int in_fd, off_t off, size_t size)
44 DEBUG(D_transport) debug_printf("send file fd=%d size=%d\n", out_fd, size - off);
46 /*XXX should implement timeout, like transport_write_block_fd() ? */
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)
57 while(copied >= 0 && off < size)
58 copied = sendfile(out_fd, in_fd, &off, size - off);
70 lseek(in_fd, off, SEEK_SET);
72 /* Send file down the original fd */
73 while((sread = read(in_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) >0)
75 uschar * p = deliver_out_buffer;
81 wwritten = tls_out.active == out_fd
82 ? tls_write(FALSE, p, sread)
83 : write(out_fd, CS p, sread);
85 wwritten = write(out_fd, CS p, sread);
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.
109 As for transport_write_message() in transort.c, with additional arguments
112 Returns: TRUE on success; FALSE (with errno) for any failure
116 dkt_direct(transport_ctx * tctx, struct ob_dkim * dkim,
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;
124 const uschar * errstr;
127 DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
129 /* Get headers in string for signing and transmission. Do CRLF
130 and dotstuffing (but no body nor dot-termination) */
133 tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat)
134 | topt_output_string | topt_no_body;
136 rc = transport_write_message(tctx, 0);
138 hdrs[hsize = tctx->msg_ptr] = '\0';
140 tctx->u.fd = save_fd;
141 tctx->options = save_options;
142 if (!rc) return FALSE;
144 /* Get signatures for headers plus spool data file */
146 dkim->dot_stuffed = !!(save_options & topt_end_dot);
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)))
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. */
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))
171 spool_file_wireformat = save_wireformat;
172 tctx->options = save_options | topt_no_headers | topt_continuation;
174 if (!(transport_write_message(tctx, 0)))
177 tctx->options = save_options;
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
192 As for transport_write_message() in transort.c, with additional arguments
195 Returns: TRUE on success; FALSE (with errno) for any failure
199 dkt_via_kfile(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err)
204 uschar * dkim_spool_name, * dkim_signature;
205 int sread = 0, wwritten = 0, siglen, options;
207 const uschar * errstr;
209 dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
210 string_sprintf("-%d-K", (int)getpid()));
212 DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
214 if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0)
216 /* Can't create spool file. Ugh. */
219 *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
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). */
227 int save_fd = tctx->u.fd;
228 tctx->u.fd = dkim_fd;
229 options = tctx->options;
230 tctx->options &= ~topt_use_bdat;
232 rc = transport_write_message(tctx, 0);
234 tctx->u.fd = save_fd;
235 tctx->options = options;
238 /* Save error state. We must clean up before returning. */
245 /* Feed the file to the goats^W DKIM lib */
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)))
256 #ifndef HAVE_LINUX_SENDFILE
257 if (options & topt_use_bdat)
259 k_file_size = lseek(dkim_fd, 0, SEEK_END); /* Fetch file size */
261 if (options & topt_use_bdat)
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. */
267 if (siglen + k_file_size > DELIVER_OUT_BUFFER_SIZE && siglen > 0)
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
277 /* Send the BDAT command for the entire message, as a single LAST-marked
280 if (tctx->chunk_cb(tctx, siglen + k_file_size, tc_chunk_last) != OK)
284 if(siglen > 0 && !transport_write_block(tctx, dkim_signature, siglen, TRUE))
287 if (!dkt_send_file(tctx->u.fd, dkim_fd, 0, k_file_size))
295 (void)close(dkim_fd);
296 Uunlink(dkim_spool_name);
308 /***************************************************************************************************
309 * External interface to write the message, while signing it with DKIM and/or Domainkeys *
310 ***************************************************************************************************/
312 /* This function is a wrapper around transport_write_message().
313 It is only called from the smtp transport if DKIM or Domainkeys support
317 As for transport_write_message() in transort.c, with additional arguments
320 Returns: TRUE on success; FALSE (with errno) for any failure
324 dkim_transport_write_message(transport_ctx * tctx,
325 struct ob_dkim * dkim, const uschar ** err)
327 /* If we can't sign, just call the original function. */
329 if (!(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector))
330 return transport_write_message(tctx, 0);
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. */
336 if ( !transport_filter_argv
337 || !*transport_filter_argv
338 || !**transport_filter_argv
340 return dkt_direct(tctx, dkim, err);
342 /* Use the transport path to write a file, calculate a dkim signature,
343 send the signature and then send the file. */
345 return dkt_via_kfile(tctx, dkim, err);
348 #endif /* whole file */
352 /* End of dkim_transport.c */