1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) The Exim Maintainers 2022 */
6 /* Copyright (c) University of Cambridge 1995 - 2018 */
7 /* See the file NOTICE for conditions of use and distribution. */
8 /* SPDX-License-Identifier: GPL-2.0-or-later */
10 /* Transport shim for dkim signing */
15 #ifndef DISABLE_DKIM /* rest of file */
19 dkt_sign_fail(struct ob_dkim * dkim, int * errp)
21 if (dkim->dkim_strict)
23 uschar * dkim_strict_result = expand_string(dkim->dkim_strict);
25 if (dkim_strict_result)
26 if ( strcmpic(dkim_strict_result, US"1") == 0
27 || strcmpic(dkim_strict_result, US"true") == 0)
29 /* Set errno to something halfway meaningful */
31 log_write(0, LOG_MAIN, "DKIM: message could not be signed,"
32 " and dkim_strict is set. Deferring message delivery.");
39 /* Send the file at in_fd down the output fd */
42 dkt_send_file(int out_fd, int in_fd, off_t off
49 DEBUG(D_transport) debug_printf("send file fd=%d size=%u\n", out_fd, (unsigned)(size - off));
51 DEBUG(D_transport) debug_printf("send file fd=%d\n", out_fd);
54 /*XXX should implement timeout, like transport_write_block_fd() ? */
57 /* We can use sendfile() to shove the file contents
58 to the socket. However only if we don't use TLS,
59 as then there's another layer of indirection
60 before the data finally hits the socket. */
61 if (tls_out.active.sock != out_fd)
65 while(copied >= 0 && off < size)
66 copied = os_sendfile(out_fd, in_fd, &off, size - off);
78 if (lseek(in_fd, off, SEEK_SET) < 0) return FALSE;
80 /* Send file down the original fd */
81 while((sread = read(in_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) > 0)
83 uschar * p = deliver_out_buffer;
89 wwritten = tls_out.active.sock == out_fd
90 ? tls_write(tls_out.active.tls_ctx, p, sread, FALSE)
91 : write(out_fd, CS p, sread);
93 wwritten = write(out_fd, CS p, sread);
112 /* This function is a wrapper around transport_write_message().
113 It is only called from the smtp transport if DKIM or Domainkeys support
114 is active and no transport filter is to be used.
117 As for transport_write_message() in transort.c, with additional arguments
120 Returns: TRUE on success; FALSE (with errno) for any failure
124 dkt_direct(transport_ctx * tctx, struct ob_dkim * dkim,
127 int save_fd = tctx->u.fd;
128 int save_options = tctx->options;
129 BOOL save_wireformat = f.spool_file_wireformat;
131 gstring * dkim_signature;
133 const uschar * errstr;
136 DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
138 /* Get headers in string for signing and transmission. Do CRLF
139 and dotstuffing (but no body nor dot-termination) */
142 tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat)
143 | topt_output_string | topt_no_body;
145 rc = transport_write_message(tctx, 0);
146 hdrs = string_from_gstring(tctx->u.msg);
147 hsize = tctx->u.msg->ptr;
149 tctx->u.fd = save_fd;
150 tctx->options = save_options;
151 if (!rc) return FALSE;
153 /* Get signatures for headers plus spool data file */
155 #ifdef EXPERIMENTAL_ARC
159 /* The dotstuffed status of the datafile depends on whether it was stored
162 dkim->dot_stuffed = f.spool_file_wireformat;
163 if (!(dkim_signature = dkim_exim_sign(deliver_datafile,
164 spool_data_start_offset(message_id), hdrs, dkim, &errstr)))
165 if (!(rc = dkt_sign_fail(dkim, &errno)))
171 #ifdef EXPERIMENTAL_ARC
172 if (dkim->arc_signspec) /* Prepend ARC headers */
175 if (!(dkim_signature = arc_sign(dkim->arc_signspec, dkim_signature, &e)))
183 /* Write the signature and headers into the deliver-out-buffer. This should
184 mean they go out in the same packet as the MAIL, RCPT and (first) BDAT commands
185 (transport_write_message() sizes the BDAT for the buffered amount) - for short
186 messages, the BDAT LAST command. We want no dotstuffing expansion here, it
187 having already been done - but we have to say we want CRLF output format, and
188 temporarily set the marker for possible already-CRLF input. */
190 tctx->options &= ~topt_escape_headers;
191 f.spool_file_wireformat = TRUE;
192 transport_write_reset(0);
193 if ( ( dkim_signature
194 && dkim_signature->ptr > 0
195 && !write_chunk(tctx, dkim_signature->s, dkim_signature->ptr)
197 || !write_chunk(tctx, hdrs, hsize)
201 f.spool_file_wireformat = save_wireformat;
202 tctx->options = save_options | topt_no_headers | topt_continuation;
204 if (!(transport_write_message(tctx, 0)))
207 tctx->options = save_options;
212 /* This function is a wrapper around transport_write_message().
213 It is only called from the smtp transport if DKIM or Domainkeys support
214 is active and a transport filter is to be used. The function sets up a
215 replacement fd into a -K file, then calls the normal function. This way, the
216 exact bits that exim would have put "on the wire" will end up in the file
217 (except for TLS encapsulation, which is the very very last thing). When we
218 are done signing the file, send the signed message down the original fd (or
222 As for transport_write_message() in transort.c, with additional arguments
225 Returns: TRUE on success; FALSE (with errno) for any failure
229 dkt_via_kfile(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err)
234 uschar * dkim_spool_name;
235 gstring * dkim_signature;
238 const uschar * errstr;
240 dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
241 string_sprintf("-%d-K", (int)getpid()));
243 DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
245 if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0)
247 /* Can't create spool file. Ugh. */
250 *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
254 /* Call transport utility function to write the -K file; does the CRLF expansion
255 (but, in the CHUNKING case, neither dot-stuffing nor dot-termination). */
258 int save_fd = tctx->u.fd;
259 tctx->u.fd = dkim_fd;
260 options = tctx->options;
261 tctx->options &= ~topt_use_bdat;
263 rc = transport_write_message(tctx, 0);
265 tctx->u.fd = save_fd;
266 tctx->options = options;
269 /* Save error state. We must clean up before returning. */
276 #ifdef EXPERIMENTAL_ARC
280 /* Feed the file to the goats^W DKIM lib. At this point the dotstuffed
281 status of the file depends on the output of transport_write_message() just
282 above, which should be the result of the end_dot flag in tctx->options. */
284 dkim->dot_stuffed = !!(options & topt_end_dot);
285 if (!(dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
288 if (!(rc = dkt_sign_fail(dkim, &save_errno)))
295 dlen = dkim_signature->ptr;
297 #ifdef EXPERIMENTAL_ARC
298 if (dkim->arc_signspec) /* Prepend ARC headers */
300 if (!(dkim_signature = arc_sign(dkim->arc_signspec, dkim_signature, USS err)))
302 dlen = dkim_signature->ptr;
307 if (options & topt_use_bdat)
309 if ((k_file_size = lseek(dkim_fd, 0, SEEK_END)) < 0)
311 *err = string_sprintf("dkim spoolfile seek: %s", strerror(errno));
315 if (options & topt_use_bdat)
317 /* On big messages output a precursor chunk to get any pipelined
318 MAIL & RCPT commands flushed, then reap the responses so we can
319 error out on RCPT rejects before sending megabytes. */
321 if ( dlen + k_file_size > DELIVER_OUT_BUFFER_SIZE
324 if ( tctx->chunk_cb(tctx, dlen, 0) != OK
325 || !transport_write_block(tctx,
326 dkim_signature->s, dlen, FALSE)
327 || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
333 /* Send the BDAT command for the entire message, as a single LAST-marked
336 if (tctx->chunk_cb(tctx, dlen + k_file_size, tc_chunk_last) != OK)
340 if(dlen > 0 && !transport_write_block(tctx, dkim_signature->s, dlen, TRUE))
343 if (!dkt_send_file(tctx->u.fd, dkim_fd, 0
355 if (dkim_fd >= 0) (void)close(dkim_fd);
356 Uunlink(dkim_spool_name);
368 /***************************************************************************************************
369 * External interface to write the message, while signing it with DKIM and/or Domainkeys *
370 ***************************************************************************************************/
372 /* This function is a wrapper around transport_write_message().
373 It is only called from the smtp transport if DKIM or Domainkeys support
377 As for transport_write_message() in transort.c, with additional arguments
380 Returns: TRUE on success; FALSE (with errno) for any failure
384 dkim_transport_write_message(transport_ctx * tctx,
385 struct ob_dkim * dkim, const uschar ** err)
387 /* If we can't sign, just call the original function. */
389 if ( !(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector)
390 && !dkim->force_bodyhash)
391 return transport_write_message(tctx, 0);
393 /* If there is no filter command set up, construct the message and calculate
394 a dkim signature of it, send the signature and a reconstructed message. This
395 avoids using a temprary file. */
397 if ( !transport_filter_argv
398 || !*transport_filter_argv
399 || !**transport_filter_argv
401 return dkt_direct(tctx, dkim, err);
403 /* Use the transport path to write a file, calculate a dkim signature,
404 send the signature and then send the file. */
406 return dkt_via_kfile(tctx, dkim, err);
409 #endif /* whole file */
413 /* End of dkim_transport.c */