1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) The Exim Maintainers 2022 - 2024 */
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 GET_OPTION("dkim_strict");
22 if (dkim->dkim_strict)
24 uschar * dkim_strict_result = expand_string(dkim->dkim_strict);
26 if (dkim_strict_result)
27 if ( strcmpic(dkim_strict_result, US"1") == 0
28 || strcmpic(dkim_strict_result, US"true") == 0)
30 /* Set errno to something halfway meaningful */
32 log_write(0, LOG_MAIN, "DKIM: message could not be signed,"
33 " and dkim_strict is set. Deferring message delivery.");
40 /* Send the file at in_fd down the output fd */
43 dkt_send_file(int out_fd, int in_fd, off_t off
50 DEBUG(D_transport) debug_printf("send file fd=%d size=%u\n", out_fd, (unsigned)(size - off));
52 DEBUG(D_transport) debug_printf("send file fd=%d\n", out_fd);
55 /*XXX should implement timeout, like transport_write_block_fd() ? */
58 /* We can use sendfile() to shove the file contents
59 to the socket. However only if we don't use TLS,
60 as then there's another layer of indirection
61 before the data finally hits the socket. */
62 if (tls_out.active.sock != out_fd)
66 while(copied >= 0 && off < size)
67 copied = os_sendfile(out_fd, in_fd, &off, size - off);
79 if (lseek(in_fd, off, SEEK_SET) < 0) return FALSE;
81 /* Send file down the original fd */
82 while((sread = read(in_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) > 0)
84 uschar * p = deliver_out_buffer;
90 wwritten = tls_out.active.sock == out_fd
91 ? tls_write(tls_out.active.tls_ctx, p, sread, FALSE)
92 : write(out_fd, CS p, sread);
94 wwritten = write(out_fd, CS p, sread);
113 /* This function is a wrapper around transport_write_message().
114 It is only called from the smtp transport if DKIM or Domainkeys support
115 is active and no transport filter is to be used.
118 As for transport_write_message() in transort.c, with additional arguments
121 Returns: TRUE on success; FALSE (with errno) for any failure
125 dkt_direct(transport_ctx * tctx, struct ob_dkim * dkim,
128 int save_fd = tctx->u.fd;
129 int save_options = tctx->options;
130 BOOL save_wireformat = f.spool_file_wireformat;
132 gstring * dkim_signature;
134 const uschar * errstr;
137 DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
139 /* Get headers in string for signing and transmission. Do CRLF
140 and dotstuffing (but no body nor dot-termination) */
143 tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat)
144 | topt_output_string | topt_no_body;
146 rc = transport_write_message(tctx, 0);
147 hdrs = string_from_gstring(tctx->u.msg);
148 hsize = tctx->u.msg->ptr;
150 tctx->u.fd = save_fd;
151 tctx->options = save_options;
152 if (!rc) return FALSE;
154 /* Get signatures for headers plus spool data file */
156 #ifdef EXPERIMENTAL_ARC
160 /* The dotstuffed status of the datafile depends on whether it was stored
163 dkim->dot_stuffed = f.spool_file_wireformat;
164 if (!(dkim_signature = dkim_exim_sign(deliver_datafile,
165 spool_data_start_offset(message_id), hdrs, dkim, &errstr)))
166 if (!(rc = dkt_sign_fail(dkim, &errno)))
172 #ifdef EXPERIMENTAL_ARC
173 if (dkim->arc_signspec) /* Prepend ARC headers */
176 if (!(dkim_signature = arc_sign(dkim->arc_signspec, dkim_signature, &e)))
184 /* Write the signature and headers into the deliver-out-buffer. This should
185 mean they go out in the same packet as the MAIL, RCPT and (first) BDAT commands
186 (transport_write_message() sizes the BDAT for the buffered amount) - for short
187 messages, the BDAT LAST command. We want no dotstuffing expansion here, it
188 having already been done - but we have to say we want CRLF output format, and
189 temporarily set the marker for possible already-CRLF input. */
191 tctx->options &= ~topt_escape_headers;
192 f.spool_file_wireformat = TRUE;
193 transport_write_reset(0);
194 if ( ( dkim_signature
195 && dkim_signature->ptr > 0
196 && !write_chunk(tctx, dkim_signature->s, dkim_signature->ptr)
198 || !write_chunk(tctx, hdrs, hsize)
202 f.spool_file_wireformat = save_wireformat;
203 tctx->options = save_options | topt_no_headers | topt_continuation;
205 if (!(transport_write_message(tctx, 0)))
208 tctx->options = save_options;
213 /* This function is a wrapper around transport_write_message().
214 It is only called from the smtp transport if DKIM or Domainkeys support
215 is active and a transport filter is to be used. The function sets up a
216 replacement fd into a -K file, then calls the normal function. This way, the
217 exact bits that exim would have put "on the wire" will end up in the file
218 (except for TLS encapsulation, which is the very very last thing). When we
219 are done signing the file, send the signed message down the original fd (or
223 As for transport_write_message() in transort.c, with additional arguments
226 Returns: TRUE on success; FALSE (with errno) for any failure
230 dkt_via_kfile(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err)
235 uschar * dkim_spool_name;
236 gstring * dkim_signature;
239 const uschar * errstr;
241 dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
242 string_sprintf("-%d-K", (int)getpid()));
244 DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
246 if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0)
248 /* Can't create spool file. Ugh. */
251 *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
255 /* Call transport utility function to write the -K file; does the CRLF expansion
256 (but, in the CHUNKING case, neither dot-stuffing nor dot-termination). */
259 int save_fd = tctx->u.fd;
260 tctx->u.fd = dkim_fd;
261 options = tctx->options;
262 tctx->options &= ~topt_use_bdat;
264 rc = transport_write_message(tctx, 0);
266 tctx->u.fd = save_fd;
267 tctx->options = options;
270 /* Save error state. We must clean up before returning. */
277 #ifdef EXPERIMENTAL_ARC
281 /* Feed the file to the goats^W DKIM lib. At this point the dotstuffed
282 status of the file depends on the output of transport_write_message() just
283 above, which should be the result of the end_dot flag in tctx->options. */
285 dkim->dot_stuffed = !!(options & topt_end_dot);
286 if (!(dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
289 if (!(rc = dkt_sign_fail(dkim, &save_errno)))
296 dlen = dkim_signature->ptr;
298 #ifdef EXPERIMENTAL_ARC
299 if (dkim->arc_signspec) /* Prepend ARC headers */
301 if (!(dkim_signature = arc_sign(dkim->arc_signspec, dkim_signature, USS err)))
303 dlen = dkim_signature->ptr;
308 if (options & topt_use_bdat)
310 if ((k_file_size = lseek(dkim_fd, 0, SEEK_END)) < 0)
312 *err = string_sprintf("dkim spoolfile seek: %s", strerror(errno));
316 if (options & topt_use_bdat)
318 /* On big messages output a precursor chunk to get any pipelined
319 MAIL & RCPT commands flushed, then reap the responses so we can
320 error out on RCPT rejects before sending megabytes. */
322 if ( dlen + k_file_size > DELIVER_OUT_BUFFER_SIZE
325 if ( tctx->chunk_cb(tctx, dlen, 0) != OK
326 || !transport_write_block(tctx,
327 dkim_signature->s, dlen, FALSE)
328 || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
334 /* Send the BDAT command for the entire message, as a single LAST-marked
337 if (tctx->chunk_cb(tctx, dlen + k_file_size, tc_chunk_last) != OK)
341 if(dlen > 0 && !transport_write_block(tctx, dkim_signature->s, dlen, TRUE))
344 if (!dkt_send_file(tctx->u.fd, dkim_fd, 0
356 if (dkim_fd >= 0) (void)close(dkim_fd);
357 Uunlink(dkim_spool_name);
369 /***************************************************************************************************
370 * External interface to write the message, while signing it with DKIM and/or Domainkeys *
371 ***************************************************************************************************/
373 /* This function is a wrapper around transport_write_message().
374 It is only called from the smtp transport if DKIM or Domainkeys support
378 As for transport_write_message() in transort.c, with additional arguments
381 Returns: TRUE on success; FALSE (with errno) for any failure
385 dkim_transport_write_message(transport_ctx * tctx,
386 struct ob_dkim * dkim, const uschar ** err)
390 /* If we can't sign, just call the original function. */
392 if ( !(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector)
393 && !dkim->force_bodyhash)
394 return transport_write_message(tctx, 0);
396 /* If there is no filter command set up, construct the message and calculate
397 a dkim signature of it, send the signature and a reconstructed message. This
398 avoids using a temprary file. */
400 if ( !transport_filter_argv
401 || !*transport_filter_argv
402 || !**transport_filter_argv
404 yield = dkt_direct(tctx, dkim, err);
407 /* Use the transport path to write a file, calculate a dkim signature,
408 send the signature and then send the file. */
410 yield = dkt_via_kfile(tctx, dkim, err);
412 tctx->addr->dkim_used = string_from_gstring(dkim_signing_record);
416 #endif /* whole file */
420 /* End of dkim_transport.c */