9e20b5bca977fae7525b13e6c93bc973f426fd8c
[exim.git] / src / src / tls.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) The Exim Maintainers 2020 - 2022 */
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-only */
9
10 /* This module provides TLS (aka SSL) support for Exim. The code for OpenSSL is
11 based on a patch that was originally contributed by Steve Haslam. It was
12 adapted from stunnel, a GPL program by Michal Trojnara. The code for GNU TLS is
13 based on a patch contributed by Nikos Mavrogiannopoulos. Because these packages
14 are so very different, the functions for each are kept in separate files. The
15 relevant file is #included as required, after any any common functions.
16
17 No cryptographic code is included in Exim. All this module does is to call
18 functions from the OpenSSL or GNU TLS libraries. */
19
20
21 #include "exim.h"
22 #include "transports/smtp.h"
23
24 #if !defined(DISABLE_TLS) && !defined(USE_OPENSSL) && !defined(USE_GNUTLS)
25 # error One of USE_OPENSSL or USE_GNUTLS must be defined for a TLS build
26 #endif
27
28
29 /* Forward decl. */
30 static void tls_client_resmption_key(tls_support *, smtp_connect_args *,
31   smtp_transport_options_block *);
32
33
34 #if defined(MACRO_PREDEF) && !defined(DISABLE_TLS)
35 # include "macro_predef.h"
36 # ifdef USE_GNUTLS
37 #  include "tls-gnu.c"
38 # else
39 #  include "tls-openssl.c"
40 # endif
41 #endif
42
43 #ifndef MACRO_PREDEF
44
45 static void tls_per_lib_daemon_init(void);
46 static void tls_per_lib_daemon_tick(void);
47 static unsigned  tls_server_creds_init(void);
48 static void tls_server_creds_invalidate(void);
49 static void tls_client_creds_init(transport_instance *, BOOL);
50 static void tls_client_creds_invalidate(transport_instance *);
51 static void tls_daemon_creds_reload(void);
52 static BOOL opt_set_and_noexpand(const uschar *);
53 static BOOL opt_unset_or_noexpand(const uschar *);
54
55
56
57 /* This module is compiled only when it is specifically requested in the
58 build-time configuration. However, some compilers don't like compiling empty
59 modules, so keep them happy with a dummy when skipping the rest. Make it
60 reference itself to stop picky compilers complaining that it is unused, and put
61 in a dummy argument to stop even pickier compilers complaining about infinite
62 loops. */
63
64 #ifdef DISABLE_TLS
65 static void dummy(int x) { dummy(x-1); }
66 #else   /* most of the rest of the file */
67
68 const exim_tlslib_state null_tls_preload = {0};
69
70 /* Static variables that are used for buffering data by both sets of
71 functions and the common functions below.
72
73 We're moving away from this; GnuTLS is already using a state, which
74 can switch, so we can do TLS callouts during ACLs. */
75
76 static const int ssl_xfer_buffer_size = 4096;
77 #ifdef USE_OPENSSL
78 static uschar *ssl_xfer_buffer = NULL;
79 static int ssl_xfer_buffer_lwm = 0;
80 static int ssl_xfer_buffer_hwm = 0;
81 static int ssl_xfer_eof = FALSE;
82 static BOOL ssl_xfer_error = FALSE;
83 #endif
84
85 #ifdef EXIM_HAVE_KEVENT
86 # define KEV_SIZE 16    /* Eight file,dir pairs */
87 static struct kevent kev[KEV_SIZE];
88 static int kev_used = 0;
89 #endif
90
91 static unsigned tls_creds_expire = 0;
92
93 /*************************************************
94 *       Expand string; give error on failure     *
95 *************************************************/
96
97 /* If expansion is forced to fail, set the result NULL and return TRUE.
98 Other failures return FALSE. For a server, an SMTP response is given.
99
100 Arguments:
101   s         the string to expand; if NULL just return TRUE
102   name      name of string being expanded (for error)
103   result    where to put the result
104
105 Returns:    TRUE if OK; result may still be NULL after forced failure
106 */
107
108 static BOOL
109 expand_check(const uschar *s, const uschar *name, uschar **result, uschar ** errstr)
110 {
111 if (!s)
112   *result = NULL;
113 else if (  !(*result = expand_string(US s)) /* need to clean up const more */
114         && !f.expand_string_forcedfail
115         )
116   {
117   *errstr = US"Internal error";
118   log_write(0, LOG_MAIN|LOG_PANIC, "expansion of %s failed: %s", name,
119     expand_string_message);
120   return FALSE;
121   }
122 return TRUE;
123 }
124
125
126 #if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
127 /* Add the directory for a filename to the inotify handle, creating that if
128 needed.  This is enough to see changes to files in that dir.
129 Return boolean success.
130
131 The word "system" fails, which is on the safe side as we don't know what
132 directory it implies nor if the TLS library handles a watch for us.
133
134 The string "system,cache" is recognised and explicitly accepted without
135 setting a watch.  This permits the system CA bundle to be cached even though
136 we have no way to tell when it gets modified by an update.
137 The call chain for OpenSSL uses a (undocumented) call into the library
138 to discover the actual file.  We don't know what GnuTLS uses.
139
140 A full set of caching including the CAs takes 35ms output off of the
141 server tls_init() (GnuTLS, Fedora 32, 2018-class x86_64 laptop hardware).
142 */
143 static BOOL
144 tls_set_one_watch(const uschar * filename)
145 # ifdef EXIM_HAVE_INOTIFY
146 {
147 uschar buf[PATH_MAX];
148 ssize_t len;
149 uschar * s;
150
151 if (Ustrcmp(filename, "system,cache") == 0) return TRUE;
152 if (!(s = Ustrrchr(filename, '/'))) return FALSE;
153
154 for (unsigned loop = 20;
155      (len = readlink(CCS filename, CS buf, sizeof(buf))) >= 0; )
156   {                                             /* a symlink */
157   if (--loop == 0) { errno = ELOOP; return FALSE; }
158   filename = buf[0] == '/'
159     ? string_copyn(buf, (unsigned)len)  /* mem released by tls_set_watch */
160     : string_sprintf("%.*s/%.*s", (int)(s - filename), filename, (int)len, buf);
161   s = Ustrrchr(filename, '/');
162   }
163 if (errno != EINVAL)
164   return FALSE;                                 /* other error */
165
166 /* not a symlink */
167 s = string_copyn(filename, s - filename);       /* mem released by tls_set_watch */
168
169 DEBUG(D_tls) debug_printf("watch dir '%s'\n", s);
170
171 if (inotify_add_watch(tls_watch_fd, CCS s,
172       IN_ONESHOT | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF
173       | IN_MOVED_FROM | IN_MOVED_TO | IN_MOVE_SELF) >= 0)
174   return TRUE;
175 DEBUG(D_tls) debug_printf("notify_add_watch: %s\n", strerror(errno));
176 return FALSE;
177 }
178 # endif
179 # ifdef EXIM_HAVE_KEVENT
180 {
181 uschar * s, * t;
182 int fd1, fd2, i, j, cnt = 0;
183 struct stat sb;
184 #ifdef OpenBSD
185 struct kevent k_dummy;
186 struct timespec ts = {0};
187 #endif
188
189 errno = 0;
190 if (Ustrcmp(filename, "system,cache") == 0) return TRUE;
191
192 for (;;)
193   {
194   if (kev_used > KEV_SIZE-2) { s = US"out of kev space"; goto bad; }
195   if (!(s = Ustrrchr(filename, '/'))) return FALSE;
196   s = string_copyn(filename, s - filename);     /* mem released by tls_set_watch */
197
198   /* The dir open will fail if there is a symlink on the path. Fine; it's too
199   much effort to handle all possible cases; just refuse the preload. */
200
201   if ((fd2 = open(CCS s, O_RDONLY | O_NOFOLLOW)) < 0) { s = US"open dir"; goto bad; }
202
203   if ((lstat(CCS filename, &sb)) < 0) { s = US"lstat"; goto bad; }
204   if (!S_ISLNK(sb.st_mode))
205     {
206     if ((fd1 = open(CCS filename, O_RDONLY | O_NOFOLLOW)) < 0)
207       { s = US"open file"; goto bad; }
208     DEBUG(D_tls) debug_printf("watch file '%s':\t%d\n", filename, fd1);
209     EV_SET(&kev[kev_used++],
210         (uintptr_t)fd1,
211         EVFILT_VNODE,
212         EV_ADD | EV_ENABLE | EV_ONESHOT,
213         NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND
214         | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE,
215         0,
216         NULL);
217     cnt++;
218     }
219   DEBUG(D_tls) debug_printf("watch dir  '%s':\t%d\n", s, fd2);
220   EV_SET(&kev[kev_used++],
221         (uintptr_t)fd2,
222         EVFILT_VNODE,
223         EV_ADD | EV_ENABLE | EV_ONESHOT,
224         NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND
225         | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE,
226         0,
227         NULL);
228   cnt++;
229
230   if (!(S_ISLNK(sb.st_mode))) break;
231
232   t = store_get(1024, GET_UNTAINTED);
233   Ustrncpy(t, s, 1022);
234   j = Ustrlen(s);
235   t[j++] = '/';
236   if ((i = readlink(CCS filename, (void *)(t+j), 1023-j)) < 0) { s = US"readlink"; goto bad; }
237   filename = t;
238   *(t += i+j) = '\0';
239   store_release_above(t+1);
240   }
241
242 #ifdef OpenBSD
243 if (kevent(tls_watch_fd, &kev[kev_used-cnt], cnt, &k_dummy, 1, &ts) >= 0)
244   return TRUE;
245 #else
246 if (kevent(tls_watch_fd, &kev[kev_used-cnt], cnt, NULL, 0, NULL) >= 0)
247   return TRUE;
248 #endif
249 s = US"kevent";
250
251 bad:
252 DEBUG(D_tls)
253   if (errno)
254     debug_printf("%s: %s: %s\n", __FUNCTION__, s, strerror(errno));
255   else
256     debug_printf("%s: %s\n", __FUNCTION__, s);
257 return FALSE;
258 }
259 # endif /*EXIM_HAVE_KEVENT*/
260
261
262 /* Create an inotify facility if needed.
263 Then set watches on the dir containing the given file or (optionally)
264 list of files.  Return boolean success. */
265
266 static BOOL
267 tls_set_watch(const uschar * filename, BOOL list)
268 {
269 rmark r;
270 BOOL rc = FALSE;
271
272 if (!filename || !*filename) return TRUE;
273 if (Ustrncmp(filename, "system", 6) == 0) return TRUE;
274
275 DEBUG(D_tls) debug_printf("tls_set_watch: '%s'\n", filename);
276
277 if (  tls_watch_fd < 0
278 # ifdef EXIM_HAVE_INOTIFY
279    && (tls_watch_fd = inotify_init1(O_CLOEXEC)) < 0
280 # endif
281 # ifdef EXIM_HAVE_KEVENT
282    && (tls_watch_fd = kqueue()) < 0
283 # endif
284    )
285     {
286     DEBUG(D_tls) debug_printf("inotify_init: %s\n", strerror(errno));
287     return FALSE;
288     }
289
290 r = store_mark();
291
292 if (list)
293   {
294   int sep = 0;
295   for (uschar * s; s = string_nextinlist(&filename, &sep, NULL, 0); )
296     if (!(rc = tls_set_one_watch(s))) break;
297   }
298 else
299   rc = tls_set_one_watch(filename);
300
301 store_reset(r);
302 if (!rc) DEBUG(D_tls) debug_printf("tls_set_watch() fail on '%s': %s\n", filename, strerror(errno));
303 return rc;
304 }
305
306
307 void
308 tls_watch_discard_event(int fd)
309 {
310 #ifdef EXIM_HAVE_INOTIFY
311 (void) read(fd, big_buffer, big_buffer_size);
312 #endif
313 #ifdef EXIM_HAVE_KEVENT
314 struct kevent kev;
315 struct timespec t = {0};
316 (void) kevent(fd, NULL, 0, &kev, 1, &t);
317 #endif
318 }
319 #endif  /*EXIM_HAVE_INOTIFY*/
320
321
322 void
323 tls_client_creds_reload(BOOL watch)
324 {
325 for(transport_instance * t = transports; t; t = t->next)
326   if (Ustrcmp(t->driver_name, "smtp") == 0)
327     {
328     tls_client_creds_invalidate(t);
329     tls_client_creds_init(t, watch);
330     }
331 }
332
333
334 void
335 tls_watch_invalidate(void)
336 {
337 if (tls_watch_fd < 0) return;
338
339 #ifdef EXIM_HAVE_KEVENT
340 /* Close the files we had open for kevent */
341 for (int i = 0; i < kev_used; i++)
342   {
343   DEBUG(D_tls) debug_printf("closing watch fd: %d\n", (int) kev[i].ident);
344   (void) close((int) kev[i].ident);
345   kev[i].ident = (uintptr_t)-1;
346   }
347 kev_used = 0;
348 #endif
349
350 close(tls_watch_fd);
351 tls_watch_fd = -1;
352 }
353
354
355 static void
356 tls_daemon_creds_reload(void)
357 {
358 unsigned lifetime;
359
360 #ifdef EXIM_HAVE_KEVENT
361 tls_watch_invalidate();
362 #endif
363
364 tls_server_creds_invalidate();
365
366 /* _expire is for a time-limited selfsign server cert */
367 tls_creds_expire = (lifetime = tls_server_creds_init())
368   ? time(NULL) + lifetime : 0;
369
370 tls_client_creds_reload(TRUE);
371 }
372
373
374 /* Utility predicates for use by the per-library code */
375 static BOOL
376 opt_set_and_noexpand(const uschar * opt)
377 { return opt && *opt && Ustrchr(opt, '$') == NULL; }
378
379 static BOOL
380 opt_unset_or_noexpand(const uschar * opt)
381 { return !opt || Ustrchr(opt, '$') == NULL; }
382
383
384
385 /* Called every time round the daemon loop.
386
387 If we reloaded fd-watcher, return the old watch fd
388 having modified the global for the new one. Otherwise
389 return -1.
390 */
391
392 int
393 tls_daemon_tick(void)
394 {
395 int old_watch_fd = tls_watch_fd;
396
397 tls_per_lib_daemon_tick();
398 #if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
399 if (tls_creds_expire && time(NULL) >= tls_creds_expire)
400   {
401   /* The server cert is a selfsign, with limited lifetime.  Dump it and
402   generate a new one.  Reload the rest of the creds also as the machinery
403   is all there. */
404
405   DEBUG(D_tls) debug_printf("selfsign cert rotate\n");
406   tls_creds_expire = 0;
407   tls_daemon_creds_reload();
408   return old_watch_fd;
409   }
410 else if (tls_watch_trigger_time && time(NULL) >= tls_watch_trigger_time + 5)
411   {
412   /* Called, after a delay for multiple file ops to get done, from
413   the daemon when any of the watches added (above) fire.
414   Dump the set of watches and arrange to reload cached creds (which
415   will set up new watches). */
416
417   DEBUG(D_tls) debug_printf("watch triggered\n");
418   tls_watch_trigger_time = tls_creds_expire = 0;
419   tls_daemon_creds_reload();
420   return old_watch_fd;
421   }
422 #endif
423 return -1;
424 }
425
426 /* Called once at daemon startup */
427
428 void
429 tls_daemon_init(void)
430 {
431 tls_per_lib_daemon_init();
432 }
433
434
435 /*************************************************
436 *        Timezone environment flipping           *
437 *************************************************/
438
439 static uschar *
440 to_tz(uschar * tz)
441 {
442 uschar * old = US getenv("TZ");
443 (void) setenv("TZ", CCS tz, 1);
444 tzset();
445 return old;
446 }
447
448 static void
449 restore_tz(uschar * tz)
450 {
451 if (tz)
452   (void) setenv("TZ", CCS tz, 1);
453 else
454   (void) os_unsetenv(US"TZ");
455 tzset();
456 }
457
458 /*************************************************
459 *        Many functions are package-specific     *
460 *************************************************/
461
462 #ifdef USE_GNUTLS
463 # include "tls-gnu.c"
464 # include "tlscert-gnu.c"
465 # define ssl_xfer_buffer (state_server.xfer_buffer)
466 # define ssl_xfer_buffer_lwm (state_server.xfer_buffer_lwm)
467 # define ssl_xfer_buffer_hwm (state_server.xfer_buffer_hwm)
468 # define ssl_xfer_eof (state_server.xfer_eof)
469 # define ssl_xfer_error (state_server.xfer_error)
470 #endif
471
472 #ifdef USE_OPENSSL
473 # include "tls-openssl.c"
474 # include "tlscert-openssl.c"
475 #endif
476
477
478
479 /*************************************************
480 *           TLS version of ungetc                *
481 *************************************************/
482
483 /* Puts a character back in the input buffer. Only ever
484 called once.
485 Only used by the server-side TLS.
486
487 Arguments:
488   ch           the character
489
490 Returns:       the character
491 */
492
493 int
494 tls_ungetc(int ch)
495 {
496 if (ssl_xfer_buffer_lwm <= 0)
497   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "buffer underflow in tls_ungetc");
498
499 ssl_xfer_buffer[--ssl_xfer_buffer_lwm] = ch;
500 return ch;
501 }
502
503
504
505 /*************************************************
506 *           TLS version of feof                  *
507 *************************************************/
508
509 /* Tests for a previous EOF
510 Only used by the server-side TLS.
511
512 Arguments:     none
513 Returns:       non-zero if the eof flag is set
514 */
515
516 int
517 tls_feof(void)
518 {
519 return (int)ssl_xfer_eof;
520 }
521
522
523
524 /*************************************************
525 *              TLS version of ferror             *
526 *************************************************/
527
528 /* Tests for a previous read error, and returns with errno
529 restored to what it was when the error was detected.
530 Only used by the server-side TLS.
531
532 >>>>> Hmm. Errno not handled yet. Where do we get it from?  >>>>>
533
534 Arguments:     none
535 Returns:       non-zero if the error flag is set
536 */
537
538 int
539 tls_ferror(void)
540 {
541 return (int)ssl_xfer_error;
542 }
543
544
545 /*************************************************
546 *           TLS version of smtp_buffered         *
547 *************************************************/
548
549 /* Tests for unused chars in the TLS input buffer.
550 Only used by the server-side TLS.
551
552 Arguments:     none
553 Returns:       TRUE/FALSE
554 */
555
556 BOOL
557 tls_smtp_buffered(void)
558 {
559 return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm;
560 }
561
562
563 #endif  /*DISABLE_TLS*/
564
565 void
566 tls_modify_variables(tls_support * dest_tsp)
567 {
568 modify_variable(US"tls_bits",                 &dest_tsp->bits);
569 modify_variable(US"tls_certificate_verified", &dest_tsp->certificate_verified);
570 modify_variable(US"tls_cipher",               &dest_tsp->cipher);
571 modify_variable(US"tls_peerdn",               &dest_tsp->peerdn);
572 #ifdef USE_OPENSSL
573 modify_variable(US"tls_sni",                  &dest_tsp->sni);
574 #endif
575 }
576
577
578 #ifndef DISABLE_TLS
579 /************************************************
580 *       TLS certificate name operations         *
581 ************************************************/
582
583 /* Convert an rfc4514 DN to an exim comma-sep list.
584 Backslashed commas need to be replaced by doublecomma
585 for Exim's list quoting.  We modify the given string
586 inplace.
587 */
588
589 static void
590 dn_to_list(uschar * dn)
591 {
592 for (uschar * cp = dn; *cp; cp++)
593   if (cp[0] == '\\' && cp[1] == ',')
594     *cp++ = ',';
595 }
596
597
598 /* Extract fields of a given type from an RFC4514-
599 format Distinguished Name.  Return an Exim list.
600 NOTE: We modify the supplied dn string during operation.
601
602 Arguments:
603         dn      Distinguished Name string
604         mod     list containing optional output list-sep and
605                 field selector match, comma-separated
606 Return:
607         allocated string with list of matching fields,
608         field type stripped
609 */
610
611 uschar *
612 tls_field_from_dn(uschar * dn, const uschar * mod)
613 {
614 int insep = ',';
615 uschar outsep = '\n';
616 uschar * ele;
617 uschar * match = NULL;
618 int len;
619 gstring * list = NULL;
620
621 while ((ele = string_nextinlist(&mod, &insep, NULL, 0)))
622   if (ele[0] != '>')
623     match = ele;        /* field tag to match */
624   else if (ele[1])
625     outsep = ele[1];    /* nondefault output separator */
626
627 dn_to_list(dn);
628 insep = ',';
629 len = match ? Ustrlen(match) : -1;
630 while ((ele = string_nextinlist(CUSS &dn, &insep, NULL, 0)))
631   if (  !match
632      || Ustrncmp(ele, match, len) == 0 && ele[len] == '='
633      )
634     list = string_append_listele(list, outsep, ele+len+1);
635 return string_from_gstring(list);
636 }
637
638
639 /* Compare a domain name with a possibly-wildcarded name. Wildcards
640 are restricted to a single one, as the first element of patterns
641 having at least three dot-separated elements.  Case-independent.
642 Return TRUE for a match
643 */
644 static BOOL
645 is_name_match(const uschar * name, const uschar * pat)
646 {
647 uschar * cp;
648 return *pat == '*'              /* possible wildcard match */
649   ?    *++pat == '.'            /* starts star, dot              */
650     && !Ustrchr(++pat, '*')     /* has no more stars             */
651     && Ustrchr(pat, '.')        /* and has another dot.          */
652     && (cp = Ustrchr(name, '.'))/* The name has at least one dot */
653     && strcmpic(++cp, pat) == 0 /* and we only compare after it. */
654   :    !Ustrchr(pat+1, '*')
655     && strcmpic(name, pat) == 0;
656 }
657
658 /* Compare a list of names with the dnsname elements
659 of the Subject Alternate Name, if any, and the
660 Subject otherwise.
661
662 Arguments:
663         namelist names to compare
664         cert     certificate
665
666 Returns:
667         TRUE/FALSE
668 */
669
670 BOOL
671 tls_is_name_for_cert(const uschar * namelist, void * cert)
672 {
673 uschar * altnames = tls_cert_subject_altname(cert, US"dns");
674 uschar * subjdn;
675 uschar * certname;
676 int cmp_sep = 0;
677 uschar * cmpname;
678
679 if ((altnames = tls_cert_subject_altname(cert, US"dns")))
680   {
681   int alt_sep = '\n';
682   while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0)))
683     {
684     const uschar * an = altnames;
685     while ((certname = string_nextinlist(&an, &alt_sep, NULL, 0)))
686       if (is_name_match(cmpname, certname))
687         return TRUE;
688     }
689   }
690
691 else if ((subjdn = tls_cert_subject(cert, NULL)))
692   {
693   int sn_sep = ',';
694
695   dn_to_list(subjdn);
696   while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0)))
697     {
698     const uschar * sn = subjdn;
699     while ((certname = string_nextinlist(&sn, &sn_sep, NULL, 0)))
700       if (  *certname++ == 'C'
701          && *certname++ == 'N'
702          && *certname++ == '='
703          && is_name_match(cmpname, certname)
704          )
705         return TRUE;
706     }
707   }
708 return FALSE;
709 }
710
711 /* Environment cleanup: The GnuTLS library uses SSLKEYLOGFILE in the environment
712 and writes a file by that name.  Our OpenSSL code does the same, using keying
713 info from the library API.
714 The GnuTLS support only works if exim is run by root, not taking advantage of
715 the setuid bit.
716 You can use either the external environment (modulo the keep_environment config)
717 or the add_environment config option for SSLKEYLOGFILE; the latter takes
718 precedence.
719
720 If the path is absolute, require it starts with the spooldir; otherwise delete
721 the env variable.  If relative, prefix the spooldir.
722 */
723 void
724 tls_clean_env(void)
725 {
726 uschar * path = US getenv("SSLKEYLOGFILE");
727 if (path)
728   if (!*path)
729     unsetenv("SSLKEYLOGFILE");
730   else if (*path != '/')
731     {
732     DEBUG(D_tls)
733       debug_printf("prepending spooldir to  env SSLKEYLOGFILE\n");
734     setenv("SSLKEYLOGFILE", CCS string_sprintf("%s/%s", spool_directory, path), 1);
735     }
736   else if (Ustrncmp(path, spool_directory, Ustrlen(spool_directory)) != 0)
737     {
738     DEBUG(D_tls)
739       debug_printf("removing env SSLKEYLOGFILE=%s: not under spooldir\n", path);
740     unsetenv("SSLKEYLOGFILE");
741     }
742 }
743
744 /*************************************************
745 *       Drop privs for checking TLS config      *
746 *************************************************/
747
748 /* We want to validate TLS options during readconf, but do not want to be
749 root when we call into the TLS library, in case of library linkage errors
750 which cause segfaults; before this check, those were always done as the Exim
751 runtime user and it makes sense to continue with that.
752
753 Assumes:  tls_require_ciphers has been set, if it will be
754           exim_user has been set, if it will be
755           exim_group has been set, if it will be
756
757 Returns:  bool for "okay"; false will cause caller to immediately exit.
758 */
759
760 BOOL
761 tls_dropprivs_validate_require_cipher(BOOL nowarn)
762 {
763 const uschar *errmsg;
764 pid_t pid;
765 int rc, status;
766 void (*oldsignal)(int);
767
768 /* If TLS will never be used, no point checking ciphers */
769
770 if (  !tls_advertise_hosts
771    || !*tls_advertise_hosts
772    || Ustrcmp(tls_advertise_hosts, ":") == 0
773    )
774   return TRUE;
775 else if (!nowarn && !tls_certificate)
776   log_write(0, LOG_MAIN,
777     "Warning: No server certificate defined; will use a selfsigned one.\n"
778     " Suggested action: either install a certificate or change tls_advertise_hosts option");
779
780 oldsignal = signal(SIGCHLD, SIG_DFL);
781
782 fflush(NULL);
783 if ((pid = exim_fork(US"cipher-validate")) < 0)
784   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork failed for TLS check");
785
786 if (pid == 0)
787   {
788   /* in some modes, will have dropped privilege already */
789   if (!geteuid())
790     exim_setugid(exim_uid, exim_gid, FALSE,
791         US"calling tls_validate_require_cipher");
792
793   if ((errmsg = tls_validate_require_cipher()))
794     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
795         "tls_require_ciphers invalid: %s", errmsg);
796   fflush(NULL);
797   exim_underbar_exit(EXIT_SUCCESS);
798   }
799
800 do {
801   rc = waitpid(pid, &status, 0);
802 } while (rc < 0 && errno == EINTR);
803
804 DEBUG(D_tls)
805   debug_printf("tls_validate_require_cipher child %d ended: status=0x%x\n",
806       (int)pid, status);
807
808 signal(SIGCHLD, oldsignal);
809
810 return status == 0;
811 }
812
813
814
815
816 static void
817 tls_client_resmption_key(tls_support * tlsp, smtp_connect_args * conn_args,
818   smtp_transport_options_block * ob)
819 {
820 #ifndef DISABLE_TLS_RESUME
821 hctx * h = &tlsp->resume_hctx;
822 blob b;
823 gstring * g;
824
825 DEBUG(D_tls) if (conn_args->host_lbserver)
826   debug_printf("TLS: lbserver '%s'\n", conn_args->host_lbserver);
827
828 # ifdef EXIM_HAVE_SHA2
829 exim_sha_init(h, HASH_SHA2_256);
830 # else
831 exim_sha_init(h, HASH_SHA1);
832 # endif
833 exim_sha_update_string(h, conn_args->host_lbserver);
834 # ifdef SUPPORT_DANE
835 if (conn_args->dane)
836   exim_sha_update(h,  CUS &conn_args->tlsa_dnsa, sizeof(dns_answer));
837 # endif
838 exim_sha_update_string(h, conn_args->host->address);
839 exim_sha_update(h,   CUS &conn_args->host->port, sizeof(conn_args->host->port));
840 exim_sha_update_string(h, conn_args->sending_ip_address);
841 exim_sha_update_string(h, openssl_options);
842 exim_sha_update_string(h, ob->tls_require_ciphers);
843 exim_sha_update_string(h, tlsp->sni);
844 # ifdef EXIM_HAVE_ALPN
845 exim_sha_update_string(h, ob->tls_alpn);
846 # endif
847 exim_sha_finish(h, &b);
848 for (g = string_get(b.len*2+1); b.len-- > 0; )
849   g = string_fmt_append(g, "%02x", *b.data++);
850 tlsp->resume_index = string_from_gstring(g);
851 DEBUG(D_tls) debug_printf("TLS: resume session index %s\n", tlsp->resume_index);
852 #endif
853 }
854
855 #endif  /*!DISABLE_TLS*/
856 #endif  /*!MACRO_PREDEF*/
857
858 /* vi: aw ai sw=2
859 */
860 /* End of tls.c */