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.");
41 /* Send the file at in_fd down the output fd */
44 dkt_send_file(int out_fd, int in_fd, off_t off, size_t size)
46 DEBUG(D_transport) debug_printf("send file fd=%d size=%u\n", out_fd, (unsigned)(size - off));
48 /*XXX should implement timeout, like transport_write_block_fd() ? */
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)
59 while(copied >= 0 && off < size)
60 copied = sendfile(out_fd, in_fd, &off, size - off);
72 if (lseek(in_fd, off, SEEK_SET) < 0) return FALSE;
74 /* Send file down the original fd */
75 while((sread = read(in_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) > 0)
77 uschar * p = deliver_out_buffer;
83 wwritten = tls_out.active == out_fd
84 ? tls_write(FALSE, p, sread)
85 : write(out_fd, CS p, sread);
87 wwritten = write(out_fd, CS p, sread);
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.
111 As for transport_write_message() in transort.c, with additional arguments
114 Returns: TRUE on success; FALSE (with errno) for any failure
118 dkt_direct(transport_ctx * tctx, struct ob_dkim * dkim,
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;
129 DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
131 /* Get headers in string for signing and transmission. Do CRLF
132 and dotstuffing (but no body nor dot-termination) */
135 tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat)
136 | topt_output_string | topt_no_body;
138 rc = transport_write_message(tctx, 0);
140 hdrs[hsize = tctx->msg_ptr] = '\0';
142 tctx->u.fd = save_fd;
143 tctx->options = save_options;
144 if (!rc) return FALSE;
146 /* Get signatures for headers plus spool data file */
148 dkim->dot_stuffed = !!(save_options & topt_end_dot);
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)))
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. */
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))
173 spool_file_wireformat = save_wireformat;
174 tctx->options = save_options | topt_no_headers | topt_continuation;
176 if (!(transport_write_message(tctx, 0)))
179 tctx->options = save_options;
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
194 As for transport_write_message() in transort.c, with additional arguments
197 Returns: TRUE on success; FALSE (with errno) for any failure
201 dkt_via_kfile(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err)
206 uschar * dkim_spool_name, * dkim_signature;
207 int sread = 0, wwritten = 0, siglen = 0, options;
209 const uschar * errstr;
211 dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
212 string_sprintf("-%d-K", (int)getpid()));
214 DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
216 if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0)
218 /* Can't create spool file. Ugh. */
221 *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
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). */
229 int save_fd = tctx->u.fd;
230 tctx->u.fd = dkim_fd;
231 options = tctx->options;
232 tctx->options &= ~topt_use_bdat;
234 rc = transport_write_message(tctx, 0);
236 tctx->u.fd = save_fd;
237 tctx->options = options;
240 /* Save error state. We must clean up before returning. */
247 /* Feed the file to the goats^W DKIM lib */
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)))
258 #ifndef HAVE_LINUX_SENDFILE
259 if (options & topt_use_bdat)
261 if ((k_file_size = lseek(dkim_fd, 0, SEEK_END)) < 0)
263 *err = string_sprintf("dkim spoolfile seek: %s", strerror(errno));
267 if (options & topt_use_bdat)
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. */
273 if (siglen + k_file_size > DELIVER_OUT_BUFFER_SIZE && siglen > 0)
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
283 /* Send the BDAT command for the entire message, as a single LAST-marked
286 if (tctx->chunk_cb(tctx, siglen + k_file_size, tc_chunk_last) != OK)
290 if(siglen > 0 && !transport_write_block(tctx, dkim_signature, siglen, TRUE))
293 if (!dkt_send_file(tctx->u.fd, dkim_fd, 0, k_file_size))
301 if (dkim_fd >= 0) (void)close(dkim_fd);
302 Uunlink(dkim_spool_name);
314 /***************************************************************************************************
315 * External interface to write the message, while signing it with DKIM and/or Domainkeys *
316 ***************************************************************************************************/
318 /* This function is a wrapper around transport_write_message().
319 It is only called from the smtp transport if DKIM or Domainkeys support
323 As for transport_write_message() in transort.c, with additional arguments
326 Returns: TRUE on success; FALSE (with errno) for any failure
330 dkim_transport_write_message(transport_ctx * tctx,
331 struct ob_dkim * dkim, const uschar ** err)
333 /* If we can't sign, just call the original function. */
335 if (!(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector))
336 return transport_write_message(tctx, 0);
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. */
342 if ( !transport_filter_argv
343 || !*transport_filter_argv
344 || !**transport_filter_argv
346 return dkt_direct(tctx, dkim, err);
348 /* Use the transport path to write a file, calculate a dkim signature,
349 send the signature and then send the file. */
351 return dkt_via_kfile(tctx, dkim, err);
354 #endif /* whole file */
358 /* End of dkim_transport.c */