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.sock != 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.sock == out_fd
88 ? tls_write(tls_out.active.tls_ctx, 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;
134 DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
136 /* Get headers in string for signing and transmission. Do CRLF
137 and dotstuffing (but no body nor dot-termination) */
140 tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat)
141 | topt_output_string | topt_no_body;
143 rc = transport_write_message(tctx, 0);
144 hdrs = string_from_gstring(tctx->u.msg);
145 hsize = tctx->u.msg->ptr;
147 tctx->u.fd = save_fd;
148 tctx->options = save_options;
149 if (!rc) return FALSE;
151 /* Get signatures for headers plus spool data file */
153 #ifdef EXPERIMENTAL_ARC
157 /* The dotstuffed status of the datafile depends on whether it was stored
160 dkim->dot_stuffed = spool_file_wireformat;
161 if (!(dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET,
162 hdrs, dkim, &errstr)))
163 if (!(rc = dkt_sign_fail(dkim, &errno)))
169 #ifdef EXPERIMENTAL_ARC
170 if (dkim->arc_signspec) /* Prepend ARC headers */
173 if (!(dkim_signature = arc_sign(dkim->arc_signspec, dkim_signature, &e)))
181 /* Write the signature and headers into the deliver-out-buffer. This should
182 mean they go out in the same packet as the MAIL, RCPT and (first) BDAT commands
183 (transport_write_message() sizes the BDAT for the buffered amount) - for short
184 messages, the BDAT LAST command. We want no dotstuffing expansion here, it
185 having already been done - but we have to say we want CRLF output format, and
186 temporarily set the marker for possible already-CRLF input. */
188 tctx->options &= ~topt_escape_headers;
189 spool_file_wireformat = TRUE;
190 transport_write_reset(0);
191 if ( ( dkim_signature
192 && dkim_signature->ptr > 0
193 && !write_chunk(tctx, dkim_signature->s, dkim_signature->ptr)
195 || !write_chunk(tctx, hdrs, hsize)
199 spool_file_wireformat = save_wireformat;
200 tctx->options = save_options | topt_no_headers | topt_continuation;
202 if (!(transport_write_message(tctx, 0)))
205 tctx->options = save_options;
210 /* This function is a wrapper around transport_write_message().
211 It is only called from the smtp transport if DKIM or Domainkeys support
212 is active and a transport filter is to be used. The function sets up a
213 replacement fd into a -K file, then calls the normal function. This way, the
214 exact bits that exim would have put "on the wire" will end up in the file
215 (except for TLS encapsulation, which is the very very last thing). When we
216 are done signing the file, send the signed message down the original fd (or
220 As for transport_write_message() in transort.c, with additional arguments
223 Returns: TRUE on success; FALSE (with errno) for any failure
227 dkt_via_kfile(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err)
232 uschar * dkim_spool_name;
233 gstring * dkim_signature;
236 const uschar * errstr;
238 dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
239 string_sprintf("-%d-K", (int)getpid()));
241 DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
243 if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0)
245 /* Can't create spool file. Ugh. */
248 *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
252 /* Call transport utility function to write the -K file; does the CRLF expansion
253 (but, in the CHUNKING case, neither dot-stuffing nor dot-termination). */
256 int save_fd = tctx->u.fd;
257 tctx->u.fd = dkim_fd;
258 options = tctx->options;
259 tctx->options &= ~topt_use_bdat;
261 rc = transport_write_message(tctx, 0);
263 tctx->u.fd = save_fd;
264 tctx->options = options;
267 /* Save error state. We must clean up before returning. */
274 #ifdef EXPERIMENTAL_ARC
278 /* Feed the file to the goats^W DKIM lib. At this point the dotstuffed
279 status of the file depends on the output of transport_write_message() just
280 above, which should be the result of the end_dot flag in tctx->options. */
282 dkim->dot_stuffed = !!(options & topt_end_dot);
283 if (!(dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
286 if (!(rc = dkt_sign_fail(dkim, &save_errno)))
293 dlen = dkim_signature->ptr;
295 #ifdef EXPERIMENTAL_ARC
296 if (dkim->arc_signspec) /* Prepend ARC headers */
298 if (!(dkim_signature = arc_sign(dkim->arc_signspec, dkim_signature, USS err)))
300 dlen = dkim_signature->ptr;
305 if (options & topt_use_bdat)
307 if ((k_file_size = lseek(dkim_fd, 0, SEEK_END)) < 0)
309 *err = string_sprintf("dkim spoolfile seek: %s", strerror(errno));
313 if (options & topt_use_bdat)
315 /* On big messages output a precursor chunk to get any pipelined
316 MAIL & RCPT commands flushed, then reap the responses so we can
317 error out on RCPT rejects before sending megabytes. */
319 if ( dlen + k_file_size > DELIVER_OUT_BUFFER_SIZE
322 if ( tctx->chunk_cb(tctx, dlen, 0) != OK
323 || !transport_write_block(tctx,
324 dkim_signature->s, dlen, FALSE)
325 || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
331 /* Send the BDAT command for the entire message, as a single LAST-marked
334 if (tctx->chunk_cb(tctx, dlen + k_file_size, tc_chunk_last) != OK)
338 if(dlen > 0 && !transport_write_block(tctx, dkim_signature->s, dlen, TRUE))
341 if (!dkt_send_file(tctx->u.fd, dkim_fd, 0
353 if (dkim_fd >= 0) (void)close(dkim_fd);
354 Uunlink(dkim_spool_name);
366 /***************************************************************************************************
367 * External interface to write the message, while signing it with DKIM and/or Domainkeys *
368 ***************************************************************************************************/
370 /* This function is a wrapper around transport_write_message().
371 It is only called from the smtp transport if DKIM or Domainkeys support
375 As for transport_write_message() in transort.c, with additional arguments
378 Returns: TRUE on success; FALSE (with errno) for any failure
382 dkim_transport_write_message(transport_ctx * tctx,
383 struct ob_dkim * dkim, const uschar ** err)
385 /* If we can't sign, just call the original function. */
387 if ( !(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector)
388 && !dkim->force_bodyhash)
389 return transport_write_message(tctx, 0);
391 /* If there is no filter command set up, construct the message and calculate
392 a dkim signature of it, send the signature and a reconstructed message. This
393 avoids using a temprary file. */
395 if ( !transport_filter_argv
396 || !*transport_filter_argv
397 || !**transport_filter_argv
399 return dkt_direct(tctx, dkim, err);
401 /* Use the transport path to write a file, calculate a dkim signature,
402 send the signature and then send the file. */
404 return dkt_via_kfile(tctx, dkim, err);
407 #endif /* whole file */
411 /* End of dkim_transport.c */