1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2018 */
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
47 DEBUG(D_transport) debug_printf("send file fd=%d size=%u\n", out_fd, (unsigned)(size - off));
49 DEBUG(D_transport) debug_printf("send file fd=%d\n", out_fd);
52 /*XXX should implement timeout, like transport_write_block_fd() ? */
55 /* We can use sendfile() to shove the file contents
56 to the socket. However only if we don't use TLS,
57 as then there's another layer of indirection
58 before the data finally hits the socket. */
59 if (tls_out.active != out_fd)
63 while(copied >= 0 && off < size)
64 copied = os_sendfile(out_fd, in_fd, &off, size - off);
76 if (lseek(in_fd, off, SEEK_SET) < 0) return FALSE;
78 /* Send file down the original fd */
79 while((sread = read(in_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) > 0)
81 uschar * p = deliver_out_buffer;
87 wwritten = tls_out.active == out_fd
88 ? tls_write(FALSE, p, sread, FALSE)
89 : write(out_fd, CS p, sread);
91 wwritten = write(out_fd, CS p, sread);
110 /* This function is a wrapper around transport_write_message().
111 It is only called from the smtp transport if DKIM or Domainkeys support
112 is active and no transport filter is to be used.
115 As for transport_write_message() in transort.c, with additional arguments
118 Returns: TRUE on success; FALSE (with errno) for any failure
122 dkt_direct(transport_ctx * tctx, struct ob_dkim * dkim,
125 int save_fd = tctx->u.fd;
126 int save_options = tctx->options;
127 BOOL save_wireformat = spool_file_wireformat;
129 gstring * dkim_signature;
131 const uschar * errstr;
135 DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
137 /* Get headers in string for signing and transmission. Do CRLF
138 and dotstuffing (but no body nor dot-termination) */
141 tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat)
142 | topt_output_string | topt_no_body;
144 rc = transport_write_message(tctx, 0);
145 hdrs = string_from_gstring(tctx->u.msg);
146 hsize = tctx->u.msg->ptr;
148 tctx->u.fd = save_fd;
149 tctx->options = save_options;
150 if (!rc) return FALSE;
152 /* Get signatures for headers plus spool data file */
154 #ifdef EXPERIMENTAL_ARC
158 dkim->dot_stuffed = !!(save_options & topt_end_dot);
159 if (!(dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET,
160 hdrs, dkim, &errstr)))
161 if (!(rc = dkt_sign_fail(dkim, &errno)))
167 #ifdef EXPERIMENTAL_ARC
168 if (dkim->arc_signspec) /* Prepend ARC headers */
169 if (!(dkim_signature =
170 arc_sign(dkim->arc_signspec, dkim_signature, &verrstr)))
177 /* Write the signature and headers into the deliver-out-buffer. This should
178 mean they go out in the same packet as the MAIL, RCPT and (first) BDAT commands
179 (transport_write_message() sizes the BDAT for the buffered amount) - for short
180 messages, the BDAT LAST command. We want no dotstuffing expansion here, it
181 having already been done - but we have to say we want CRLF output format, and
182 temporarily set the marker for possible already-CRLF input. */
184 tctx->options &= ~topt_escape_headers;
185 spool_file_wireformat = TRUE;
186 transport_write_reset(0);
187 if ( ( dkim_signature
188 && dkim_signature->ptr > 0
189 && !write_chunk(tctx, dkim_signature->s, dkim_signature->ptr)
191 || !write_chunk(tctx, hdrs, hsize)
195 spool_file_wireformat = save_wireformat;
196 tctx->options = save_options | topt_no_headers | topt_continuation;
198 if (!(transport_write_message(tctx, 0)))
201 tctx->options = save_options;
206 /* This function is a wrapper around transport_write_message().
207 It is only called from the smtp transport if DKIM or Domainkeys support
208 is active and a transport filter is to be used. The function sets up a
209 replacement fd into a -K file, then calls the normal function. This way, the
210 exact bits that exim would have put "on the wire" will end up in the file
211 (except for TLS encapsulation, which is the very very last thing). When we
212 are done signing the file, send the signed message down the original fd (or
216 As for transport_write_message() in transort.c, with additional arguments
219 Returns: TRUE on success; FALSE (with errno) for any failure
223 dkt_via_kfile(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err)
228 uschar * dkim_spool_name;
229 gstring * dkim_signature;
232 const uschar * errstr;
234 dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
235 string_sprintf("-%d-K", (int)getpid()));
237 DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
239 if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0)
241 /* Can't create spool file. Ugh. */
244 *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
248 /* Call transport utility function to write the -K file; does the CRLF expansion
249 (but, in the CHUNKING case, neither dot-stuffing nor dot-termination). */
252 int save_fd = tctx->u.fd;
253 tctx->u.fd = dkim_fd;
254 options = tctx->options;
255 tctx->options &= ~topt_use_bdat;
257 rc = transport_write_message(tctx, 0);
259 tctx->u.fd = save_fd;
260 tctx->options = options;
263 /* Save error state. We must clean up before returning. */
270 #ifdef EXPERIMENTAL_ARC
274 /* Feed the file to the goats^W DKIM lib */
276 dkim->dot_stuffed = !!(options & topt_end_dot);
277 if (!(dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
280 if (!(rc = dkt_sign_fail(dkim, &save_errno)))
287 dlen = dkim_signature->ptr;
289 #ifdef EXPERIMENTAL_ARC
290 if (dkim->arc_signspec) /* Prepend ARC headers */
292 if (!(dkim_signature = arc_sign(dkim->arc_signspec, dkim_signature, USS err)))
294 dlen = dkim_signature->ptr;
299 if (options & topt_use_bdat)
301 if ((k_file_size = lseek(dkim_fd, 0, SEEK_END)) < 0)
303 *err = string_sprintf("dkim spoolfile seek: %s", strerror(errno));
307 if (options & topt_use_bdat)
309 /* On big messages output a precursor chunk to get any pipelined
310 MAIL & RCPT commands flushed, then reap the responses so we can
311 error out on RCPT rejects before sending megabytes. */
313 if ( dlen + k_file_size > DELIVER_OUT_BUFFER_SIZE
316 if ( tctx->chunk_cb(tctx, dlen, 0) != OK
317 || !transport_write_block(tctx,
318 dkim_signature->s, dlen, FALSE)
319 || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
325 /* Send the BDAT command for the entire message, as a single LAST-marked
328 if (tctx->chunk_cb(tctx, dlen + k_file_size, tc_chunk_last) != OK)
332 if(dlen > 0 && !transport_write_block(tctx, dkim_signature->s, dlen, TRUE))
335 if (!dkt_send_file(tctx->u.fd, dkim_fd, 0
347 if (dkim_fd >= 0) (void)close(dkim_fd);
348 Uunlink(dkim_spool_name);
360 /***************************************************************************************************
361 * External interface to write the message, while signing it with DKIM and/or Domainkeys *
362 ***************************************************************************************************/
364 /* This function is a wrapper around transport_write_message().
365 It is only called from the smtp transport if DKIM or Domainkeys support
369 As for transport_write_message() in transort.c, with additional arguments
372 Returns: TRUE on success; FALSE (with errno) for any failure
376 dkim_transport_write_message(transport_ctx * tctx,
377 struct ob_dkim * dkim, const uschar ** err)
379 /* If we can't sign, just call the original function. */
381 if ( !(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector)
382 && !dkim->force_bodyhash)
383 return transport_write_message(tctx, 0);
385 /* If there is no filter command set up, construct the message and calculate
386 a dkim signature of it, send the signature and a reconstructed message. This
387 avoids using a temprary file. */
389 if ( !transport_filter_argv
390 || !*transport_filter_argv
391 || !**transport_filter_argv
393 return dkt_direct(tctx, dkim, err);
395 /* Use the transport path to write a file, calculate a dkim signature,
396 send the signature and then send the file. */
398 return dkt_via_kfile(tctx, dkim, err);
401 #endif /* whole file */
405 /* End of dkim_transport.c */