OpenSSL: better handling of $tls_{in,out}_certificate_verified under resumption
[exim.git] / src / src / tls.c
... / ...
CommitLineData
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
5/* Copyright (c) University of Cambridge 1995 - 2018 */
6/* See the file NOTICE for conditions of use and distribution. */
7
8/* This module provides TLS (aka SSL) support for Exim. The code for OpenSSL is
9based on a patch that was originally contributed by Steve Haslam. It was
10adapted from stunnel, a GPL program by Michal Trojnara. The code for GNU TLS is
11based on a patch contributed by Nikos Mavrogiannopoulos. Because these packages
12are so very different, the functions for each are kept in separate files. The
13relevant file is #included as required, after any any common functions.
14
15No cryptographic code is included in Exim. All this module does is to call
16functions from the OpenSSL or GNU TLS libraries. */
17
18
19#include "exim.h"
20#include "transports/smtp.h"
21
22#if defined(MACRO_PREDEF) && defined(SUPPORT_TLS)
23# include "macro_predef.h"
24# ifdef USE_GNUTLS
25# include "tls-gnu.c"
26# else
27# include "tls-openssl.c"
28# endif
29#endif
30
31#ifndef MACRO_PREDEF
32
33/* This module is compiled only when it is specifically requested in the
34build-time configuration. However, some compilers don't like compiling empty
35modules, so keep them happy with a dummy when skipping the rest. Make it
36reference itself to stop picky compilers complaining that it is unused, and put
37in a dummy argument to stop even pickier compilers complaining about infinite
38loops. */
39
40#ifndef SUPPORT_TLS
41static void dummy(int x) { dummy(x-1); }
42#else
43
44/* Static variables that are used for buffering data by both sets of
45functions and the common functions below.
46
47We're moving away from this; GnuTLS is already using a state, which
48can switch, so we can do TLS callouts during ACLs. */
49
50static const int ssl_xfer_buffer_size = 4096;
51#ifndef USE_GNUTLS
52static uschar *ssl_xfer_buffer = NULL;
53static int ssl_xfer_buffer_lwm = 0;
54static int ssl_xfer_buffer_hwm = 0;
55static int ssl_xfer_eof = FALSE;
56static BOOL ssl_xfer_error = FALSE;
57#endif
58
59uschar *tls_channelbinding_b64 = NULL;
60
61
62/*************************************************
63* Expand string; give error on failure *
64*************************************************/
65
66/* If expansion is forced to fail, set the result NULL and return TRUE.
67Other failures return FALSE. For a server, an SMTP response is given.
68
69Arguments:
70 s the string to expand; if NULL just return TRUE
71 name name of string being expanded (for error)
72 result where to put the result
73
74Returns: TRUE if OK; result may still be NULL after forced failure
75*/
76
77static BOOL
78expand_check(const uschar *s, const uschar *name, uschar **result, uschar ** errstr)
79{
80if (!s)
81 *result = NULL;
82else if ( !(*result = expand_string(US s)) /* need to clean up const more */
83 && !f.expand_string_forcedfail
84 )
85 {
86 *errstr = US"Internal error";
87 log_write(0, LOG_MAIN|LOG_PANIC, "expansion of %s failed: %s", name,
88 expand_string_message);
89 return FALSE;
90 }
91return TRUE;
92}
93
94
95/*************************************************
96* Timezone environment flipping *
97*************************************************/
98
99static uschar *
100to_tz(uschar * tz)
101{
102uschar * old = US getenv("TZ");
103(void) setenv("TZ", CCS tz, 1);
104tzset();
105return old;
106}
107
108static void
109restore_tz(uschar * tz)
110{
111if (tz)
112 (void) setenv("TZ", CCS tz, 1);
113else
114 (void) os_unsetenv(US"TZ");
115tzset();
116}
117
118/*************************************************
119* Many functions are package-specific *
120*************************************************/
121
122#ifdef USE_GNUTLS
123# include "tls-gnu.c"
124# include "tlscert-gnu.c"
125
126# define ssl_xfer_buffer (state_server.xfer_buffer)
127# define ssl_xfer_buffer_lwm (state_server.xfer_buffer_lwm)
128# define ssl_xfer_buffer_hwm (state_server.xfer_buffer_hwm)
129# define ssl_xfer_eof (state_server.xfer_eof)
130# define ssl_xfer_error (state_server.xfer_error)
131
132#else
133# include "tls-openssl.c"
134# include "tlscert-openssl.c"
135#endif
136
137
138
139/*************************************************
140* TLS version of ungetc *
141*************************************************/
142
143/* Puts a character back in the input buffer. Only ever
144called once.
145Only used by the server-side TLS.
146
147Arguments:
148 ch the character
149
150Returns: the character
151*/
152
153int
154tls_ungetc(int ch)
155{
156ssl_xfer_buffer[--ssl_xfer_buffer_lwm] = ch;
157return ch;
158}
159
160
161
162/*************************************************
163* TLS version of feof *
164*************************************************/
165
166/* Tests for a previous EOF
167Only used by the server-side TLS.
168
169Arguments: none
170Returns: non-zero if the eof flag is set
171*/
172
173int
174tls_feof(void)
175{
176return (int)ssl_xfer_eof;
177}
178
179
180
181/*************************************************
182* TLS version of ferror *
183*************************************************/
184
185/* Tests for a previous read error, and returns with errno
186restored to what it was when the error was detected.
187Only used by the server-side TLS.
188
189>>>>> Hmm. Errno not handled yet. Where do we get it from? >>>>>
190
191Arguments: none
192Returns: non-zero if the error flag is set
193*/
194
195int
196tls_ferror(void)
197{
198return (int)ssl_xfer_error;
199}
200
201
202/*************************************************
203* TLS version of smtp_buffered *
204*************************************************/
205
206/* Tests for unused chars in the TLS input buffer.
207Only used by the server-side TLS.
208
209Arguments: none
210Returns: TRUE/FALSE
211*/
212
213BOOL
214tls_smtp_buffered(void)
215{
216return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm;
217}
218
219
220#endif /* SUPPORT_TLS */
221
222void
223tls_modify_variables(tls_support * dest_tsp)
224{
225modify_variable(US"tls_bits", &dest_tsp->bits);
226modify_variable(US"tls_certificate_verified", &dest_tsp->certificate_verified);
227modify_variable(US"tls_cipher", &dest_tsp->cipher);
228modify_variable(US"tls_peerdn", &dest_tsp->peerdn);
229#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
230modify_variable(US"tls_sni", &dest_tsp->sni);
231#endif
232}
233
234
235#ifdef SUPPORT_TLS
236/************************************************
237* TLS certificate name operations *
238************************************************/
239
240/* Convert an rfc4514 DN to an exim comma-sep list.
241Backslashed commas need to be replaced by doublecomma
242for Exim's list quoting. We modify the given string
243inplace.
244*/
245
246static void
247dn_to_list(uschar * dn)
248{
249for (uschar * cp = dn; *cp; cp++)
250 if (cp[0] == '\\' && cp[1] == ',')
251 *cp++ = ',';
252}
253
254
255/* Extract fields of a given type from an RFC4514-
256format Distinguished Name. Return an Exim list.
257NOTE: We modify the supplied dn string during operation.
258
259Arguments:
260 dn Distinguished Name string
261 mod list containing optional output list-sep and
262 field selector match, comma-separated
263Return:
264 allocated string with list of matching fields,
265 field type stripped
266*/
267
268uschar *
269tls_field_from_dn(uschar * dn, const uschar * mod)
270{
271int insep = ',';
272uschar outsep = '\n';
273uschar * ele;
274uschar * match = NULL;
275int len;
276gstring * list = NULL;
277
278while ((ele = string_nextinlist(&mod, &insep, NULL, 0)))
279 if (ele[0] != '>')
280 match = ele; /* field tag to match */
281 else if (ele[1])
282 outsep = ele[1]; /* nondefault output separator */
283
284dn_to_list(dn);
285insep = ',';
286len = match ? Ustrlen(match) : -1;
287while ((ele = string_nextinlist(CUSS &dn, &insep, NULL, 0)))
288 if ( !match
289 || Ustrncmp(ele, match, len) == 0 && ele[len] == '='
290 )
291 list = string_append_listele(list, outsep, ele+len+1);
292return string_from_gstring(list);
293}
294
295
296/* Compare a domain name with a possibly-wildcarded name. Wildcards
297are restricted to a single one, as the first element of patterns
298having at least three dot-separated elements. Case-independent.
299Return TRUE for a match
300*/
301static BOOL
302is_name_match(const uschar * name, const uschar * pat)
303{
304uschar * cp;
305return *pat == '*' /* possible wildcard match */
306 ? *++pat == '.' /* starts star, dot */
307 && !Ustrchr(++pat, '*') /* has no more stars */
308 && Ustrchr(pat, '.') /* and has another dot. */
309 && (cp = Ustrchr(name, '.'))/* The name has at least one dot */
310 && strcmpic(++cp, pat) == 0 /* and we only compare after it. */
311 : !Ustrchr(pat+1, '*')
312 && strcmpic(name, pat) == 0;
313}
314
315/* Compare a list of names with the dnsname elements
316of the Subject Alternate Name, if any, and the
317Subject otherwise.
318
319Arguments:
320 namelist names to compare
321 cert certificate
322
323Returns:
324 TRUE/FALSE
325*/
326
327BOOL
328tls_is_name_for_cert(const uschar * namelist, void * cert)
329{
330uschar * altnames = tls_cert_subject_altname(cert, US"dns");
331uschar * subjdn;
332uschar * certname;
333int cmp_sep = 0;
334uschar * cmpname;
335
336if ((altnames = tls_cert_subject_altname(cert, US"dns")))
337 {
338 int alt_sep = '\n';
339 while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0)))
340 {
341 const uschar * an = altnames;
342 while ((certname = string_nextinlist(&an, &alt_sep, NULL, 0)))
343 if (is_name_match(cmpname, certname))
344 return TRUE;
345 }
346 }
347
348else if ((subjdn = tls_cert_subject(cert, NULL)))
349 {
350 int sn_sep = ',';
351
352 dn_to_list(subjdn);
353 while ((cmpname = string_nextinlist(&namelist, &cmp_sep, NULL, 0)))
354 {
355 const uschar * sn = subjdn;
356 while ((certname = string_nextinlist(&sn, &sn_sep, NULL, 0)))
357 if ( *certname++ == 'C'
358 && *certname++ == 'N'
359 && *certname++ == '='
360 && is_name_match(cmpname, certname)
361 )
362 return TRUE;
363 }
364 }
365return FALSE;
366}
367#endif /*SUPPORT_TLS*/
368#endif /*!MACRO_PREDEF*/
369
370/* vi: aw ai sw=2
371*/
372/* End of tls.c */