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