1fd10d52b374000a425166f90804a88482076aea
[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 /* 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
9 based on a patch that was originally contributed by Steve Haslam. It was
10 adapted from stunnel, a GPL program by Michal Trojnara. The code for GNU TLS is
11 based on a patch contributed by Nikos Mavrogiannopoulos. Because these packages
12 are so very different, the functions for each are kept in separate files. The
13 relevant file is #included as required, after any any common functions.
14
15 No cryptographic code is included in Exim. All this module does is to call
16 functions 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(DISABLE_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
34 build-time configuration. However, some compilers don't like compiling empty
35 modules, so keep them happy with a dummy when skipping the rest. Make it
36 reference itself to stop picky compilers complaining that it is unused, and put
37 in a dummy argument to stop even pickier compilers complaining about infinite
38 loops. */
39
40 #ifdef DISABLE_TLS
41 static void dummy(int x) { dummy(x-1); }
42 #else
43
44 /* Static variables that are used for buffering data by both sets of
45 functions and the common functions below.
46
47 We're moving away from this; GnuTLS is already using a state, which
48 can switch, so we can do TLS callouts during ACLs. */
49
50 static const int ssl_xfer_buffer_size = 4096;
51 #ifndef USE_GNUTLS
52 static uschar *ssl_xfer_buffer = NULL;
53 static int ssl_xfer_buffer_lwm = 0;
54 static int ssl_xfer_buffer_hwm = 0;
55 static int ssl_xfer_eof = FALSE;
56 static BOOL ssl_xfer_error = FALSE;
57 #endif
58
59 uschar *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.
67 Other failures return FALSE. For a server, an SMTP response is given.
68
69 Arguments:
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
74 Returns: TRUE if OK; result may still be NULL after forced failure
75 */
76
77 static BOOL
78 expand_check(const uschar *s, const uschar *name, uschar **result, uschar ** errstr)
79 {
80 if (!s)
81 *result = NULL;
82 else 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 }
91 return TRUE;
92 }
93
94
95 /*************************************************
96 * Timezone environment flipping *
97 *************************************************/
98
99 static uschar *
100 to_tz(uschar * tz)
101 {
102 uschar * old = US getenv("TZ");
103 (void) setenv("TZ", CCS tz, 1);
104 tzset();
105 return old;
106 }
107
108 static void
109 restore_tz(uschar * tz)
110 {
111 if (tz)
112 (void) setenv("TZ", CCS tz, 1);
113 else
114 (void) os_unsetenv(US"TZ");
115 tzset();
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
144 called once.
145 Only used by the server-side TLS.
146
147 Arguments:
148 ch the character
149
150 Returns: the character
151 */
152
153 int
154 tls_ungetc(int ch)
155 {
156 ssl_xfer_buffer[--ssl_xfer_buffer_lwm] = ch;
157 return ch;
158 }
159
160
161
162 /*************************************************
163 * TLS version of feof *
164 *************************************************/
165
166 /* Tests for a previous EOF
167 Only used by the server-side TLS.
168
169 Arguments: none
170 Returns: non-zero if the eof flag is set
171 */
172
173 int
174 tls_feof(void)
175 {
176 return (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
186 restored to what it was when the error was detected.
187 Only used by the server-side TLS.
188
189 >>>>> Hmm. Errno not handled yet. Where do we get it from? >>>>>
190
191 Arguments: none
192 Returns: non-zero if the error flag is set
193 */
194
195 int
196 tls_ferror(void)
197 {
198 return (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.
207 Only used by the server-side TLS.
208
209 Arguments: none
210 Returns: TRUE/FALSE
211 */
212
213 BOOL
214 tls_smtp_buffered(void)
215 {
216 return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm;
217 }
218
219
220 #endif /*DISABLE_TLS*/
221
222 void
223 tls_modify_variables(tls_support * dest_tsp)
224 {
225 modify_variable(US"tls_bits", &dest_tsp->bits);
226 modify_variable(US"tls_certificate_verified", &dest_tsp->certificate_verified);
227 modify_variable(US"tls_cipher", &dest_tsp->cipher);
228 modify_variable(US"tls_peerdn", &dest_tsp->peerdn);
229 #if !defined(DISABLE_TLS) && !defined(USE_GNUTLS)
230 modify_variable(US"tls_sni", &dest_tsp->sni);
231 #endif
232 }
233
234
235 #ifndef DISABLE_TLS
236 /************************************************
237 * TLS certificate name operations *
238 ************************************************/
239
240 /* Convert an rfc4514 DN to an exim comma-sep list.
241 Backslashed commas need to be replaced by doublecomma
242 for Exim's list quoting. We modify the given string
243 inplace.
244 */
245
246 static void
247 dn_to_list(uschar * dn)
248 {
249 for (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-
256 format Distinguished Name. Return an Exim list.
257 NOTE: We modify the supplied dn string during operation.
258
259 Arguments:
260 dn Distinguished Name string
261 mod list containing optional output list-sep and
262 field selector match, comma-separated
263 Return:
264 allocated string with list of matching fields,
265 field type stripped
266 */
267
268 uschar *
269 tls_field_from_dn(uschar * dn, const uschar * mod)
270 {
271 int insep = ',';
272 uschar outsep = '\n';
273 uschar * ele;
274 uschar * match = NULL;
275 int len;
276 gstring * list = NULL;
277
278 while ((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
284 dn_to_list(dn);
285 insep = ',';
286 len = match ? Ustrlen(match) : -1;
287 while ((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);
292 return string_from_gstring(list);
293 }
294
295
296 /* Compare a domain name with a possibly-wildcarded name. Wildcards
297 are restricted to a single one, as the first element of patterns
298 having at least three dot-separated elements. Case-independent.
299 Return TRUE for a match
300 */
301 static BOOL
302 is_name_match(const uschar * name, const uschar * pat)
303 {
304 uschar * cp;
305 return *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
316 of the Subject Alternate Name, if any, and the
317 Subject otherwise.
318
319 Arguments:
320 namelist names to compare
321 cert certificate
322
323 Returns:
324 TRUE/FALSE
325 */
326
327 BOOL
328 tls_is_name_for_cert(const uschar * namelist, void * cert)
329 {
330 uschar * altnames = tls_cert_subject_altname(cert, US"dns");
331 uschar * subjdn;
332 uschar * certname;
333 int cmp_sep = 0;
334 uschar * cmpname;
335
336 if ((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
348 else 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 }
365 return FALSE;
366 }
367 #endif /*!DISABLE_TLS*/
368 #endif /*!MACRO_PREDEF*/
369
370 /* vi: aw ai sw=2
371 */
372 /* End of tls.c */