Copyright updates:
[exim.git] / src / src / tls.c
CommitLineData
059ec3d9
PH
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
f9ba5e22 5/* Copyright (c) University of Cambridge 1995 - 2018 */
1e1ddfac 6/* Copyright (c) The Exim Maintainers 2020 */
059ec3d9
PH
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
10based on a patch that was originally contributed by Steve Haslam. It was
11adapted from stunnel, a GPL program by Michal Trojnara. The code for GNU TLS is
6aa6fc9c 12based on a patch contributed by Nikos Mavrogiannopoulos. Because these packages
059ec3d9
PH
13are so very different, the functions for each are kept in separate files. The
14relevant file is #included as required, after any any common functions.
15
16No cryptographic code is included in Exim. All this module does is to call
17functions from the OpenSSL or GNU TLS libraries. */
18
19
20#include "exim.h"
65867078 21#include "transports/smtp.h"
059ec3d9 22
de517fd3
JH
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
01603eec 28#if defined(MACRO_PREDEF) && !defined(DISABLE_TLS)
b10c87b3
JH
29# include "macro_predef.h"
30# ifdef USE_GNUTLS
31# include "tls-gnu.c"
32# else
8442641e
JH
33# include "tls-openssl.c"
34# endif
35#endif
36
37#ifndef MACRO_PREDEF
38
059ec3d9
PH
39/* This module is compiled only when it is specifically requested in the
40build-time configuration. However, some compilers don't like compiling empty
41modules, so keep them happy with a dummy when skipping the rest. Make it
42reference itself to stop picky compilers complaining that it is unused, and put
43in a dummy argument to stop even pickier compilers complaining about infinite
44loops. */
45
01603eec 46#ifdef DISABLE_TLS
059ec3d9
PH
47static void dummy(int x) { dummy(x-1); }
48#else
49
50/* Static variables that are used for buffering data by both sets of
4fe99a6c 51functions and the common functions below.
059ec3d9 52
4fe99a6c
PP
53We're moving away from this; GnuTLS is already using a state, which
54can switch, so we can do TLS callouts during ACLs. */
059ec3d9 55
17c76198 56static const int ssl_xfer_buffer_size = 4096;
de517fd3 57#ifdef USE_OPENSSL
4fe99a6c 58static uschar *ssl_xfer_buffer = NULL;
059ec3d9
PH
59static int ssl_xfer_buffer_lwm = 0;
60static int ssl_xfer_buffer_hwm = 0;
8b77d27a
JH
61static int ssl_xfer_eof = FALSE;
62static BOOL ssl_xfer_error = FALSE;
4fe99a6c 63#endif
059ec3d9 64
059ec3d9
PH
65
66/*************************************************
67* Expand string; give error on failure *
68*************************************************/
69
70/* If expansion is forced to fail, set the result NULL and return TRUE.
71Other failures return FALSE. For a server, an SMTP response is given.
72
73Arguments:
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
77
78Returns: TRUE if OK; result may still be NULL after forced failure
79*/
80
81static BOOL
cf0c6164 82expand_check(const uschar *s, const uschar *name, uschar **result, uschar ** errstr)
059ec3d9 83{
cf0c6164
JH
84if (!s)
85 *result = NULL;
86else if ( !(*result = expand_string(US s)) /* need to clean up const more */
8768d548 87 && !f.expand_string_forcedfail
cf0c6164 88 )
059ec3d9 89 {
cf0c6164
JH
90 *errstr = US"Internal error";
91 log_write(0, LOG_MAIN|LOG_PANIC, "expansion of %s failed: %s", name,
92 expand_string_message);
93 return FALSE;
059ec3d9
PH
94 }
95return TRUE;
96}
97
98
99/*************************************************
e9477a08
JH
100* Timezone environment flipping *
101*************************************************/
102
103static uschar *
104to_tz(uschar * tz)
105{
dfe7d917
JH
106uschar * old = US getenv("TZ");
107(void) setenv("TZ", CCS tz, 1);
271019bd 108tzset();
dfe7d917 109return old;
e9477a08 110}
dfe7d917 111
e9477a08
JH
112static void
113restore_tz(uschar * tz)
114{
dfe7d917
JH
115if (tz)
116 (void) setenv("TZ", CCS tz, 1);
117else
93a680e4 118 (void) os_unsetenv(US"TZ");
271019bd 119tzset();
e9477a08
JH
120}
121
122/*************************************************
059ec3d9
PH
123* Many functions are package-specific *
124*************************************************/
125
126#ifdef USE_GNUTLS
27f19eb4
JH
127# include "tls-gnu.c"
128# include "tlscert-gnu.c"
27f19eb4
JH
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)
de517fd3 134#endif
4fe99a6c 135
de517fd3 136#ifdef USE_OPENSSL
27f19eb4
JH
137# include "tls-openssl.c"
138# include "tlscert-openssl.c"
059ec3d9
PH
139#endif
140
141
142
143/*************************************************
144* TLS version of ungetc *
145*************************************************/
146
147/* Puts a character back in the input buffer. Only ever
148called once.
389ca47a 149Only used by the server-side TLS.
059ec3d9
PH
150
151Arguments:
152 ch the character
153
154Returns: the character
155*/
156
157int
158tls_ungetc(int ch)
159{
160ssl_xfer_buffer[--ssl_xfer_buffer_lwm] = ch;
161return ch;
162}
163
164
165
166/*************************************************
167* TLS version of feof *
168*************************************************/
169
170/* Tests for a previous EOF
389ca47a 171Only used by the server-side TLS.
059ec3d9
PH
172
173Arguments: none
174Returns: non-zero if the eof flag is set
175*/
176
177int
178tls_feof(void)
179{
8b77d27a 180return (int)ssl_xfer_eof;
059ec3d9
PH
181}
182
183
184
185/*************************************************
186* TLS version of ferror *
187*************************************************/
188
189/* Tests for a previous read error, and returns with errno
190restored to what it was when the error was detected.
389ca47a 191Only used by the server-side TLS.
059ec3d9
PH
192
193>>>>> Hmm. Errno not handled yet. Where do we get it from? >>>>>
194
195Arguments: none
196Returns: non-zero if the error flag is set
197*/
198
199int
200tls_ferror(void)
201{
8b77d27a 202return (int)ssl_xfer_error;
059ec3d9
PH
203}
204
58eb016e
PH
205
206/*************************************************
207* TLS version of smtp_buffered *
208*************************************************/
209
210/* Tests for unused chars in the TLS input buffer.
389ca47a 211Only used by the server-side TLS.
58eb016e
PH
212
213Arguments: none
214Returns: TRUE/FALSE
215*/
216
217BOOL
218tls_smtp_buffered(void)
219{
220return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm;
221}
222
223
01603eec 224#endif /*DISABLE_TLS*/
059ec3d9 225
35aba663
JH
226void
227tls_modify_variables(tls_support * dest_tsp)
228{
229modify_variable(US"tls_bits", &dest_tsp->bits);
230modify_variable(US"tls_certificate_verified", &dest_tsp->certificate_verified);
231modify_variable(US"tls_cipher", &dest_tsp->cipher);
232modify_variable(US"tls_peerdn", &dest_tsp->peerdn);
de517fd3 233#ifdef USE_OPENSSL
35aba663
JH
234modify_variable(US"tls_sni", &dest_tsp->sni);
235#endif
236}
237
417bfce4 238
01603eec 239#ifndef DISABLE_TLS
812a6045
JH
240/************************************************
241* TLS certificate name operations *
242************************************************/
243
244/* Convert an rfc4514 DN to an exim comma-sep list.
245Backslashed commas need to be replaced by doublecomma
246for Exim's list quoting. We modify the given string
247inplace.
248*/
249
250static void
251dn_to_list(uschar * dn)
252{
d7978c0f 253for (uschar * cp = dn; *cp; cp++)
812a6045
JH
254 if (cp[0] == '\\' && cp[1] == ',')
255 *cp++ = ',';
256}
257
258
259/* Extract fields of a given type from an RFC4514-
260format Distinguished Name. Return an Exim list.
261NOTE: We modify the supplied dn string during operation.
262
263Arguments:
264 dn Distinguished Name string
bfbad1dd 265 mod list containing optional output list-sep and
812a6045
JH
266 field selector match, comma-separated
267Return:
268 allocated string with list of matching fields,
269 field type stripped
270*/
271
272uschar *
55414b25 273tls_field_from_dn(uschar * dn, const uschar * mod)
812a6045
JH
274{
275int insep = ',';
276uschar outsep = '\n';
277uschar * ele;
278uschar * match = NULL;
279int len;
acec9514 280gstring * list = NULL;
812a6045 281
7437665e 282while ((ele = string_nextinlist(&mod, &insep, NULL, 0)))
812a6045
JH
283 if (ele[0] != '>')
284 match = ele; /* field tag to match */
285 else if (ele[1])
bfbad1dd 286 outsep = ele[1]; /* nondefault output separator */
812a6045
JH
287
288dn_to_list(dn);
289insep = ',';
bfbad1dd 290len = match ? Ustrlen(match) : -1;
55414b25 291while ((ele = string_nextinlist(CUSS &dn, &insep, NULL, 0)))
bfbad1dd
JH
292 if ( !match
293 || Ustrncmp(ele, match, len) == 0 && ele[len] == '='
294 )
acec9514
JH
295 list = string_append_listele(list, outsep, ele+len+1);
296return string_from_gstring(list);
812a6045
JH
297}
298
299
e51c7be2
JH
300/* Compare a domain name with a possibly-wildcarded name. Wildcards
301are restricted to a single one, as the first element of patterns
302having at least three dot-separated elements. Case-independent.
303Return TRUE for a match
304*/
305static BOOL
306is_name_match(const uschar * name, const uschar * pat)
307{
308uschar * cp;
309return *pat == '*' /* possible wildcard match */
310 ? *++pat == '.' /* starts star, dot */
311 && !Ustrchr(++pat, '*') /* has no more stars */
312 && Ustrchr(pat, '.') /* and has another dot. */
313 && (cp = Ustrchr(name, '.'))/* The name has at least one dot */
314 && strcmpic(++cp, pat) == 0 /* and we only compare after it. */
315 : !Ustrchr(pat+1, '*')
316 && strcmpic(name, pat) == 0;
317}
318
319/* Compare a list of names with the dnsname elements
320of the Subject Alternate Name, if any, and the
321Subject otherwise.
322
323Arguments:
324 namelist names to compare
325 cert certificate
326
327Returns:
328 TRUE/FALSE
329*/
330
331BOOL
55414b25 332tls_is_name_for_cert(const uschar * namelist, void * cert)
e51c7be2
JH
333{
334uschar * altnames = tls_cert_subject_altname(cert, US"dns");
335uschar * subjdn;
336uschar * certname;
337int cmp_sep = 0;
338uschar * cmpname;
339
340if ((altnames = tls_cert_subject_altname(cert, US"dns")))
341 {
342 int alt_sep = '\n';
7437665e 343 while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0)))
e51c7be2 344 {
55414b25 345 const uschar * an = altnames;
7437665e 346 while ((certname = string_nextinlist(&an, &alt_sep, NULL, 0)))
e51c7be2
JH
347 if (is_name_match(cmpname, certname))
348 return TRUE;
349 }
350 }
351
352else if ((subjdn = tls_cert_subject(cert, NULL)))
353 {
354 int sn_sep = ',';
e51c7be2
JH
355
356 dn_to_list(subjdn);
67791ce4 357 while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0)))
e51c7be2 358 {
55414b25 359 const uschar * sn = subjdn;
7437665e 360 while ((certname = string_nextinlist(&sn, &sn_sep, NULL, 0)))
e51c7be2
JH
361 if ( *certname++ == 'C'
362 && *certname++ == 'N'
363 && *certname++ == '='
364 && is_name_match(cmpname, certname)
365 )
366 return TRUE;
367 }
368 }
369return FALSE;
370}
6b5cbf74
JH
371
372
2e5d9e71
JH
373/* Environment cleanup: The GnuTLS library uses SSLKEYLOGFILE in the environment
374and writes a file by that name. Our OpenSSL code does the same, using keying
375info from the library API.
376The GnuTLS support only works if exim is run by root, not taking advantage of
377the setuid bit.
378You can use either the external environment (modulo the keep_environment config)
379or the add_environment config option for SSLKEYLOGFILE; the latter takes
380precedence.
6b5cbf74
JH
381
382If the path is absolute, require it starts with the spooldir; otherwise delete
383the env variable. If relative, prefix the spooldir.
384*/
385void
386tls_clean_env(void)
387{
388uschar * path = US getenv("SSLKEYLOGFILE");
389if (path)
390 if (!*path)
391 unsetenv("SSLKEYLOGFILE");
392 else if (*path != '/')
393 {
394 DEBUG(D_tls)
395 debug_printf("prepending spooldir to env SSLKEYLOGFILE\n");
396 setenv("SSLKEYLOGFILE", CCS string_sprintf("%s/%s", spool_directory, path), 1);
397 }
398 else if (Ustrncmp(path, spool_directory, Ustrlen(spool_directory)) != 0)
399 {
400 DEBUG(D_tls)
86ede124 401 debug_printf("removing env SSLKEYLOGFILE=%s: not under spooldir\n", path);
6b5cbf74
JH
402 unsetenv("SSLKEYLOGFILE");
403 }
404}
d85cdeb5
JH
405
406/*************************************************
407* Drop privs for checking TLS config *
408*************************************************/
409
410/* We want to validate TLS options during readconf, but do not want to be
411root when we call into the TLS library, in case of library linkage errors
412which cause segfaults; before this check, those were always done as the Exim
413runtime user and it makes sense to continue with that.
414
415Assumes: tls_require_ciphers has been set, if it will be
416 exim_user has been set, if it will be
417 exim_group has been set, if it will be
418
419Returns: bool for "okay"; false will cause caller to immediately exit.
420*/
421
422BOOL
423tls_dropprivs_validate_require_cipher(BOOL nowarn)
424{
425const uschar *errmsg;
426pid_t pid;
427int rc, status;
428void (*oldsignal)(int);
429
430/* If TLS will never be used, no point checking ciphers */
431
432if ( !tls_advertise_hosts
433 || !*tls_advertise_hosts
434 || Ustrcmp(tls_advertise_hosts, ":") == 0
435 )
436 return TRUE;
437else if (!nowarn && !tls_certificate)
438 log_write(0, LOG_MAIN,
439 "Warning: No server certificate defined; will use a selfsigned one.\n"
440 " Suggested action: either install a certificate or change tls_advertise_hosts option");
441
442oldsignal = signal(SIGCHLD, SIG_DFL);
443
444fflush(NULL);
4b01271f 445if ((pid = exim_fork(US"cipher-validate")) < 0)
d85cdeb5
JH
446 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork failed for TLS check");
447
448if (pid == 0)
449 {
450 /* in some modes, will have dropped privilege already */
451 if (!geteuid())
452 exim_setugid(exim_uid, exim_gid, FALSE,
453 US"calling tls_validate_require_cipher");
454
455 if ((errmsg = tls_validate_require_cipher()))
456 log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
457 "tls_require_ciphers invalid: %s", errmsg);
458 fflush(NULL);
81022793 459 exim_underbar_exit(EXIT_SUCCESS);
d85cdeb5
JH
460 }
461
462do {
463 rc = waitpid(pid, &status, 0);
464} while (rc < 0 && errno == EINTR);
465
466DEBUG(D_tls)
467 debug_printf("tls_validate_require_cipher child %d ended: status=0x%x\n",
468 (int)pid, status);
469
470signal(SIGCHLD, oldsignal);
471
472return status == 0;
473}
474
475
476
477
01603eec 478#endif /*!DISABLE_TLS*/
8442641e 479#endif /*!MACRO_PREDEF*/
812a6045
JH
480
481/* vi: aw ai sw=2
482*/
059ec3d9 483/* End of tls.c */