ffcc8598c173b5b2ee70966322bfd39fa4f6f7fd
[users/heiko/exim.git] / src / src / tls.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2018 */
6 /* Copyright (c) The Exim Maintainers 2020 */
7 /* See the file NOTICE for conditions of use and distribution. */
8
9 /* This module provides TLS (aka SSL) support for Exim. The code for OpenSSL is
10 based on a patch that was originally contributed by Steve Haslam. It was
11 adapted from stunnel, a GPL program by Michal Trojnara. The code for GNU TLS is
12 based on a patch contributed by Nikos Mavrogiannopoulos. Because these packages
13 are so very different, the functions for each are kept in separate files. The
14 relevant file is #included as required, after any any common functions.
15
16 No cryptographic code is included in Exim. All this module does is to call
17 functions from the OpenSSL or GNU TLS libraries. */
18
19
20 #include "exim.h"
21 #include "transports/smtp.h"
22
23 #if !defined(DISABLE_TLS) && !defined(USE_OPENSSL) && !defined(USE_GNUTLS)
24 # error One of USE_OPENSSL or USE_GNUTLS must be defined for a TLS build
25 #endif
26
27
28 #if defined(MACRO_PREDEF) && !defined(DISABLE_TLS)
29 # include "macro_predef.h"
30 # ifdef USE_GNUTLS
31 #  include "tls-gnu.c"
32 # else
33 #  include "tls-openssl.c"
34 # endif
35 #endif
36
37 #ifndef MACRO_PREDEF
38
39 static void tls_per_lib_daemon_init(void);
40 static void tls_per_lib_daemon_tick(void);
41 static void tls_server_creds_init(void);
42 static void tls_server_creds_invalidate(void);
43 static void tls_client_creds_init(transport_instance *, BOOL);
44 static void tls_client_creds_invalidate(transport_instance *);
45
46
47
48 /* This module is compiled only when it is specifically requested in the
49 build-time configuration. However, some compilers don't like compiling empty
50 modules, so keep them happy with a dummy when skipping the rest. Make it
51 reference itself to stop picky compilers complaining that it is unused, and put
52 in a dummy argument to stop even pickier compilers complaining about infinite
53 loops. */
54
55 #ifdef DISABLE_TLS
56 static void dummy(int x) { dummy(x-1); }
57 #else   /* most of the rest of the file */
58
59 const exim_tlslib_state null_tls_preload = {0};
60
61 /* Static variables that are used for buffering data by both sets of
62 functions and the common functions below.
63
64 We're moving away from this; GnuTLS is already using a state, which
65 can switch, so we can do TLS callouts during ACLs. */
66
67 static const int ssl_xfer_buffer_size = 4096;
68 #ifdef USE_OPENSSL
69 static uschar *ssl_xfer_buffer = NULL;
70 static int ssl_xfer_buffer_lwm = 0;
71 static int ssl_xfer_buffer_hwm = 0;
72 static int ssl_xfer_eof = FALSE;
73 static BOOL ssl_xfer_error = FALSE;
74 #endif
75
76
77 /*************************************************
78 *       Expand string; give error on failure     *
79 *************************************************/
80
81 /* If expansion is forced to fail, set the result NULL and return TRUE.
82 Other failures return FALSE. For a server, an SMTP response is given.
83
84 Arguments:
85   s         the string to expand; if NULL just return TRUE
86   name      name of string being expanded (for error)
87   result    where to put the result
88
89 Returns:    TRUE if OK; result may still be NULL after forced failure
90 */
91
92 static BOOL
93 expand_check(const uschar *s, const uschar *name, uschar **result, uschar ** errstr)
94 {
95 if (!s)
96   *result = NULL;
97 else if (  !(*result = expand_string(US s)) /* need to clean up const more */
98         && !f.expand_string_forcedfail
99         )
100   {
101   *errstr = US"Internal error";
102   log_write(0, LOG_MAIN|LOG_PANIC, "expansion of %s failed: %s", name,
103     expand_string_message);
104   return FALSE;
105   }
106 return TRUE;
107 }
108
109
110 #ifdef EXIM_HAVE_INOTIFY
111 /* Add the directory for a filename to the inotify handle, creating that if
112 needed.  This is enough to see changes to files in that dir.
113 Return boolean success.
114
115 The word "system" fails, which is on the safe side as we don't know what
116 directory it implies nor if the TLS library handles a watch for us.
117
118 The string "system,cache" is recognised and explicitly accepted without
119 setting a watch.  This permits the system CA bundle to be cached even though
120 we have no way to tell when it gets modified by an update.
121
122 We *might* try to run "openssl version -d" and set watches on the dir
123 indicated in its output, plus the "certs" subdir of it (following
124 synlimks for both).  But this is undocumented even for OpenSSL, and
125 who knows what GnuTLS might be doing.
126
127 A full set of caching including the CAs takes 35ms output off of the
128 server tls_init() (GnuTLS, Fedora 32, 2018-class x86_64 laptop hardware).
129 */
130 static BOOL
131 tls_set_one_watch(const uschar * filename)
132 {
133 uschar * s;
134
135 if (Ustrcmp(filename, "system,cache") == 0) return TRUE;
136
137 if (!(s = Ustrrchr(filename, '/'))) return FALSE;
138 s = string_copyn(filename, s - filename);
139 DEBUG(D_tls) debug_printf("watch dir '%s'\n", s);
140
141 if (inotify_add_watch(tls_watch_fd, CCS s,
142       IN_ONESHOT | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF
143       | IN_MOVED_FROM | IN_MOVED_TO | IN_MOVE_SELF) >= 0)
144   return TRUE;
145 DEBUG(D_tls) debug_printf("add_watch: %s\n", strerror(errno));
146 return FALSE;
147 }
148
149
150 /* Create an inotify facility if needed.
151 Then set watches on the dir containing the given file or (optionally)
152 list of files.  Return boolean success. */
153
154 static BOOL
155 tls_set_watch(const uschar * filename, BOOL list)
156 {
157 rmark r;
158 BOOL rc = FALSE;
159
160 if (tls_watch_fd < 0 && (tls_watch_fd = inotify_init1(O_CLOEXEC)) < 0)
161   {
162   DEBUG(D_tls) debug_printf("inotify_init: %s\n", strerror(errno));
163   return FALSE;
164   }
165
166 if (!filename || !*filename) return TRUE;
167
168 r = store_mark();
169
170 if (list)
171   {
172   int sep = 0;
173   for (uschar * s; s = string_nextinlist(&filename, &sep, NULL, 0); )
174     if (!(rc = tls_set_one_watch(s))) break;
175   }
176 else
177   rc = tls_set_one_watch(filename);
178
179 store_reset(r);
180 return rc;
181 }
182
183
184 void
185 tls_client_creds_reload(BOOL watch)
186 {
187 for(transport_instance * t = transports; t; t = t->next)
188   if (Ustrcmp(t->driver_name, "smtp") == 0)
189     {
190     tls_client_creds_invalidate(t);
191     tls_client_creds_init(t, watch);
192     }
193 }
194
195 static void
196 tls_daemon_creds_reload(void)
197 {
198 tls_server_creds_invalidate();
199 tls_server_creds_init();
200
201 tls_client_creds_reload(TRUE);
202 }
203
204
205 /* Called, after a delay for multiple file ops to get done, from
206 the daemon when any of the watches added (above) fire.
207
208 Dump the set of watches and arrange to reload cached creds (which
209 will set up new watches). */
210
211 static void
212 tls_watch_triggered(void)
213 {
214 DEBUG(D_tls) debug_printf("watch triggered\n");
215 close(tls_watch_fd);
216 tls_watch_fd = -1;
217
218 tls_daemon_creds_reload();
219 }
220
221
222 /* Utility predicates for use by the per-library code */
223 static BOOL
224 opt_set_and_noexpand(const uschar * opt)
225 { return opt && *opt && Ustrchr(opt, '$') == NULL; }
226
227 static BOOL
228 opt_unset_or_noexpand(const uschar * opt)
229 { return !opt || Ustrchr(opt, '$') == NULL; }
230
231 #endif  /* EXIM_HAVE_INOTIFY */
232
233
234 /* Called every time round the daemon loop */
235
236 void
237 tls_daemon_tick(void)
238 {
239 tls_per_lib_daemon_tick();
240 #ifdef EXIM_HAVE_INOTIFY
241 if (tls_watch_trigger_time && time(NULL) >= tls_watch_trigger_time + 5)
242   {
243   tls_watch_trigger_time = 0;
244   tls_watch_triggered();
245   }
246 #endif
247 }
248
249 /* Called once at daemon startup */
250
251 void
252 tls_daemon_init(void)
253 {
254 tls_per_lib_daemon_init();
255 }
256
257
258 /*************************************************
259 *        Timezone environment flipping           *
260 *************************************************/
261
262 static uschar *
263 to_tz(uschar * tz)
264 {
265 uschar * old = US getenv("TZ");
266 (void) setenv("TZ", CCS tz, 1);
267 tzset();
268 return old;
269 }
270
271 static void
272 restore_tz(uschar * tz)
273 {
274 if (tz)
275   (void) setenv("TZ", CCS tz, 1);
276 else
277   (void) os_unsetenv(US"TZ");
278 tzset();
279 }
280
281 /*************************************************
282 *        Many functions are package-specific     *
283 *************************************************/
284
285 #ifdef USE_GNUTLS
286 # include "tls-gnu.c"
287 # include "tlscert-gnu.c"
288 # define ssl_xfer_buffer (state_server.xfer_buffer)
289 # define ssl_xfer_buffer_lwm (state_server.xfer_buffer_lwm)
290 # define ssl_xfer_buffer_hwm (state_server.xfer_buffer_hwm)
291 # define ssl_xfer_eof (state_server.xfer_eof)
292 # define ssl_xfer_error (state_server.xfer_error)
293 #endif
294
295 #ifdef USE_OPENSSL
296 # include "tls-openssl.c"
297 # include "tlscert-openssl.c"
298 #endif
299
300
301
302 /*************************************************
303 *           TLS version of ungetc                *
304 *************************************************/
305
306 /* Puts a character back in the input buffer. Only ever
307 called once.
308 Only used by the server-side TLS.
309
310 Arguments:
311   ch           the character
312
313 Returns:       the character
314 */
315
316 int
317 tls_ungetc(int ch)
318 {
319 ssl_xfer_buffer[--ssl_xfer_buffer_lwm] = ch;
320 return ch;
321 }
322
323
324
325 /*************************************************
326 *           TLS version of feof                  *
327 *************************************************/
328
329 /* Tests for a previous EOF
330 Only used by the server-side TLS.
331
332 Arguments:     none
333 Returns:       non-zero if the eof flag is set
334 */
335
336 int
337 tls_feof(void)
338 {
339 return (int)ssl_xfer_eof;
340 }
341
342
343
344 /*************************************************
345 *              TLS version of ferror             *
346 *************************************************/
347
348 /* Tests for a previous read error, and returns with errno
349 restored to what it was when the error was detected.
350 Only used by the server-side TLS.
351
352 >>>>> Hmm. Errno not handled yet. Where do we get it from?  >>>>>
353
354 Arguments:     none
355 Returns:       non-zero if the error flag is set
356 */
357
358 int
359 tls_ferror(void)
360 {
361 return (int)ssl_xfer_error;
362 }
363
364
365 /*************************************************
366 *           TLS version of smtp_buffered         *
367 *************************************************/
368
369 /* Tests for unused chars in the TLS input buffer.
370 Only used by the server-side TLS.
371
372 Arguments:     none
373 Returns:       TRUE/FALSE
374 */
375
376 BOOL
377 tls_smtp_buffered(void)
378 {
379 return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm;
380 }
381
382
383 #endif  /*DISABLE_TLS*/
384
385 void
386 tls_modify_variables(tls_support * dest_tsp)
387 {
388 modify_variable(US"tls_bits",                 &dest_tsp->bits);
389 modify_variable(US"tls_certificate_verified", &dest_tsp->certificate_verified);
390 modify_variable(US"tls_cipher",               &dest_tsp->cipher);
391 modify_variable(US"tls_peerdn",               &dest_tsp->peerdn);
392 #ifdef USE_OPENSSL
393 modify_variable(US"tls_sni",                  &dest_tsp->sni);
394 #endif
395 }
396
397
398 #ifndef DISABLE_TLS
399 /************************************************
400 *       TLS certificate name operations         *
401 ************************************************/
402
403 /* Convert an rfc4514 DN to an exim comma-sep list.
404 Backslashed commas need to be replaced by doublecomma
405 for Exim's list quoting.  We modify the given string
406 inplace.
407 */
408
409 static void
410 dn_to_list(uschar * dn)
411 {
412 for (uschar * cp = dn; *cp; cp++)
413   if (cp[0] == '\\' && cp[1] == ',')
414     *cp++ = ',';
415 }
416
417
418 /* Extract fields of a given type from an RFC4514-
419 format Distinguished Name.  Return an Exim list.
420 NOTE: We modify the supplied dn string during operation.
421
422 Arguments:
423         dn      Distinguished Name string
424         mod     list containing optional output list-sep and
425                 field selector match, comma-separated
426 Return:
427         allocated string with list of matching fields,
428         field type stripped
429 */
430
431 uschar *
432 tls_field_from_dn(uschar * dn, const uschar * mod)
433 {
434 int insep = ',';
435 uschar outsep = '\n';
436 uschar * ele;
437 uschar * match = NULL;
438 int len;
439 gstring * list = NULL;
440
441 while ((ele = string_nextinlist(&mod, &insep, NULL, 0)))
442   if (ele[0] != '>')
443     match = ele;        /* field tag to match */
444   else if (ele[1])
445     outsep = ele[1];    /* nondefault output separator */
446
447 dn_to_list(dn);
448 insep = ',';
449 len = match ? Ustrlen(match) : -1;
450 while ((ele = string_nextinlist(CUSS &dn, &insep, NULL, 0)))
451   if (  !match
452      || Ustrncmp(ele, match, len) == 0 && ele[len] == '='
453      )
454     list = string_append_listele(list, outsep, ele+len+1);
455 return string_from_gstring(list);
456 }
457
458
459 /* Compare a domain name with a possibly-wildcarded name. Wildcards
460 are restricted to a single one, as the first element of patterns
461 having at least three dot-separated elements.  Case-independent.
462 Return TRUE for a match
463 */
464 static BOOL
465 is_name_match(const uschar * name, const uschar * pat)
466 {
467 uschar * cp;
468 return *pat == '*'              /* possible wildcard match */
469   ?    *++pat == '.'            /* starts star, dot              */
470     && !Ustrchr(++pat, '*')     /* has no more stars             */
471     && Ustrchr(pat, '.')        /* and has another dot.          */
472     && (cp = Ustrchr(name, '.'))/* The name has at least one dot */
473     && strcmpic(++cp, pat) == 0 /* and we only compare after it. */
474   :    !Ustrchr(pat+1, '*')
475     && strcmpic(name, pat) == 0;
476 }
477
478 /* Compare a list of names with the dnsname elements
479 of the Subject Alternate Name, if any, and the
480 Subject otherwise.
481
482 Arguments:
483         namelist names to compare
484         cert     certificate
485
486 Returns:
487         TRUE/FALSE
488 */
489
490 BOOL
491 tls_is_name_for_cert(const uschar * namelist, void * cert)
492 {
493 uschar * altnames = tls_cert_subject_altname(cert, US"dns");
494 uschar * subjdn;
495 uschar * certname;
496 int cmp_sep = 0;
497 uschar * cmpname;
498
499 if ((altnames = tls_cert_subject_altname(cert, US"dns")))
500   {
501   int alt_sep = '\n';
502   while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0)))
503     {
504     const uschar * an = altnames;
505     while ((certname = string_nextinlist(&an, &alt_sep, NULL, 0)))
506       if (is_name_match(cmpname, certname))
507         return TRUE;
508     }
509   }
510
511 else if ((subjdn = tls_cert_subject(cert, NULL)))
512   {
513   int sn_sep = ',';
514
515   dn_to_list(subjdn);
516   while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0)))
517     {
518     const uschar * sn = subjdn;
519     while ((certname = string_nextinlist(&sn, &sn_sep, NULL, 0)))
520       if (  *certname++ == 'C'
521          && *certname++ == 'N'
522          && *certname++ == '='
523          && is_name_match(cmpname, certname)
524          )
525         return TRUE;
526     }
527   }
528 return FALSE;
529 }
530
531
532 /* Environment cleanup: The GnuTLS library uses SSLKEYLOGFILE in the environment
533 and writes a file by that name.  Our OpenSSL code does the same, using keying
534 info from the library API.
535 The GnuTLS support only works if exim is run by root, not taking advantage of
536 the setuid bit.
537 You can use either the external environment (modulo the keep_environment config)
538 or the add_environment config option for SSLKEYLOGFILE; the latter takes
539 precedence.
540
541 If the path is absolute, require it starts with the spooldir; otherwise delete
542 the env variable.  If relative, prefix the spooldir.
543 */
544 void
545 tls_clean_env(void)
546 {
547 uschar * path = US getenv("SSLKEYLOGFILE");
548 if (path)
549   if (!*path)
550     unsetenv("SSLKEYLOGFILE");
551   else if (*path != '/')
552     {
553     DEBUG(D_tls)
554       debug_printf("prepending spooldir to  env SSLKEYLOGFILE\n");
555     setenv("SSLKEYLOGFILE", CCS string_sprintf("%s/%s", spool_directory, path), 1);
556     }
557   else if (Ustrncmp(path, spool_directory, Ustrlen(spool_directory)) != 0)
558     {
559     DEBUG(D_tls)
560       debug_printf("removing env SSLKEYLOGFILE=%s: not under spooldir\n", path);
561     unsetenv("SSLKEYLOGFILE");
562     }
563 }
564
565 /*************************************************
566 *       Drop privs for checking TLS config      *
567 *************************************************/
568
569 /* We want to validate TLS options during readconf, but do not want to be
570 root when we call into the TLS library, in case of library linkage errors
571 which cause segfaults; before this check, those were always done as the Exim
572 runtime user and it makes sense to continue with that.
573
574 Assumes:  tls_require_ciphers has been set, if it will be
575           exim_user has been set, if it will be
576           exim_group has been set, if it will be
577
578 Returns:  bool for "okay"; false will cause caller to immediately exit.
579 */
580
581 BOOL
582 tls_dropprivs_validate_require_cipher(BOOL nowarn)
583 {
584 const uschar *errmsg;
585 pid_t pid;
586 int rc, status;
587 void (*oldsignal)(int);
588
589 /* If TLS will never be used, no point checking ciphers */
590
591 if (  !tls_advertise_hosts
592    || !*tls_advertise_hosts
593    || Ustrcmp(tls_advertise_hosts, ":") == 0
594    )
595   return TRUE;
596 else if (!nowarn && !tls_certificate)
597   log_write(0, LOG_MAIN,
598     "Warning: No server certificate defined; will use a selfsigned one.\n"
599     " Suggested action: either install a certificate or change tls_advertise_hosts option");
600
601 oldsignal = signal(SIGCHLD, SIG_DFL);
602
603 fflush(NULL);
604 if ((pid = exim_fork(US"cipher-validate")) < 0)
605   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork failed for TLS check");
606
607 if (pid == 0)
608   {
609   /* in some modes, will have dropped privilege already */
610   if (!geteuid())
611     exim_setugid(exim_uid, exim_gid, FALSE,
612         US"calling tls_validate_require_cipher");
613
614   if ((errmsg = tls_validate_require_cipher()))
615     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
616         "tls_require_ciphers invalid: %s", errmsg);
617   fflush(NULL);
618   exim_underbar_exit(EXIT_SUCCESS);
619   }
620
621 do {
622   rc = waitpid(pid, &status, 0);
623 } while (rc < 0 && errno == EINTR);
624
625 DEBUG(D_tls)
626   debug_printf("tls_validate_require_cipher child %d ended: status=0x%x\n",
627       (int)pid, status);
628
629 signal(SIGCHLD, oldsignal);
630
631 return status == 0;
632 }
633
634
635
636
637 #endif  /*!DISABLE_TLS*/
638 #endif  /*!MACRO_PREDEF*/
639
640 /* vi: aw ai sw=2
641 */
642 /* End of tls.c */