1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
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. */
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.
16 No cryptographic code is included in Exim. All this module does is to call
17 functions from the OpenSSL or GNU TLS libraries. */
21 #include "transports/smtp.h"
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
28 #if defined(MACRO_PREDEF) && !defined(DISABLE_TLS)
29 # include "macro_predef.h"
33 # include "tls-openssl.c"
39 /* This module is compiled only when it is specifically requested in the
40 build-time configuration. However, some compilers don't like compiling empty
41 modules, so keep them happy with a dummy when skipping the rest. Make it
42 reference itself to stop picky compilers complaining that it is unused, and put
43 in a dummy argument to stop even pickier compilers complaining about infinite
47 static void dummy(int x) { dummy(x-1); }
50 /* Static variables that are used for buffering data by both sets of
51 functions and the common functions below.
53 We're moving away from this; GnuTLS is already using a state, which
54 can switch, so we can do TLS callouts during ACLs. */
56 static const int ssl_xfer_buffer_size = 4096;
58 static uschar *ssl_xfer_buffer = NULL;
59 static int ssl_xfer_buffer_lwm = 0;
60 static int ssl_xfer_buffer_hwm = 0;
61 static int ssl_xfer_eof = FALSE;
62 static BOOL ssl_xfer_error = FALSE;
66 /*************************************************
67 * Expand string; give error on failure *
68 *************************************************/
70 /* If expansion is forced to fail, set the result NULL and return TRUE.
71 Other failures return FALSE. For a server, an SMTP response is given.
74 s the string to expand; if NULL just return TRUE
75 name name of string being expanded (for error)
76 result where to put the result
78 Returns: TRUE if OK; result may still be NULL after forced failure
82 expand_check(const uschar *s, const uschar *name, uschar **result, uschar ** errstr)
86 else if ( !(*result = expand_string(US s)) /* need to clean up const more */
87 && !f.expand_string_forcedfail
90 *errstr = US"Internal error";
91 log_write(0, LOG_MAIN|LOG_PANIC, "expansion of %s failed: %s", name,
92 expand_string_message);
99 /*************************************************
100 * Timezone environment flipping *
101 *************************************************/
106 uschar * old = US getenv("TZ");
107 (void) setenv("TZ", CCS tz, 1);
113 restore_tz(uschar * tz)
116 (void) setenv("TZ", CCS tz, 1);
118 (void) os_unsetenv(US"TZ");
122 /*************************************************
123 * Many functions are package-specific *
124 *************************************************/
127 # include "tls-gnu.c"
128 # include "tlscert-gnu.c"
129 # define ssl_xfer_buffer (state_server.xfer_buffer)
130 # define ssl_xfer_buffer_lwm (state_server.xfer_buffer_lwm)
131 # define ssl_xfer_buffer_hwm (state_server.xfer_buffer_hwm)
132 # define ssl_xfer_eof (state_server.xfer_eof)
133 # define ssl_xfer_error (state_server.xfer_error)
137 # include "tls-openssl.c"
138 # include "tlscert-openssl.c"
143 /*************************************************
144 * TLS version of ungetc *
145 *************************************************/
147 /* Puts a character back in the input buffer. Only ever
149 Only used by the server-side TLS.
154 Returns: the character
160 if (ssl_xfer_buffer_lwm <= 0)
161 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "buffer underflow in tls_ungetc");
163 ssl_xfer_buffer[--ssl_xfer_buffer_lwm] = ch;
169 /*************************************************
170 * TLS version of feof *
171 *************************************************/
173 /* Tests for a previous EOF
174 Only used by the server-side TLS.
177 Returns: non-zero if the eof flag is set
183 return (int)ssl_xfer_eof;
188 /*************************************************
189 * TLS version of ferror *
190 *************************************************/
192 /* Tests for a previous read error, and returns with errno
193 restored to what it was when the error was detected.
194 Only used by the server-side TLS.
196 >>>>> Hmm. Errno not handled yet. Where do we get it from? >>>>>
199 Returns: non-zero if the error flag is set
205 return (int)ssl_xfer_error;
209 /*************************************************
210 * TLS version of smtp_buffered *
211 *************************************************/
213 /* Tests for unused chars in the TLS input buffer.
214 Only used by the server-side TLS.
221 tls_smtp_buffered(void)
223 return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm;
227 #endif /*DISABLE_TLS*/
230 tls_modify_variables(tls_support * dest_tsp)
232 modify_variable(US"tls_bits", &dest_tsp->bits);
233 modify_variable(US"tls_certificate_verified", &dest_tsp->certificate_verified);
234 modify_variable(US"tls_cipher", &dest_tsp->cipher);
235 modify_variable(US"tls_peerdn", &dest_tsp->peerdn);
237 modify_variable(US"tls_sni", &dest_tsp->sni);
243 /************************************************
244 * TLS certificate name operations *
245 ************************************************/
247 /* Convert an rfc4514 DN to an exim comma-sep list.
248 Backslashed commas need to be replaced by doublecomma
249 for Exim's list quoting. We modify the given string
254 dn_to_list(uschar * dn)
256 for (uschar * cp = dn; *cp; cp++)
257 if (cp[0] == '\\' && cp[1] == ',')
262 /* Extract fields of a given type from an RFC4514-
263 format Distinguished Name. Return an Exim list.
264 NOTE: We modify the supplied dn string during operation.
267 dn Distinguished Name string
268 mod list containing optional output list-sep and
269 field selector match, comma-separated
271 allocated string with list of matching fields,
276 tls_field_from_dn(uschar * dn, const uschar * mod)
279 uschar outsep = '\n';
281 uschar * match = NULL;
283 gstring * list = NULL;
285 while ((ele = string_nextinlist(&mod, &insep, NULL, 0)))
287 match = ele; /* field tag to match */
289 outsep = ele[1]; /* nondefault output separator */
293 len = match ? Ustrlen(match) : -1;
294 while ((ele = string_nextinlist(CUSS &dn, &insep, NULL, 0)))
296 || Ustrncmp(ele, match, len) == 0 && ele[len] == '='
298 list = string_append_listele(list, outsep, ele+len+1);
299 return string_from_gstring(list);
303 /* Compare a domain name with a possibly-wildcarded name. Wildcards
304 are restricted to a single one, as the first element of patterns
305 having at least three dot-separated elements. Case-independent.
306 Return TRUE for a match
309 is_name_match(const uschar * name, const uschar * pat)
312 return *pat == '*' /* possible wildcard match */
313 ? *++pat == '.' /* starts star, dot */
314 && !Ustrchr(++pat, '*') /* has no more stars */
315 && Ustrchr(pat, '.') /* and has another dot. */
316 && (cp = Ustrchr(name, '.'))/* The name has at least one dot */
317 && strcmpic(++cp, pat) == 0 /* and we only compare after it. */
318 : !Ustrchr(pat+1, '*')
319 && strcmpic(name, pat) == 0;
322 /* Compare a list of names with the dnsname elements
323 of the Subject Alternate Name, if any, and the
327 namelist names to compare
335 tls_is_name_for_cert(const uschar * namelist, void * cert)
337 uschar * altnames = tls_cert_subject_altname(cert, US"dns");
343 if ((altnames = tls_cert_subject_altname(cert, US"dns")))
346 while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0)))
348 const uschar * an = altnames;
349 while ((certname = string_nextinlist(&an, &alt_sep, NULL, 0)))
350 if (is_name_match(cmpname, certname))
355 else if ((subjdn = tls_cert_subject(cert, NULL)))
360 while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0)))
362 const uschar * sn = subjdn;
363 while ((certname = string_nextinlist(&sn, &sn_sep, NULL, 0)))
364 if ( *certname++ == 'C'
365 && *certname++ == 'N'
366 && *certname++ == '='
367 && is_name_match(cmpname, certname)
376 /* Environment cleanup: The GnuTLS library uses SSLKEYLOGFILE in the environment
377 and writes a file by that name. Our OpenSSL code does the same, using keying
378 info from the library API.
379 The GnuTLS support only works if exim is run by root, not taking advantage of
381 You can use either the external environment (modulo the keep_environment config)
382 or the add_environment config option for SSLKEYLOGFILE; the latter takes
385 If the path is absolute, require it starts with the spooldir; otherwise delete
386 the env variable. If relative, prefix the spooldir.
391 uschar * path = US getenv("SSLKEYLOGFILE");
394 unsetenv("SSLKEYLOGFILE");
395 else if (*path != '/')
398 debug_printf("prepending spooldir to env SSLKEYLOGFILE\n");
399 setenv("SSLKEYLOGFILE", CCS string_sprintf("%s/%s", spool_directory, path), 1);
401 else if (Ustrncmp(path, spool_directory, Ustrlen(spool_directory)) != 0)
404 debug_printf("removing env SSLKEYLOGFILE=%s: not under spooldir\n", path);
405 unsetenv("SSLKEYLOGFILE");
409 /*************************************************
410 * Drop privs for checking TLS config *
411 *************************************************/
413 /* We want to validate TLS options during readconf, but do not want to be
414 root when we call into the TLS library, in case of library linkage errors
415 which cause segfaults; before this check, those were always done as the Exim
416 runtime user and it makes sense to continue with that.
418 Assumes: tls_require_ciphers has been set, if it will be
419 exim_user has been set, if it will be
420 exim_group has been set, if it will be
422 Returns: bool for "okay"; false will cause caller to immediately exit.
426 tls_dropprivs_validate_require_cipher(BOOL nowarn)
428 const uschar *errmsg;
431 void (*oldsignal)(int);
433 /* If TLS will never be used, no point checking ciphers */
435 if ( !tls_advertise_hosts
436 || !*tls_advertise_hosts
437 || Ustrcmp(tls_advertise_hosts, ":") == 0
440 else if (!nowarn && !tls_certificate)
441 log_write(0, LOG_MAIN,
442 "Warning: No server certificate defined; will use a selfsigned one.\n"
443 " Suggested action: either install a certificate or change tls_advertise_hosts option");
445 oldsignal = signal(SIGCHLD, SIG_DFL);
448 if ((pid = exim_fork(US"cipher-validate")) < 0)
449 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork failed for TLS check");
453 /* in some modes, will have dropped privilege already */
455 exim_setugid(exim_uid, exim_gid, FALSE,
456 US"calling tls_validate_require_cipher");
458 if ((errmsg = tls_validate_require_cipher()))
459 log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
460 "tls_require_ciphers invalid: %s", errmsg);
462 exim_underbar_exit(EXIT_SUCCESS);
466 rc = waitpid(pid, &status, 0);
467 } while (rc < 0 && errno == EINTR);
470 debug_printf("tls_validate_require_cipher child %d ended: status=0x%x\n",
473 signal(SIGCHLD, oldsignal);
481 #endif /*!DISABLE_TLS*/
482 #endif /*!MACRO_PREDEF*/