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() ? */
49 lseek(in_fd, off, SEEK_SET);
51 #ifdef HAVE_LINUX_SENDFILE
52 /* We can use sendfile() to shove the file contents
53 to the socket. However only if we don't use TLS,
54 as then there's another layer of indirection
55 before the data finally hits the socket. */
56 if (tls_out.active != out_fd)
60 while(copied >= 0 && off < size)
61 copied = sendfile(out_fd, in_fd, &off, size - off);
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 uschar * hdrs, * dkim_signature;
123 const uschar * errstr;
126 DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
128 /* Get headers in string for signing and transmission */
131 tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat)
132 | topt_output_string | topt_no_body;
134 rc = transport_write_message(tctx, 0);
136 hdrs[hsize = tctx->msg_ptr] = '\0';
138 tctx->u.fd = save_fd;
139 tctx->options = save_options;
140 if (!rc) return FALSE;
142 /* Get signatures for headers plus spool data file */
144 dkim->dot_stuffed = !!(save_options & topt_end_dot);
146 if ((dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET,
147 hdrs, dkim, &errstr)))
148 siglen = Ustrlen(dkim_signature);
149 else if (!(rc = dkt_sign_fail(dkim, &errno)))
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 CRLF or dotstuffing expansion */
160 tctx->options &= ~(topt_use_crlf | topt_escape_headers);
161 transport_write_reset(0);
162 if ( !write_chunk(tctx, dkim_signature, siglen)
163 || !write_chunk(tctx, hdrs, hsize))
166 tctx->options = save_options | topt_no_headers | topt_continuation;
168 if (!(transport_write_message(tctx, 0)))
171 tctx->options = save_options;
176 /* This function is a wrapper around transport_write_message().
177 It is only called from the smtp transport if DKIM or Domainkeys support
178 is active and a transport filter is to be used. The function sets up a
179 replacement fd into a -K file, then calls the normal function. This way, the
180 exact bits that exim would have put "on the wire" will end up in the file
181 (except for TLS encapsulation, which is the very very last thing). When we
182 are done signing the file, send the signed message down the original fd (or
186 As for transport_write_message() in transort.c, with additional arguments
189 Returns: TRUE on success; FALSE (with errno) for any failure
193 dkt_via_kfile(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err)
198 uschar * dkim_spool_name, * dkim_signature;
199 int sread = 0, wwritten = 0, siglen, options;
201 const uschar * errstr;
203 dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
204 string_sprintf("-%d-K", (int)getpid()));
206 DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
208 if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0)
210 /* Can't create spool file. Ugh. */
213 *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
217 /* Call transport utility function to write the -K file; does the CRLF expansion
218 (but, in the CHUNKING case, neither dot-stuffing nor dot-termination). */
221 int save_fd = tctx->u.fd;
222 tctx->u.fd = dkim_fd;
223 options = tctx->options;
224 tctx->options &= ~topt_use_bdat;
226 rc = transport_write_message(tctx, 0);
228 tctx->u.fd = save_fd;
229 tctx->options = options;
232 /* Save error state. We must clean up before returning. */
239 /* Feed the file to the goats^W DKIM lib */
241 dkim->dot_stuffed = !!(options & topt_end_dot);
242 if ((dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
243 siglen = Ustrlen(dkim_signature);
244 else if (!(rc = dkt_sign_fail(dkim, &save_errno)))
250 #ifndef HAVE_LINUX_SENDFILE
251 if (options & topt_use_bdat)
253 k_file_size = lseek(dkim_fd, 0, SEEK_END); /* Fetch file size */
255 if (options & topt_use_bdat)
257 /* On big messages output a precursor chunk to get any pipelined
258 MAIL & RCPT commands flushed, then reap the responses so we can
259 error out on RCPT rejects before sending megabytes. */
261 if (siglen + k_file_size > DELIVER_OUT_BUFFER_SIZE && siglen > 0)
263 if ( tctx->chunk_cb(tctx, siglen, 0) != OK
264 || !transport_write_block(tctx, dkim_signature, siglen, FALSE)
265 || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
271 /* Send the BDAT command for the entire message, as a single LAST-marked
274 if (tctx->chunk_cb(tctx, siglen + k_file_size, tc_chunk_last) != OK)
278 if(siglen > 0 && !transport_write_block(tctx, dkim_signature, siglen, TRUE))
281 if (!dkt_send_file(tctx->u.fd, dkim_fd, 0, k_file_size))
289 (void)close(dkim_fd);
290 Uunlink(dkim_spool_name);
302 /***************************************************************************************************
303 * External interface to write the message, while signing it with DKIM and/or Domainkeys *
304 ***************************************************************************************************/
306 /* This function is a wrapper around transport_write_message().
307 It is only called from the smtp transport if DKIM or Domainkeys support
311 As for transport_write_message() in transort.c, with additional arguments
314 Returns: TRUE on success; FALSE (with errno) for any failure
318 dkim_transport_write_message(transport_ctx * tctx,
319 struct ob_dkim * dkim, const uschar ** err)
321 /* If we can't sign, just call the original function. */
323 if (!(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector))
324 return transport_write_message(tctx, 0);
326 /* If there is no filter command set up, construct the message and calculate
327 a dkim signature of it, send the signature and a reconstructed message. This
328 avoids using a temprary file. */
330 if ( !transport_filter_argv
331 || !*transport_filter_argv
332 || !**transport_filter_argv
334 return dkt_direct(tctx, dkim, err);
336 /* Use the transport path to write a file, calculate a dkim signature,
337 send the signature and then send the file. */
339 return dkt_via_kfile(tctx, dkim, err);
342 #endif /* whole file */
346 /* End of dkim_transport.c */