Tidy build for OpenBSD
[exim.git] / src / src / dkim_transport.c
1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2016 */
6 /* See the file NOTICE for conditions of use and distribution. */
7
8 /* Transport shim for dkim signing */
9
10
11 #include "exim.h"
12
13 #ifndef DISABLE_DKIM /* rest of file */
14
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)
24 if ( (strcmpic(dkim->dkim_strict, US"1") == 0) ||
25 (strcmpic(dkim->dkim_strict, US"true") == 0) )
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
37 /* Send the file at in_fd down the output fd */
38
39 static BOOL
40 dkt_send_file(int out_fd, int in_fd, off_t off
41 #ifdef OS_SENDFILE
42 , size_t size
43 #endif
44 )
45 {
46 DEBUG(D_transport) debug_printf("send file fd=%d size=%u\n", out_fd, (unsigned)(size - off));
47
48 /*XXX should implement timeout, like transport_write_block_fd() ? */
49
50 #ifdef OS_SENDFILE
51 /* We can use sendfile() to shove the file contents
52 to the socket. However only if we don't use TLS,
53 as then there's another layer of indirection
54 before the data finally hits the socket. */
55 if (tls_out.active != out_fd)
56 {
57 ssize_t copied = 0;
58
59 while(copied >= 0 && off < size)
60 copied = os_sendfile(out_fd, in_fd, &off, size - off);
61 if (copied < 0)
62 return FALSE;
63 }
64 else
65
66 #endif
67
68 {
69 int sread, wwritten;
70
71 /* Rewind file */
72 if (lseek(in_fd, off, SEEK_SET) < 0) return FALSE;
73
74 /* Send file down the original fd */
75 while((sread = read(in_fd, deliver_out_buffer, DELIVER_OUT_BUFFER_SIZE)) > 0)
76 {
77 uschar * p = deliver_out_buffer;
78 /* write the chunk */
79
80 while (sread)
81 {
82 #ifdef SUPPORT_TLS
83 wwritten = tls_out.active == out_fd
84 ? tls_write(FALSE, p, sread, FALSE)
85 : write(out_fd, CS p, sread);
86 #else
87 wwritten = write(out_fd, CS p, sread);
88 #endif
89 if (wwritten == -1)
90 return FALSE;
91 p += wwritten;
92 sread -= wwritten;
93 }
94 }
95
96 if (sread == -1)
97 return FALSE;
98 }
99
100 return TRUE;
101 }
102
103
104
105
106 /* This function is a wrapper around transport_write_message().
107 It is only called from the smtp transport if DKIM or Domainkeys support
108 is active and no transport filter is to be used.
109
110 Arguments:
111 As for transport_write_message() in transort.c, with additional arguments
112 for DKIM.
113
114 Returns: TRUE on success; FALSE (with errno) for any failure
115 */
116
117 static BOOL
118 dkt_direct(transport_ctx * tctx, struct ob_dkim * dkim,
119 const uschar ** err)
120 {
121 int save_fd = tctx->u.fd;
122 int save_options = tctx->options;
123 BOOL save_wireformat = spool_file_wireformat;
124 uschar * hdrs;
125 gstring * dkim_signature;
126 int hsize;
127 const uschar * errstr;
128 BOOL rc;
129
130 DEBUG(D_transport) debug_printf("dkim signing direct-mode\n");
131
132 /* Get headers in string for signing and transmission. Do CRLF
133 and dotstuffing (but no body nor dot-termination) */
134
135 tctx->u.msg = NULL;
136 tctx->options = tctx->options & ~(topt_end_dot | topt_use_bdat)
137 | topt_output_string | topt_no_body;
138
139 rc = transport_write_message(tctx, 0);
140 hdrs = string_from_gstring(tctx->u.msg);
141 hsize = tctx->u.msg->ptr;
142
143 tctx->u.fd = save_fd;
144 tctx->options = save_options;
145 if (!rc) return FALSE;
146
147 /* Get signatures for headers plus spool data file */
148
149 dkim->dot_stuffed = !!(save_options & topt_end_dot);
150
151 if (!(dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET,
152 hdrs, dkim, &errstr)))
153 if (!(rc = dkt_sign_fail(dkim, &errno)))
154 {
155 *err = errstr;
156 return FALSE;
157 }
158
159 /* Write the signature and headers into the deliver-out-buffer. This should
160 mean they go out in the same packet as the MAIL, RCPT and (first) BDAT commands
161 (transport_write_message() sizes the BDAT for the buffered amount) - for short
162 messages, the BDAT LAST command. We want no dotstuffing expansion here, it
163 having already been done - but we have to say we want CRLF output format, and
164 temporarily set the marker for possible already-CRLF input. */
165
166 tctx->options &= ~topt_escape_headers;
167 spool_file_wireformat = TRUE;
168 transport_write_reset(0);
169 if ( ( dkim_signature
170 && dkim_signature->ptr > 0
171 && !write_chunk(tctx, dkim_signature->s, dkim_signature->ptr)
172 )
173 || !write_chunk(tctx, hdrs, hsize)
174 )
175 return FALSE;
176
177 spool_file_wireformat = save_wireformat;
178 tctx->options = save_options | topt_no_headers | topt_continuation;
179
180 if (!(transport_write_message(tctx, 0)))
181 return FALSE;
182
183 tctx->options = save_options;
184 return TRUE;
185 }
186
187
188 /* This function is a wrapper around transport_write_message().
189 It is only called from the smtp transport if DKIM or Domainkeys support
190 is active and a transport filter is to be used. The function sets up a
191 replacement fd into a -K file, then calls the normal function. This way, the
192 exact bits that exim would have put "on the wire" will end up in the file
193 (except for TLS encapsulation, which is the very very last thing). When we
194 are done signing the file, send the signed message down the original fd (or
195 TLS fd).
196
197 Arguments:
198 As for transport_write_message() in transort.c, with additional arguments
199 for DKIM.
200
201 Returns: TRUE on success; FALSE (with errno) for any failure
202 */
203
204 static BOOL
205 dkt_via_kfile(transport_ctx * tctx, struct ob_dkim * dkim, const uschar ** err)
206 {
207 int dkim_fd;
208 int save_errno = 0;
209 BOOL rc;
210 uschar * dkim_spool_name;
211 gstring * dkim_signature;
212 int options, dlen;
213 off_t k_file_size;
214 const uschar * errstr;
215
216 dkim_spool_name = spool_fname(US"input", message_subdir, message_id,
217 string_sprintf("-%d-K", (int)getpid()));
218
219 DEBUG(D_transport) debug_printf("dkim signing via file %s\n", dkim_spool_name);
220
221 if ((dkim_fd = Uopen(dkim_spool_name, O_RDWR|O_CREAT|O_TRUNC, SPOOL_MODE)) < 0)
222 {
223 /* Can't create spool file. Ugh. */
224 rc = FALSE;
225 save_errno = errno;
226 *err = string_sprintf("dkim spoolfile create: %s", strerror(errno));
227 goto CLEANUP;
228 }
229
230 /* Call transport utility function to write the -K file; does the CRLF expansion
231 (but, in the CHUNKING case, neither dot-stuffing nor dot-termination). */
232
233 {
234 int save_fd = tctx->u.fd;
235 tctx->u.fd = dkim_fd;
236 options = tctx->options;
237 tctx->options &= ~topt_use_bdat;
238
239 rc = transport_write_message(tctx, 0);
240
241 tctx->u.fd = save_fd;
242 tctx->options = options;
243 }
244
245 /* Save error state. We must clean up before returning. */
246 if (!rc)
247 {
248 save_errno = errno;
249 goto CLEANUP;
250 }
251
252 /* Feed the file to the goats^W DKIM lib */
253
254 dkim->dot_stuffed = !!(options & topt_end_dot);
255 if (!(dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr)))
256 {
257 dlen = 0;
258 if (!(rc = dkt_sign_fail(dkim, &save_errno)))
259 {
260 *err = errstr;
261 goto CLEANUP;
262 }
263 }
264 else
265 dlen = dkim_signature->ptr;
266
267 #ifndef OS_SENDFILE
268 if (options & topt_use_bdat)
269 #endif
270 if ((k_file_size = lseek(dkim_fd, 0, SEEK_END)) < 0)
271 {
272 *err = string_sprintf("dkim spoolfile seek: %s", strerror(errno));
273 goto CLEANUP;
274 }
275
276 if (options & topt_use_bdat)
277 {
278 /* On big messages output a precursor chunk to get any pipelined
279 MAIL & RCPT commands flushed, then reap the responses so we can
280 error out on RCPT rejects before sending megabytes. */
281
282 if ( dlen + k_file_size > DELIVER_OUT_BUFFER_SIZE
283 && dlen > 0)
284 {
285 if ( tctx->chunk_cb(tctx, dlen, 0) != OK
286 || !transport_write_block(tctx,
287 dkim_signature->s, dlen, FALSE)
288 || tctx->chunk_cb(tctx, 0, tc_reap_prev) != OK
289 )
290 goto err;
291 dlen = 0;
292 }
293
294 /* Send the BDAT command for the entire message, as a single LAST-marked
295 chunk. */
296
297 if (tctx->chunk_cb(tctx, dlen + k_file_size, tc_chunk_last) != OK)
298 goto err;
299 }
300
301 if(dlen > 0 && !transport_write_block(tctx, dkim_signature->s, dlen, TRUE))
302 goto err;
303
304 if (!dkt_send_file(tctx->u.fd, dkim_fd, 0
305 #ifdef OS_SENDFILE
306 , k_file_size
307 #endif
308 ))
309 {
310 save_errno = errno;
311 rc = FALSE;
312 }
313
314 CLEANUP:
315 /* unlink -K file */
316 if (dkim_fd >= 0) (void)close(dkim_fd);
317 Uunlink(dkim_spool_name);
318 errno = save_errno;
319 return rc;
320
321 err:
322 save_errno = errno;
323 rc = FALSE;
324 goto CLEANUP;
325 }
326
327
328
329 /***************************************************************************************************
330 * External interface to write the message, while signing it with DKIM and/or Domainkeys *
331 ***************************************************************************************************/
332
333 /* This function is a wrapper around transport_write_message().
334 It is only called from the smtp transport if DKIM or Domainkeys support
335 is compiled in.
336
337 Arguments:
338 As for transport_write_message() in transort.c, with additional arguments
339 for DKIM.
340
341 Returns: TRUE on success; FALSE (with errno) for any failure
342 */
343
344 BOOL
345 dkim_transport_write_message(transport_ctx * tctx,
346 struct ob_dkim * dkim, const uschar ** err)
347 {
348 /* If we can't sign, just call the original function. */
349
350 if (!(dkim->dkim_private_key && dkim->dkim_domain && dkim->dkim_selector))
351 return transport_write_message(tctx, 0);
352
353 /* If there is no filter command set up, construct the message and calculate
354 a dkim signature of it, send the signature and a reconstructed message. This
355 avoids using a temprary file. */
356
357 if ( !transport_filter_argv
358 || !*transport_filter_argv
359 || !**transport_filter_argv
360 )
361 return dkt_direct(tctx, dkim, err);
362
363 /* Use the transport path to write a file, calculate a dkim signature,
364 send the signature and then send the file. */
365
366 return dkt_via_kfile(tctx, dkim, err);
367 }
368
369 #endif /* whole file */
370
371 /* vi: aw ai sw=2
372 */
373 /* End of dkim_transport.c */