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. */
9 /* Transport shim for dkim signing */
14 #ifndef DISABLE_DKIM /* rest of file */
18 dkt_sign_fail(struct ob_dkim * dkim, int * errp)
20 if (dkim->dkim_strict)
22 uschar * dkim_strict_result = expand_string(dkim->dkim_strict);
24 if (dkim_strict_result)
25 if ( strcmpic(dkim_strict_result, US"1") == 0
26 || strcmpic(dkim_strict_result, US"true") == 0)
28 /* Set errno to something halfway meaningful */
30 log_write(0, LOG_MAIN, "DKIM: message could not be signed,"
31 " and dkim_strict is set. Deferring message delivery.");
38 /* Send the file at in_fd down the output fd */
41 dkt_send_file(int out_fd, int in_fd, off_t off
48 DEBUG(D_transport) debug_printf("send file fd=%d size=%u\n", out_fd, (unsigned)(size - off));
50 DEBUG(D_transport) debug_printf("send file fd=%d\n", out_fd);
53 /*XXX should implement timeout, like transport_write_block_fd() ? */
56 /* We can use sendfile() to shove the file contents
57 to the socket. However only if we don't use TLS,
58 as then there's another layer of indirection
59 before the data finally hits the socket. */
60 if (tls_out.active.sock != out_fd)
64 while(copied >= 0 && off < size)
65 copied = os_sendfile(out_fd, in_fd, &off, size - off);
77 if (lseek(in_fd, off, SEEK_SET) < 0) return FALSE;
79 /* Send file down the original fd */
80 while((sread = read(in_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) > 0)
82 uschar * p = deliver_out_buffer;
88 wwritten = tls_out.active.sock == out_fd
89 ? tls_write(tls_out.active.tls_ctx, p, sread, FALSE)
90 : write(out_fd, CS p, sread);
92 wwritten = write(out_fd, CS p, sread);
111 /* This function is a wrapper around transport_write_message().
112 It is only called from the smtp transport if DKIM or Domainkeys support
113 is active and no transport filter is to be used.
116 As for transport_write_message() in transort.c, with additional arguments
119 Returns: TRUE on success; FALSE (with errno) for any failure
123 dkt_direct(transport_ctx * tctx, struct ob_dkim * dkim,
126 int save_fd = tctx->u.fd;
127 int save_options = tctx->options;
128 BOOL save_wireformat = f.spool_file_wireformat;
130 gstring * dkim_signature;
132 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 /* The dotstuffed status of the datafile depends on whether it was stored
161 dkim->dot_stuffed = f.spool_file_wireformat;
162 if (!(dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET,
163 hdrs, dkim, &errstr)))
164 if (!(rc = dkt_sign_fail(dkim, &errno)))
170 #ifdef EXPERIMENTAL_ARC
171 if (dkim->arc_signspec) /* Prepend ARC headers */
174 if (!(dkim_signature = arc_sign(dkim->arc_signspec, dkim_signature, &e)))
182 /* Write the signature and headers into the deliver-out-buffer. This should
183 mean they go out in the same packet as the MAIL, RCPT and (first) BDAT commands
184 (transport_write_message() sizes the BDAT for the buffered amount) - for short
185 messages, the BDAT LAST command. We want no dotstuffing expansion here, it
186 having already been done - but we have to say we want CRLF output format, and
187 temporarily set the marker for possible already-CRLF input. */
189 tctx->options &= ~topt_escape_headers;
190 f.spool_file_wireformat = TRUE;
191 transport_write_reset(0);
192 if ( ( dkim_signature
193 && dkim_signature->ptr > 0
194 && !write_chunk(tctx, dkim_signature->s, dkim_signature->ptr)
196 || !write_chunk(tctx, hdrs, hsize)
200 f.spool_file_wireformat = save_wireformat;
201 tctx->options = save_options | topt_no_headers | topt_continuation;
203 if (!(transport_write_message(tctx, 0)))
206 tctx->options = save_options;
211 /* This function is a wrapper around transport_write_message().
212 It is only called from the smtp transport if DKIM or Domainkeys support
213 is active and a transport filter is to be used. The function sets up a
214 replacement fd into a -K file, then calls the normal function. This way, the
215 exact bits that exim would have put "on the wire" will end up in the file
216 (except for TLS encapsulation, which is the very very last thing). When we
217 are done signing the file, send the signed message down the original fd (or
221 As for transport_write_message() in transort.c, with additional arguments
224 Returns: TRUE on success; FALSE (with errno) for any failure
228 dkt_via_kfile(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err)
233 uschar * dkim_spool_name;
234 gstring * dkim_signature;
237 const uschar * errstr;
239 dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
240 string_sprintf("-%d-K", (int)getpid()));
242 DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
244 if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0)
246 /* Can't create spool file. Ugh. */
249 *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
253 /* Call transport utility function to write the -K file; does the CRLF expansion
254 (but, in the CHUNKING case, neither dot-stuffing nor dot-termination). */
257 int save_fd = tctx->u.fd;
258 tctx->u.fd = dkim_fd;
259 options = tctx->options;
260 tctx->options &= ~topt_use_bdat;
262 rc = transport_write_message(tctx, 0);
264 tctx->u.fd = save_fd;
265 tctx->options = options;
268 /* Save error state. We must clean up before returning. */
275 #ifdef EXPERIMENTAL_ARC
279 /* Feed the file to the goats^W DKIM lib. At this point the dotstuffed
280 status of the file depends on the output of transport_write_message() just
281 above, which should be the result of the end_dot flag in tctx->options. */
283 dkim->dot_stuffed = !!(options & topt_end_dot);
284 if (!(dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
287 if (!(rc = dkt_sign_fail(dkim, &save_errno)))
294 dlen = dkim_signature->ptr;
296 #ifdef EXPERIMENTAL_ARC
297 if (dkim->arc_signspec) /* Prepend ARC headers */
299 if (!(dkim_signature = arc_sign(dkim->arc_signspec, dkim_signature, USS err)))
301 dlen = dkim_signature->ptr;
306 if (options & topt_use_bdat)
308 if ((k_file_size = lseek(dkim_fd, 0, SEEK_END)) < 0)
310 *err = string_sprintf("dkim spoolfile seek: %s", strerror(errno));
314 if (options & topt_use_bdat)
316 /* On big messages output a precursor chunk to get any pipelined
317 MAIL & RCPT commands flushed, then reap the responses so we can
318 error out on RCPT rejects before sending megabytes. */
320 if ( dlen + k_file_size > DELIVER_OUT_BUFFER_SIZE
323 if ( tctx->chunk_cb(tctx, dlen, 0) != OK
324 || !transport_write_block(tctx,
325 dkim_signature->s, dlen, FALSE)
326 || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
332 /* Send the BDAT command for the entire message, as a single LAST-marked
335 if (tctx->chunk_cb(tctx, dlen + k_file_size, tc_chunk_last) != OK)
339 if(dlen > 0 && !transport_write_block(tctx, dkim_signature->s, dlen, TRUE))
342 if (!dkt_send_file(tctx->u.fd, dkim_fd, 0
354 if (dkim_fd >= 0) (void)close(dkim_fd);
355 Uunlink(dkim_spool_name);
367 /***************************************************************************************************
368 * External interface to write the message, while signing it with DKIM and/or Domainkeys *
369 ***************************************************************************************************/
371 /* This function is a wrapper around transport_write_message().
372 It is only called from the smtp transport if DKIM or Domainkeys support
376 As for transport_write_message() in transort.c, with additional arguments
379 Returns: TRUE on success; FALSE (with errno) for any failure
383 dkim_transport_write_message(transport_ctx * tctx,
384 struct ob_dkim * dkim, const uschar ** err)
386 /* If we can't sign, just call the original function. */
388 if ( !(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector)
389 && !dkim->force_bodyhash)
390 return transport_write_message(tctx, 0);
392 /* If there is no filter command set up, construct the message and calculate
393 a dkim signature of it, send the signature and a reconstructed message. This
394 avoids using a temprary file. */
396 if ( !transport_filter_argv
397 || !*transport_filter_argv
398 || !**transport_filter_argv
400 return dkt_direct(tctx, dkim, err);
402 /* Use the transport path to write a file, calculate a dkim signature,
403 send the signature and then send the file. */
405 return dkt_via_kfile(tctx, dkim, err);
408 #endif /* whole file */
412 /* End of dkim_transport.c */