Commit | Line | Data |
---|---|---|
42055a33 JH |
1 | /************************************************* |
2 | * Exim - an Internet mail transport agent * | |
3 | *************************************************/ | |
4 | ||
f9ba5e22 | 5 | /* Copyright (c) University of Cambridge 1995 - 2018 */ |
42055a33 JH |
6 | /* See the file NOTICE for conditions of use and distribution. */ |
7 | ||
8 | /* Transport shim for dkim signing */ | |
9 | ||
42055a33 JH |
10 | |
11 | #include "exim.h" | |
12 | ||
09d5060c JH |
13 | #ifndef DISABLE_DKIM /* rest of file */ |
14 | ||
42055a33 JH |
15 | |
16 | static BOOL | |
17 | dkt_sign_fail(struct ob_dkim * dkim, int * errp) | |
18 | { | |
19 | if (dkim->dkim_strict) | |
20 | { | |
21 | uschar * dkim_strict_result = expand_string(dkim->dkim_strict); | |
22 | ||
23 | if (dkim_strict_result) | |
9fa4d5b4 RJ |
24 | if ( strcmpic(dkim_strict_result, US"1") == 0 |
25 | || strcmpic(dkim_strict_result, US"true") == 0) | |
42055a33 JH |
26 | { |
27 | /* Set errno to something halfway meaningful */ | |
28 | *errp = EACCES; | |
29 | log_write(0, LOG_MAIN, "DKIM: message could not be signed," | |
30 | " and dkim_strict is set. Deferring message delivery."); | |
31 | return FALSE; | |
32 | } | |
33 | } | |
34 | return TRUE; | |
35 | } | |
36 | ||
d315eda1 JH |
37 | /* Send the file at in_fd down the output fd */ |
38 | ||
42055a33 | 39 | static BOOL |
bb07bcd3 JH |
40 | dkt_send_file(int out_fd, int in_fd, off_t off |
41 | #ifdef OS_SENDFILE | |
42 | , size_t size | |
43 | #endif | |
44 | ) | |
42055a33 | 45 | { |
47c292c9 | 46 | #ifdef OS_SENDFILE |
6c140c01 | 47 | DEBUG(D_transport) debug_printf("send file fd=%d size=%u\n", out_fd, (unsigned)(size - off)); |
47c292c9 JH |
48 | #else |
49 | DEBUG(D_transport) debug_printf("send file fd=%d\n", out_fd); | |
50 | #endif | |
42055a33 JH |
51 | |
52 | /*XXX should implement timeout, like transport_write_block_fd() ? */ | |
53 | ||
7d758a6a | 54 | #ifdef OS_SENDFILE |
42055a33 JH |
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. */ | |
74f1a423 | 59 | if (tls_out.active.sock != out_fd) |
42055a33 JH |
60 | { |
61 | ssize_t copied = 0; | |
62 | ||
63 | while(copied >= 0 && off < size) | |
7d758a6a | 64 | copied = os_sendfile(out_fd, in_fd, &off, size - off); |
42055a33 JH |
65 | if (copied < 0) |
66 | return FALSE; | |
67 | } | |
68 | else | |
69 | ||
70 | #endif | |
71 | ||
72 | { | |
73 | int sread, wwritten; | |
74 | ||
328c5688 | 75 | /* Rewind file */ |
d315eda1 | 76 | if (lseek(in_fd, off, SEEK_SET) < 0) return FALSE; |
328c5688 | 77 | |
42055a33 | 78 | /* Send file down the original fd */ |
d315eda1 | 79 | while((sread = read(in_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) > 0) |
42055a33 JH |
80 | { |
81 | uschar * p = deliver_out_buffer; | |
82 | /* write the chunk */ | |
83 | ||
84 | while (sread) | |
85 | { | |
01603eec | 86 | #ifndef DISABLE_TLS |
74f1a423 JH |
87 | wwritten = tls_out.active.sock == out_fd |
88 | ? tls_write(tls_out.active.tls_ctx, p, sread, FALSE) | |
42055a33 JH |
89 | : write(out_fd, CS p, sread); |
90 | #else | |
91 | wwritten = write(out_fd, CS p, sread); | |
92 | #endif | |
93 | if (wwritten == -1) | |
94 | return FALSE; | |
95 | p += wwritten; | |
96 | sread -= wwritten; | |
97 | } | |
98 | } | |
99 | ||
100 | if (sread == -1) | |
101 | return FALSE; | |
102 | } | |
103 | ||
104 | return TRUE; | |
105 | } | |
106 | ||
107 | ||
108 | ||
109 | ||
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. | |
113 | ||
114 | Arguments: | |
115 | As for transport_write_message() in transort.c, with additional arguments | |
116 | for DKIM. | |
117 | ||
118 | Returns: TRUE on success; FALSE (with errno) for any failure | |
119 | */ | |
120 | ||
121 | static BOOL | |
122 | dkt_direct(transport_ctx * tctx, struct ob_dkim * dkim, | |
123 | const uschar ** err) | |
124 | { | |
125 | int save_fd = tctx->u.fd; | |
126 | int save_options = tctx->options; | |
8768d548 | 127 | BOOL save_wireformat = f.spool_file_wireformat; |
9e70917d | 128 | uschar * hdrs; |
acec9514 | 129 | gstring * dkim_signature; |
9e70917d | 130 | int hsize; |
42055a33 JH |
131 | const uschar * errstr; |
132 | BOOL rc; | |
133 | ||
134 | DEBUG(D_transport) debug_printf("dkim signing direct-mode\n"); | |
135 | ||
328c5688 JH |
136 | /* Get headers in string for signing and transmission. Do CRLF |
137 | and dotstuffing (but no body nor dot-termination) */ | |
42055a33 JH |
138 | |
139 | tctx->u.msg = NULL; | |
140 | tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat) | |
141 | | topt_output_string | topt_no_body; | |
142 | ||
143 | rc = transport_write_message(tctx, 0); | |
acec9514 JH |
144 | hdrs = string_from_gstring(tctx->u.msg); |
145 | hsize = tctx->u.msg->ptr; | |
42055a33 JH |
146 | |
147 | tctx->u.fd = save_fd; | |
148 | tctx->options = save_options; | |
149 | if (!rc) return FALSE; | |
150 | ||
151 | /* Get signatures for headers plus spool data file */ | |
152 | ||
b3d9ebf5 JH |
153 | #ifdef EXPERIMENTAL_ARC |
154 | arc_sign_init(); | |
155 | #endif | |
42055a33 | 156 | |
49e56fb3 JH |
157 | /* The dotstuffed status of the datafile depends on whether it was stored |
158 | in wireformat. */ | |
159 | ||
8768d548 | 160 | dkim->dot_stuffed = f.spool_file_wireformat; |
9e70917d | 161 | if (!(dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET, |
42055a33 | 162 | hdrs, dkim, &errstr))) |
9e70917d JH |
163 | if (!(rc = dkt_sign_fail(dkim, &errno))) |
164 | { | |
165 | *err = errstr; | |
166 | return FALSE; | |
167 | } | |
42055a33 | 168 | |
617d3932 JH |
169 | #ifdef EXPERIMENTAL_ARC |
170 | if (dkim->arc_signspec) /* Prepend ARC headers */ | |
0e8aed8a JH |
171 | { |
172 | uschar * e; | |
173 | if (!(dkim_signature = arc_sign(dkim->arc_signspec, dkim_signature, &e))) | |
617d3932 | 174 | { |
0e8aed8a | 175 | *err = e; |
617d3932 JH |
176 | return FALSE; |
177 | } | |
0e8aed8a | 178 | } |
617d3932 JH |
179 | #endif |
180 | ||
42055a33 JH |
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 | |
328c5688 JH |
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. */ | |
42055a33 | 187 | |
328c5688 | 188 | tctx->options &= ~topt_escape_headers; |
8768d548 | 189 | f.spool_file_wireformat = TRUE; |
42055a33 | 190 | transport_write_reset(0); |
9e70917d | 191 | if ( ( dkim_signature |
acec9514 JH |
192 | && dkim_signature->ptr > 0 |
193 | && !write_chunk(tctx, dkim_signature->s, dkim_signature->ptr) | |
9e70917d JH |
194 | ) |
195 | || !write_chunk(tctx, hdrs, hsize) | |
196 | ) | |
42055a33 JH |
197 | return FALSE; |
198 | ||
8768d548 | 199 | f.spool_file_wireformat = save_wireformat; |
42055a33 JH |
200 | tctx->options = save_options | topt_no_headers | topt_continuation; |
201 | ||
202 | if (!(transport_write_message(tctx, 0))) | |
203 | return FALSE; | |
204 | ||
205 | tctx->options = save_options; | |
206 | return TRUE; | |
207 | } | |
208 | ||
209 | ||
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 | |
217 | TLS fd). | |
218 | ||
219 | Arguments: | |
220 | As for transport_write_message() in transort.c, with additional arguments | |
221 | for DKIM. | |
222 | ||
223 | Returns: TRUE on success; FALSE (with errno) for any failure | |
224 | */ | |
225 | ||
226 | static BOOL | |
227 | dkt_via_kfile(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err) | |
228 | { | |
229 | int dkim_fd; | |
230 | int save_errno = 0; | |
231 | BOOL rc; | |
9e70917d | 232 | uschar * dkim_spool_name; |
acec9514 | 233 | gstring * dkim_signature; |
9e70917d | 234 | int options, dlen; |
42055a33 JH |
235 | off_t k_file_size; |
236 | const uschar * errstr; | |
237 | ||
238 | dkim_spool_name = spool_fname(US"input", message_subdir, message_id, | |
239 | string_sprintf("-%d-K", (int)getpid())); | |
240 | ||
241 | DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name); | |
242 | ||
243 | if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0) | |
244 | { | |
245 | /* Can't create spool file. Ugh. */ | |
246 | rc = FALSE; | |
247 | save_errno = errno; | |
248 | *err = string_sprintf("dkim spoolfile create: %s", strerror(errno)); | |
249 | goto CLEANUP; | |
250 | } | |
251 | ||
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). */ | |
254 | ||
255 | { | |
256 | int save_fd = tctx->u.fd; | |
257 | tctx->u.fd = dkim_fd; | |
258 | options = tctx->options; | |
259 | tctx->options &= ~topt_use_bdat; | |
260 | ||
261 | rc = transport_write_message(tctx, 0); | |
262 | ||
263 | tctx->u.fd = save_fd; | |
264 | tctx->options = options; | |
265 | } | |
266 | ||
267 | /* Save error state. We must clean up before returning. */ | |
268 | if (!rc) | |
269 | { | |
270 | save_errno = errno; | |
271 | goto CLEANUP; | |
272 | } | |
273 | ||
b3d9ebf5 JH |
274 | #ifdef EXPERIMENTAL_ARC |
275 | arc_sign_init(); | |
276 | #endif | |
277 | ||
49e56fb3 JH |
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. */ | |
42055a33 JH |
281 | |
282 | dkim->dot_stuffed = !!(options & topt_end_dot); | |
9e70917d | 283 | if (!(dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr))) |
42055a33 | 284 | { |
9e70917d JH |
285 | dlen = 0; |
286 | if (!(rc = dkt_sign_fail(dkim, &save_errno))) | |
287 | { | |
288 | *err = errstr; | |
289 | goto CLEANUP; | |
290 | } | |
42055a33 | 291 | } |
9e70917d | 292 | else |
acec9514 | 293 | dlen = dkim_signature->ptr; |
42055a33 | 294 | |
617d3932 JH |
295 | #ifdef EXPERIMENTAL_ARC |
296 | if (dkim->arc_signspec) /* Prepend ARC headers */ | |
297 | { | |
298 | if (!(dkim_signature = arc_sign(dkim->arc_signspec, dkim_signature, USS err))) | |
299 | goto CLEANUP; | |
300 | dlen = dkim_signature->ptr; | |
301 | } | |
302 | #endif | |
303 | ||
7d758a6a | 304 | #ifndef OS_SENDFILE |
42055a33 JH |
305 | if (options & topt_use_bdat) |
306 | #endif | |
d315eda1 JH |
307 | if ((k_file_size = lseek(dkim_fd, 0, SEEK_END)) < 0) |
308 | { | |
309 | *err = string_sprintf("dkim spoolfile seek: %s", strerror(errno)); | |
310 | goto CLEANUP; | |
311 | } | |
42055a33 JH |
312 | |
313 | if (options & topt_use_bdat) | |
314 | { | |
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. */ | |
318 | ||
9e70917d JH |
319 | if ( dlen + k_file_size > DELIVER_OUT_BUFFER_SIZE |
320 | && dlen > 0) | |
42055a33 | 321 | { |
9e70917d JH |
322 | if ( tctx->chunk_cb(tctx, dlen, 0) != OK |
323 | || !transport_write_block(tctx, | |
acec9514 | 324 | dkim_signature->s, dlen, FALSE) |
42055a33 JH |
325 | || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK |
326 | ) | |
327 | goto err; | |
9e70917d | 328 | dlen = 0; |
42055a33 JH |
329 | } |
330 | ||
331 | /* Send the BDAT command for the entire message, as a single LAST-marked | |
332 | chunk. */ | |
333 | ||
9e70917d | 334 | if (tctx->chunk_cb(tctx, dlen + k_file_size, tc_chunk_last) != OK) |
42055a33 JH |
335 | goto err; |
336 | } | |
337 | ||
acec9514 | 338 | if(dlen > 0 && !transport_write_block(tctx, dkim_signature->s, dlen, TRUE)) |
42055a33 JH |
339 | goto err; |
340 | ||
bb07bcd3 JH |
341 | if (!dkt_send_file(tctx->u.fd, dkim_fd, 0 |
342 | #ifdef OS_SENDFILE | |
343 | , k_file_size | |
344 | #endif | |
345 | )) | |
42055a33 JH |
346 | { |
347 | save_errno = errno; | |
348 | rc = FALSE; | |
349 | } | |
350 | ||
351 | CLEANUP: | |
352 | /* unlink -K file */ | |
d315eda1 | 353 | if (dkim_fd >= 0) (void)close(dkim_fd); |
42055a33 JH |
354 | Uunlink(dkim_spool_name); |
355 | errno = save_errno; | |
356 | return rc; | |
357 | ||
358 | err: | |
359 | save_errno = errno; | |
360 | rc = FALSE; | |
361 | goto CLEANUP; | |
362 | } | |
363 | ||
364 | ||
365 | ||
366 | /*************************************************************************************************** | |
367 | * External interface to write the message, while signing it with DKIM and/or Domainkeys * | |
368 | ***************************************************************************************************/ | |
369 | ||
370 | /* This function is a wrapper around transport_write_message(). | |
371 | It is only called from the smtp transport if DKIM or Domainkeys support | |
372 | is compiled in. | |
373 | ||
374 | Arguments: | |
375 | As for transport_write_message() in transort.c, with additional arguments | |
376 | for DKIM. | |
377 | ||
378 | Returns: TRUE on success; FALSE (with errno) for any failure | |
379 | */ | |
380 | ||
381 | BOOL | |
382 | dkim_transport_write_message(transport_ctx * tctx, | |
383 | struct ob_dkim * dkim, const uschar ** err) | |
384 | { | |
385 | /* If we can't sign, just call the original function. */ | |
386 | ||
617d3932 JH |
387 | if ( !(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector) |
388 | && !dkim->force_bodyhash) | |
42055a33 JH |
389 | return transport_write_message(tctx, 0); |
390 | ||
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. */ | |
394 | ||
395 | if ( !transport_filter_argv | |
396 | || !*transport_filter_argv | |
397 | || !**transport_filter_argv | |
398 | ) | |
399 | return dkt_direct(tctx, dkim, err); | |
400 | ||
401 | /* Use the transport path to write a file, calculate a dkim signature, | |
402 | send the signature and then send the file. */ | |
403 | ||
404 | return dkt_via_kfile(tctx, dkim, err); | |
405 | } | |
406 | ||
407 | #endif /* whole file */ | |
408 | ||
409 | /* vi: aw ai sw=2 | |
410 | */ | |
411 | /* End of dkim_transport.c */ |