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 */
17 dkt_sign_fail(struct ob_dkim * dkim, int * errp)
19 if (dkim->dkim_strict)
21 uschar * dkim_strict_result = expand_string(dkim->dkim_strict);
23 if (dkim_strict_result)
24 if ( (strcmpic(dkim->dkim_strict, US"1") == 0) ||
25 (strcmpic(dkim->dkim_strict, US"true") == 0) )
27 /* Set errno to something halfway meaningful */
29 log_write(0, LOG_MAIN, "DKIM: message could not be signed,"
30 " and dkim_strict is set. Deferring message delivery.");
37 /* Send the file at in_fd down the output fd */
40 dkt_send_file(int out_fd, int in_fd, off_t off, size_t size)
42 DEBUG(D_transport) debug_printf("send file fd=%d size=%u\n", out_fd, (unsigned)(size - off));
44 /*XXX should implement timeout, like transport_write_block_fd() ? */
47 /* We can use sendfile() to shove the file contents
48 to the socket. However only if we don't use TLS,
49 as then there's another layer of indirection
50 before the data finally hits the socket. */
51 if (tls_out.active != out_fd)
55 while(copied >= 0 && off < size)
56 copied = os_sendfile(out_fd, in_fd, &off, size - off);
68 if (lseek(in_fd, off, SEEK_SET) < 0) return FALSE;
70 /* Send file down the original fd */
71 while((sread = read(in_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) > 0)
73 uschar * p = deliver_out_buffer;
79 wwritten = tls_out.active == out_fd
80 ? tls_write(FALSE, p, sread, FALSE)
81 : write(out_fd, CS p, sread);
83 wwritten = write(out_fd, CS p, sread);
102 /* This function is a wrapper around transport_write_message().
103 It is only called from the smtp transport if DKIM or Domainkeys support
104 is active and no transport filter is to be used.
107 As for transport_write_message() in transort.c, with additional arguments
110 Returns: TRUE on success; FALSE (with errno) for any failure
114 dkt_direct(transport_ctx * tctx, struct ob_dkim * dkim,
117 int save_fd = tctx->u.fd;
118 int save_options = tctx->options;
119 BOOL save_wireformat = spool_file_wireformat;
120 uschar * hdrs, * dkim_signature;
121 int siglen = 0, hsize;
122 const uschar * errstr;
125 DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
127 /* Get headers in string for signing and transmission. Do CRLF
128 and dotstuffing (but no body nor dot-termination) */
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 dotstuffing expansion here, it
159 having already been done - but we have to say we want CRLF output format, and
160 temporarily set the marker for possible already-CRLF input. */
162 tctx->options &= ~topt_escape_headers;
163 spool_file_wireformat = TRUE;
164 transport_write_reset(0);
165 if ( siglen > 0 && !write_chunk(tctx, dkim_signature, siglen)
166 || !write_chunk(tctx, hdrs, hsize))
169 spool_file_wireformat = save_wireformat;
170 tctx->options = save_options | topt_no_headers | topt_continuation;
172 if (!(transport_write_message(tctx, 0)))
175 tctx->options = save_options;
180 /* This function is a wrapper around transport_write_message().
181 It is only called from the smtp transport if DKIM or Domainkeys support
182 is active and a transport filter is to be used. The function sets up a
183 replacement fd into a -K file, then calls the normal function. This way, the
184 exact bits that exim would have put "on the wire" will end up in the file
185 (except for TLS encapsulation, which is the very very last thing). When we
186 are done signing the file, send the signed message down the original fd (or
190 As for transport_write_message() in transort.c, with additional arguments
193 Returns: TRUE on success; FALSE (with errno) for any failure
197 dkt_via_kfile(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err)
202 uschar * dkim_spool_name, * dkim_signature;
203 int siglen = 0, options;
205 const uschar * errstr;
207 dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
208 string_sprintf("-%d-K", (int)getpid()));
210 DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
212 if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0)
214 /* Can't create spool file. Ugh. */
217 *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
221 /* Call transport utility function to write the -K file; does the CRLF expansion
222 (but, in the CHUNKING case, neither dot-stuffing nor dot-termination). */
225 int save_fd = tctx->u.fd;
226 tctx->u.fd = dkim_fd;
227 options = tctx->options;
228 tctx->options &= ~topt_use_bdat;
230 rc = transport_write_message(tctx, 0);
232 tctx->u.fd = save_fd;
233 tctx->options = options;
236 /* Save error state. We must clean up before returning. */
243 /* Feed the file to the goats^W DKIM lib */
245 dkim->dot_stuffed = !!(options & topt_end_dot);
246 if ((dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
247 siglen = Ustrlen(dkim_signature);
248 else if (!(rc = dkt_sign_fail(dkim, &save_errno)))
255 if (options & topt_use_bdat)
257 if ((k_file_size = lseek(dkim_fd, 0, SEEK_END)) < 0)
259 *err = string_sprintf("dkim spoolfile seek: %s", strerror(errno));
263 if (options & topt_use_bdat)
265 /* On big messages output a precursor chunk to get any pipelined
266 MAIL & RCPT commands flushed, then reap the responses so we can
267 error out on RCPT rejects before sending megabytes. */
269 if (siglen + k_file_size > DELIVER_OUT_BUFFER_SIZE && siglen > 0)
271 if ( tctx->chunk_cb(tctx, siglen, 0) != OK
272 || !transport_write_block(tctx, dkim_signature, siglen, FALSE)
273 || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
279 /* Send the BDAT command for the entire message, as a single LAST-marked
282 if (tctx->chunk_cb(tctx, siglen + k_file_size, tc_chunk_last) != OK)
286 if(siglen > 0 && !transport_write_block(tctx, dkim_signature, siglen, TRUE))
289 if (!dkt_send_file(tctx->u.fd, dkim_fd, 0, k_file_size))
297 if (dkim_fd >= 0) (void)close(dkim_fd);
298 Uunlink(dkim_spool_name);
310 /***************************************************************************************************
311 * External interface to write the message, while signing it with DKIM and/or Domainkeys *
312 ***************************************************************************************************/
314 /* This function is a wrapper around transport_write_message().
315 It is only called from the smtp transport if DKIM or Domainkeys support
319 As for transport_write_message() in transort.c, with additional arguments
322 Returns: TRUE on success; FALSE (with errno) for any failure
326 dkim_transport_write_message(transport_ctx * tctx,
327 struct ob_dkim * dkim, const uschar ** err)
329 /* If we can't sign, just call the original function. */
331 if (!(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector))
332 return transport_write_message(tctx, 0);
334 /* If there is no filter command set up, construct the message and calculate
335 a dkim signature of it, send the signature and a reconstructed message. This
336 avoids using a temprary file. */
338 if ( !transport_filter_argv
339 || !*transport_filter_argv
340 || !**transport_filter_argv
342 return dkt_direct(tctx, dkim, err);
344 /* Use the transport path to write a file, calculate a dkim signature,
345 send the signature and then send the file. */
347 return dkt_via_kfile(tctx, dkim, err);
350 #endif /* whole file */
354 /* End of dkim_transport.c */