Commit | Line | Data |
---|---|---|
059ec3d9 PH |
1 | /************************************************* |
2 | * Exim - an Internet mail transport agent * | |
3 | *************************************************/ | |
4 | ||
0a49a7a4 | 5 | /* Copyright (c) University of Cambridge 1995 - 2009 */ |
059ec3d9 PH |
6 | /* See the file NOTICE for conditions of use and distribution. */ |
7 | ||
8 | /* This module provides TLS (aka SSL) support for Exim using the GnuTLS | |
9 | library. It is #included into tls.c when that library is used. The code herein | |
10 | is based on a patch that was contributed by Nikos Mavroyanopoulos. | |
11 | ||
12 | No cryptographic code is included in Exim. All this module does is to call | |
13 | functions from the GnuTLS library. */ | |
14 | ||
c566dd90 PP |
15 | /* Note: This appears to be using an old API from compat.h; it is likely that |
16 | someone familiary with GnuTLS programming could rework a lot of this to a | |
17 | modern API and perhaps remove the explicit knowledge of crypto algorithms from | |
18 | Exim. Such a re-work would be most welcome and we'd sacrifice support for | |
19 | older GnuTLS releases without too many qualms -- maturity and experience | |
20 | in crypto libraries tends to improve their robustness against attack. | |
21 | Frankly, if you maintain it, you decide what's supported and what isn't. */ | |
059ec3d9 PH |
22 | |
23 | /* Heading stuff for GnuTLS */ | |
24 | ||
25 | #include <gnutls/gnutls.h> | |
26 | #include <gnutls/x509.h> | |
27 | ||
28 | ||
29 | #define UNKNOWN_NAME "unknown" | |
87054a31 | 30 | #define DH_BITS 1024 |
b5aea5e1 PH |
31 | #define PARAM_SIZE 2*1024 |
32 | ||
059ec3d9 | 33 | |
7199e1ee | 34 | /* Values for verify_requirment */ |
059ec3d9 PH |
35 | |
36 | enum { VERIFY_NONE, VERIFY_OPTIONAL, VERIFY_REQUIRED }; | |
059ec3d9 PH |
37 | |
38 | /* Local static variables for GNUTLS */ | |
39 | ||
059ec3d9 PH |
40 | static host_item *client_host; |
41 | ||
059ec3d9 PH |
42 | static gnutls_dh_params dh_params = NULL; |
43 | ||
44 | static gnutls_certificate_server_credentials x509_cred = NULL; | |
45 | static gnutls_session tls_session = NULL; | |
46 | ||
47 | static char ssl_errstring[256]; | |
48 | ||
49 | static int ssl_session_timeout = 200; | |
50 | static int verify_requirement; | |
51 | ||
83da1223 PH |
52 | /* Priorities for TLS algorithms to use. In each case there's a default table, |
53 | and space into which it can be copied and altered. */ | |
059ec3d9 | 54 | |
83da1223 | 55 | static const int default_proto_priority[16] = { |
c566dd90 PP |
56 | /* These are gnutls_protocol_t enum values */ |
57 | #if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 7 | |
58 | GNUTLS_TLS1_2, | |
59 | #endif | |
60 | #if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 2 | |
61 | GNUTLS_TLS1_1, | |
62 | #endif | |
83da1223 PH |
63 | GNUTLS_TLS1, |
64 | GNUTLS_SSL3, | |
65 | 0 }; | |
66 | ||
67 | static int proto_priority[16]; | |
059ec3d9 | 68 | |
83da1223 | 69 | static const int default_kx_priority[16] = { |
059ec3d9 PH |
70 | GNUTLS_KX_RSA, |
71 | GNUTLS_KX_DHE_DSS, | |
72 | GNUTLS_KX_DHE_RSA, | |
059ec3d9 PH |
73 | 0 }; |
74 | ||
83da1223 PH |
75 | static int kx_priority[16]; |
76 | ||
059ec3d9 | 77 | static int default_cipher_priority[16] = { |
26dd5a95 | 78 | GNUTLS_CIPHER_AES_256_CBC, |
059ec3d9 PH |
79 | GNUTLS_CIPHER_AES_128_CBC, |
80 | GNUTLS_CIPHER_3DES_CBC, | |
26dd5a95 | 81 | GNUTLS_CIPHER_ARCFOUR_128, |
059ec3d9 PH |
82 | 0 }; |
83 | ||
84 | static int cipher_priority[16]; | |
85 | ||
83da1223 | 86 | static const int default_mac_priority[16] = { |
059ec3d9 PH |
87 | GNUTLS_MAC_SHA, |
88 | GNUTLS_MAC_MD5, | |
89 | 0 }; | |
90 | ||
83da1223 PH |
91 | static int mac_priority[16]; |
92 | ||
93 | /* These two are currently not changeable. */ | |
94 | ||
059ec3d9 PH |
95 | static const int comp_priority[16] = { GNUTLS_COMP_NULL, 0 }; |
96 | static const int cert_type_priority[16] = { GNUTLS_CRT_X509, 0 }; | |
97 | ||
83da1223 | 98 | /* Tables of priority names and equivalent numbers */ |
059ec3d9 PH |
99 | |
100 | typedef struct pri_item { | |
101 | uschar *name; | |
102 | int *values; | |
103 | } pri_item; | |
104 | ||
83da1223 | 105 | |
c566dd90 PP |
106 | #if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 7 |
107 | static int tls1_2_codes[] = { GNUTLS_TLS1_2, 0 }; | |
108 | #endif | |
109 | #if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 2 | |
110 | static int tls1_1_codes[] = { GNUTLS_TLS1_1, 0 }; | |
111 | #endif | |
112 | /* more recent libraries define this as an equivalent value to the | |
113 | canonical GNUTLS_TLS1_0; since they're the same, we stick to the | |
114 | older name. */ | |
115 | static int tls1_0_codes[] = { GNUTLS_TLS1, 0 }; | |
83da1223 PH |
116 | static int ssl3_codes[] = { GNUTLS_SSL3, 0 }; |
117 | ||
118 | static pri_item proto_index[] = { | |
c566dd90 PP |
119 | #if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 7 |
120 | { US"TLS1.2", tls1_2_codes }, | |
121 | #endif | |
122 | #if GNUTLS_VERSION_MAJOR > 1 || GNUTLS_VERSION_MINOR >= 2 | |
123 | { US"TLS1.1", tls1_1_codes }, | |
124 | #endif | |
125 | { US"TLS1.0", tls1_0_codes }, | |
126 | { US"TLS1", tls1_0_codes }, | |
83da1223 PH |
127 | { US"SSL3", ssl3_codes } |
128 | }; | |
129 | ||
130 | ||
131 | static int kx_rsa_codes[] = { GNUTLS_KX_RSA, | |
132 | GNUTLS_KX_DHE_RSA, 0 }; | |
133 | static int kx_dhe_codes[] = { GNUTLS_KX_DHE_DSS, | |
134 | GNUTLS_KX_DHE_RSA, 0 }; | |
135 | static int kx_dhe_dss_codes[] = { GNUTLS_KX_DHE_DSS, 0 }; | |
136 | static int kx_dhe_rsa_codes[] = { GNUTLS_KX_DHE_RSA, 0 }; | |
137 | ||
138 | static pri_item kx_index[] = { | |
139 | { US"DHE_DSS", kx_dhe_dss_codes }, | |
140 | { US"DHE_RSA", kx_dhe_rsa_codes }, | |
141 | { US"RSA", kx_rsa_codes }, | |
142 | { US"DHE", kx_dhe_codes } | |
143 | }; | |
144 | ||
145 | ||
059ec3d9 PH |
146 | static int arcfour_128_codes[] = { GNUTLS_CIPHER_ARCFOUR_128, 0 }; |
147 | static int arcfour_40_codes[] = { GNUTLS_CIPHER_ARCFOUR_40, 0 }; | |
148 | static int arcfour_codes[] = { GNUTLS_CIPHER_ARCFOUR_128, | |
149 | GNUTLS_CIPHER_ARCFOUR_40, 0 }; | |
150 | static int aes_256_codes[] = { GNUTLS_CIPHER_AES_256_CBC, 0 }; | |
151 | static int aes_128_codes[] = { GNUTLS_CIPHER_AES_128_CBC, 0 }; | |
152 | static int aes_codes[] = { GNUTLS_CIPHER_AES_256_CBC, | |
153 | GNUTLS_CIPHER_AES_128_CBC, 0 }; | |
154 | static int des3_codes[] = { GNUTLS_CIPHER_3DES_CBC, 0 }; | |
155 | ||
156 | static pri_item cipher_index[] = { | |
157 | { US"ARCFOUR_128", arcfour_128_codes }, | |
158 | { US"ARCFOUR_40", arcfour_40_codes }, | |
159 | { US"ARCFOUR", arcfour_codes }, | |
160 | { US"AES_256", aes_256_codes }, | |
161 | { US"AES_128", aes_128_codes }, | |
162 | { US"AES", aes_codes }, | |
163 | { US"3DES", des3_codes } | |
164 | }; | |
165 | ||
166 | ||
83da1223 PH |
167 | static int mac_sha_codes[] = { GNUTLS_MAC_SHA, 0 }; |
168 | static int mac_md5_codes[] = { GNUTLS_MAC_MD5, 0 }; | |
169 | ||
170 | static pri_item mac_index[] = { | |
171 | { US"SHA", mac_sha_codes }, | |
172 | { US"SHA1", mac_sha_codes }, | |
173 | { US"MD5", mac_md5_codes } | |
174 | }; | |
175 | ||
176 | ||
059ec3d9 PH |
177 | |
178 | /************************************************* | |
179 | * Handle TLS error * | |
180 | *************************************************/ | |
181 | ||
182 | /* Called from lots of places when errors occur before actually starting to do | |
183 | the TLS handshake, that is, while the session is still in clear. Always returns | |
184 | DEFER for a server and FAIL for a client so that most calls can use "return | |
185 | tls_error(...)" to do this processing and then give an appropriate return. A | |
186 | single function is used for both server and client, because it is called from | |
187 | some shared functions. | |
188 | ||
189 | Argument: | |
190 | prefix text to include in the logged error | |
191 | host NULL if setting up a server; | |
192 | the connected host if setting up a client | |
7199e1ee TF |
193 | msg additional error string (may be NULL) |
194 | usually obtained from gnutls_strerror() | |
059ec3d9 PH |
195 | |
196 | Returns: OK/DEFER/FAIL | |
197 | */ | |
198 | ||
199 | static int | |
7199e1ee | 200 | tls_error(uschar *prefix, host_item *host, const char *msg) |
059ec3d9 | 201 | { |
059ec3d9 PH |
202 | if (host == NULL) |
203 | { | |
7199e1ee TF |
204 | uschar *conn_info = smtp_get_connection_info(); |
205 | if (strncmp(conn_info, "SMTP ", 5) == 0) | |
206 | conn_info += 5; | |
207 | log_write(0, LOG_MAIN, "TLS error on %s (%s)%s%s", | |
208 | conn_info, prefix, msg ? ": " : "", msg ? msg : ""); | |
059ec3d9 PH |
209 | return DEFER; |
210 | } | |
211 | else | |
212 | { | |
7199e1ee TF |
213 | log_write(0, LOG_MAIN, "TLS error on connection to %s [%s] (%s)%s%s", |
214 | host->name, host->address, prefix, msg ? ": " : "", msg ? msg : ""); | |
059ec3d9 PH |
215 | return FAIL; |
216 | } | |
217 | } | |
218 | ||
219 | ||
220 | ||
221 | /************************************************* | |
222 | * Verify certificate * | |
223 | *************************************************/ | |
224 | ||
225 | /* Called after a successful handshake, when certificate verification is | |
226 | required or optional, for both server and client. | |
227 | ||
228 | Arguments: | |
229 | session GNUTLS session | |
230 | error where to put text giving a reason for failure | |
231 | ||
232 | Returns: TRUE/FALSE | |
233 | */ | |
234 | ||
235 | static BOOL | |
7199e1ee | 236 | verify_certificate(gnutls_session session, const char **error) |
059ec3d9 | 237 | { |
d641a340 | 238 | int rc = -1; |
059ec3d9 PH |
239 | uschar *dn_string = US""; |
240 | const gnutls_datum *cert; | |
89f897c3 | 241 | unsigned int verify, cert_size = 0; |
059ec3d9 PH |
242 | |
243 | *error = NULL; | |
244 | ||
245 | /* Get the peer's certificate. If it sent one, extract it's DN, and then | |
246 | attempt to verify the certificate. If no certificate is supplied, verification | |
247 | is forced to fail. */ | |
248 | ||
249 | cert = gnutls_certificate_get_peers(session, &cert_size); | |
250 | if (cert != NULL) | |
251 | { | |
252 | uschar buff[1024]; | |
253 | gnutls_x509_crt gcert; | |
254 | ||
255 | gnutls_x509_crt_init(&gcert); | |
256 | dn_string = US"unknown"; | |
257 | ||
258 | if (gnutls_x509_crt_import(gcert, cert, GNUTLS_X509_FMT_DER) == 0) | |
259 | { | |
260 | size_t bufsize = sizeof(buff); | |
261 | if (gnutls_x509_crt_get_dn(gcert, CS buff, &bufsize) >= 0) | |
262 | dn_string = string_copy_malloc(buff); | |
263 | } | |
264 | ||
89f897c3 | 265 | rc = gnutls_certificate_verify_peers2(session, &verify); |
059ec3d9 PH |
266 | } |
267 | else | |
268 | { | |
269 | DEBUG(D_tls) debug_printf("no peer certificate supplied\n"); | |
270 | verify = GNUTLS_CERT_INVALID; | |
7199e1ee | 271 | *error = "not supplied"; |
059ec3d9 PH |
272 | } |
273 | ||
274 | /* Handle the result of verification. INVALID seems to be set as well | |
275 | as REVOKED, but leave the test for both. */ | |
276 | ||
89f897c3 | 277 | if ((rc < 0) || (verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED)) != 0) |
059ec3d9 PH |
278 | { |
279 | tls_certificate_verified = FALSE; | |
280 | if (*error == NULL) *error = ((verify & GNUTLS_CERT_REVOKED) != 0)? | |
7199e1ee | 281 | "revoked" : "invalid"; |
059ec3d9 PH |
282 | if (verify_requirement == VERIFY_REQUIRED) |
283 | { | |
284 | DEBUG(D_tls) debug_printf("TLS certificate verification failed (%s): " | |
285 | "peerdn=%s\n", *error, dn_string); | |
286 | gnutls_alert_send(session, GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE); | |
287 | return FALSE; /* reject */ | |
288 | } | |
289 | DEBUG(D_tls) debug_printf("TLS certificate verify failure (%s) overridden " | |
290 | "(host in tls_try_verify_hosts): peerdn=%s\n", *error, dn_string); | |
291 | } | |
292 | else | |
293 | { | |
294 | tls_certificate_verified = TRUE; | |
295 | DEBUG(D_tls) debug_printf("TLS certificate verified: peerdn=%s\n", | |
296 | dn_string); | |
297 | } | |
298 | ||
299 | tls_peerdn = dn_string; | |
300 | return TRUE; /* accept */ | |
301 | } | |
302 | ||
303 | ||
304 | ||
059ec3d9 | 305 | /************************************************* |
575643cd | 306 | * Setup up DH parameters * |
059ec3d9 PH |
307 | *************************************************/ |
308 | ||
575643cd | 309 | /* Generating the D-H parameters may take a long time. They only need to |
059ec3d9 PH |
310 | be re-generated every so often, depending on security policy. What we do is to |
311 | keep these parameters in a file in the spool directory. If the file does not | |
312 | exist, we generate them. This means that it is easy to cause a regeneration. | |
313 | ||
314 | The new file is written as a temporary file and renamed, so that an incomplete | |
315 | file is never present. If two processes both compute some new parameters, you | |
316 | waste a bit of effort, but it doesn't seem worth messing around with locking to | |
317 | prevent this. | |
318 | ||
319 | Argument: | |
320 | host NULL for server, server for client (for error handling) | |
321 | ||
322 | Returns: OK/DEFER/FAIL | |
323 | */ | |
324 | ||
325 | static int | |
575643cd | 326 | init_dh(host_item *host) |
059ec3d9 | 327 | { |
b5aea5e1 | 328 | int fd; |
182ad5cf | 329 | int ret; |
b5aea5e1 | 330 | gnutls_datum m; |
059ec3d9 PH |
331 | uschar filename[200]; |
332 | ||
333 | /* Initialize the data structures for holding the parameters */ | |
334 | ||
059ec3d9 | 335 | ret = gnutls_dh_params_init(&dh_params); |
7199e1ee | 336 | if (ret < 0) return tls_error(US"init dh_params", host, gnutls_strerror(ret)); |
059ec3d9 PH |
337 | |
338 | /* Set up the name of the cache file */ | |
339 | ||
340 | if (!string_format(filename, sizeof(filename), "%s/gnutls-params", | |
341 | spool_directory)) | |
7199e1ee | 342 | return tls_error(US"overlong filename", host, NULL); |
059ec3d9 | 343 | |
b5aea5e1 | 344 | /* Open the cache file for reading and if successful, read it and set up the |
575643cd | 345 | parameters. */ |
059ec3d9 PH |
346 | |
347 | fd = Uopen(filename, O_RDONLY, 0); | |
b5aea5e1 | 348 | if (fd >= 0) |
059ec3d9 | 349 | { |
b5aea5e1 PH |
350 | struct stat statbuf; |
351 | if (fstat(fd, &statbuf) < 0) | |
352 | { | |
353 | (void)close(fd); | |
7199e1ee | 354 | return tls_error(US"TLS cache stat failed", host, strerror(errno)); |
b5aea5e1 | 355 | } |
059ec3d9 | 356 | |
b5aea5e1 PH |
357 | m.size = statbuf.st_size; |
358 | m.data = malloc(m.size); | |
359 | if (m.data == NULL) | |
7199e1ee TF |
360 | return tls_error(US"memory allocation failed", host, strerror(errno)); |
361 | errno = 0; | |
b5aea5e1 | 362 | if (read(fd, m.data, m.size) != m.size) |
7199e1ee | 363 | return tls_error(US"TLS cache read failed", host, strerror(errno)); |
b5aea5e1 PH |
364 | (void)close(fd); |
365 | ||
411ef850 | 366 | ret = gnutls_dh_params_import_pkcs3(dh_params, &m, GNUTLS_X509_FMT_PEM); |
7199e1ee TF |
367 | if (ret < 0) |
368 | return tls_error(US"DH params import", host, gnutls_strerror(ret)); | |
575643cd | 369 | DEBUG(D_tls) debug_printf("read D-H parameters from file\n"); |
b5aea5e1 PH |
370 | |
371 | free(m.data); | |
372 | } | |
373 | ||
374 | /* If the file does not exist, fall through to compute new data and cache it. | |
375 | If there was any other opening error, it is serious. */ | |
376 | ||
182ad5cf PH |
377 | else if (errno == ENOENT) |
378 | { | |
379 | ret = -1; | |
380 | DEBUG(D_tls) | |
381 | debug_printf("parameter cache file %s does not exist\n", filename); | |
382 | } | |
383 | else | |
b5aea5e1 | 384 | return tls_error(string_open_failed(errno, "%s for reading", filename), |
7199e1ee | 385 | host, NULL); |
b5aea5e1 PH |
386 | |
387 | /* If ret < 0, either the cache file does not exist, or the data it contains | |
388 | is not useful. One particular case of this is when upgrading from an older | |
389 | release of Exim in which the data was stored in a different format. We don't | |
390 | try to be clever and support both formats; we just regenerate new data in this | |
391 | case. */ | |
392 | ||
393 | if (ret < 0) | |
394 | { | |
395 | uschar tempfilename[sizeof(filename) + 10]; | |
059ec3d9 | 396 | |
059ec3d9 PH |
397 | DEBUG(D_tls) debug_printf("generating %d bit Diffie-Hellman key...\n", |
398 | DH_BITS); | |
399 | ret = gnutls_dh_params_generate2(dh_params, DH_BITS); | |
7199e1ee | 400 | if (ret < 0) return tls_error(US"D-H key generation", host, gnutls_strerror(ret)); |
059ec3d9 PH |
401 | |
402 | /* Write the parameters to a file in the spool directory so that we | |
403 | can use them from other Exim processes. */ | |
404 | ||
405 | sprintf(CS tempfilename, "%s-%d", filename, (int)getpid()); | |
406 | fd = Uopen(tempfilename, O_WRONLY|O_CREAT, 0400); | |
407 | if (fd < 0) | |
408 | return tls_error(string_open_failed(errno, "%s for writing", filename), | |
7199e1ee | 409 | host, NULL); |
059ec3d9 PH |
410 | (void)fchown(fd, exim_uid, exim_gid); /* Probably not necessary */ |
411 | ||
b5aea5e1 PH |
412 | /* export the parameters in a format that can be generated using GNUTLS' |
413 | * certtool or other programs. | |
414 | * | |
415 | * The commands for certtool are: | |
411ef850 | 416 | * $ certtool --generate-dh-params --bits 1024 > params |
b5aea5e1 PH |
417 | */ |
418 | ||
419 | m.size = PARAM_SIZE; | |
420 | m.data = malloc(m.size); | |
421 | if (m.data == NULL) | |
7199e1ee | 422 | return tls_error(US"memory allocation failed", host, strerror(errno)); |
b5aea5e1 | 423 | |
b5aea5e1 PH |
424 | m.size = PARAM_SIZE; |
425 | ret = gnutls_dh_params_export_pkcs3(dh_params, GNUTLS_X509_FMT_PEM, m.data, | |
426 | &m.size); | |
7199e1ee TF |
427 | if (ret < 0) |
428 | return tls_error(US"DH params export", host, gnutls_strerror(ret)); | |
059ec3d9 | 429 | |
b5aea5e1 | 430 | m.size = Ustrlen(m.data); |
7199e1ee | 431 | errno = 0; |
b5aea5e1 | 432 | if (write(fd, m.data, m.size) != m.size || write(fd, "\n", 1) != 1) |
7199e1ee | 433 | return tls_error(US"TLS cache write failed", host, strerror(errno)); |
059ec3d9 | 434 | |
b5aea5e1 | 435 | free(m.data); |
059ec3d9 PH |
436 | (void)close(fd); |
437 | ||
438 | if (rename(CS tempfilename, CS filename) < 0) | |
7199e1ee TF |
439 | return tls_error(string_sprintf("failed to rename %s as %s", |
440 | tempfilename, filename), host, strerror(errno)); | |
059ec3d9 | 441 | |
411ef850 | 442 | DEBUG(D_tls) debug_printf("wrote D-H parameters to file %s\n", filename); |
059ec3d9 PH |
443 | } |
444 | ||
411ef850 | 445 | DEBUG(D_tls) debug_printf("initialized D-H parameters\n"); |
059ec3d9 PH |
446 | return OK; |
447 | } | |
448 | ||
449 | ||
450 | ||
451 | ||
452 | /************************************************* | |
453 | * Initialize for GnuTLS * | |
454 | *************************************************/ | |
455 | ||
456 | /* Called from both server and client code. In the case of a server, errors | |
457 | before actual TLS negotiation return DEFER. | |
458 | ||
459 | Arguments: | |
460 | host connected host, if client; NULL if server | |
461 | certificate certificate file | |
462 | privatekey private key file | |
463 | cas CA certs file | |
464 | crl CRL file | |
465 | ||
466 | Returns: OK/DEFER/FAIL | |
467 | */ | |
468 | ||
469 | static int | |
470 | tls_init(host_item *host, uschar *certificate, uschar *privatekey, uschar *cas, | |
471 | uschar *crl) | |
472 | { | |
473 | int rc; | |
474 | uschar *cert_expanded, *key_expanded, *cas_expanded, *crl_expanded; | |
475 | ||
7199e1ee | 476 | client_host = host; |
059ec3d9 PH |
477 | |
478 | rc = gnutls_global_init(); | |
7199e1ee | 479 | if (rc < 0) return tls_error(US"tls-init", host, gnutls_strerror(rc)); |
059ec3d9 | 480 | |
575643cd PH |
481 | /* Create D-H parameters, or read them from the cache file. This function does |
482 | its own SMTP error messaging. */ | |
059ec3d9 | 483 | |
575643cd | 484 | rc = init_dh(host); |
059ec3d9 PH |
485 | if (rc != OK) return rc; |
486 | ||
487 | /* Create the credentials structure */ | |
488 | ||
489 | rc = gnutls_certificate_allocate_credentials(&x509_cred); | |
7199e1ee TF |
490 | if (rc < 0) |
491 | return tls_error(US"certificate_allocate_credentials", | |
492 | host, gnutls_strerror(rc)); | |
059ec3d9 PH |
493 | |
494 | /* This stuff must be done for each session, because different certificates | |
495 | may be required for different sessions. */ | |
496 | ||
497 | if (!expand_check(certificate, US"tls_certificate", &cert_expanded)) | |
498 | return DEFER; | |
499 | ||
c91535f3 | 500 | key_expanded = NULL; |
059ec3d9 PH |
501 | if (privatekey != NULL) |
502 | { | |
503 | if (!expand_check(privatekey, US"tls_privatekey", &key_expanded)) | |
504 | return DEFER; | |
505 | } | |
c91535f3 PH |
506 | |
507 | /* If expansion was forced to fail, key_expanded will be NULL. If the result of | |
508 | the expansion is an empty string, ignore it also, and assume that the private | |
509 | key is in the same file as the certificate. */ | |
510 | ||
511 | if (key_expanded == NULL || *key_expanded == 0) | |
512 | key_expanded = cert_expanded; | |
059ec3d9 PH |
513 | |
514 | /* Set the certificate and private keys */ | |
515 | ||
516 | if (cert_expanded != NULL) | |
517 | { | |
518 | DEBUG(D_tls) debug_printf("certificate file = %s\nkey file = %s\n", | |
519 | cert_expanded, key_expanded); | |
520 | rc = gnutls_certificate_set_x509_key_file(x509_cred, CS cert_expanded, | |
521 | CS key_expanded, GNUTLS_X509_FMT_PEM); | |
8e669ac1 | 522 | if (rc < 0) |
de365ded PH |
523 | { |
524 | uschar *msg = string_sprintf("cert/key setup: cert=%s key=%s", | |
8e669ac1 | 525 | cert_expanded, key_expanded); |
7199e1ee | 526 | return tls_error(msg, host, gnutls_strerror(rc)); |
8e669ac1 | 527 | } |
059ec3d9 PH |
528 | } |
529 | ||
530 | /* A certificate is mandatory in a server, but not in a client */ | |
531 | ||
532 | else | |
533 | { | |
534 | if (host == NULL) | |
7199e1ee | 535 | return tls_error(US"no TLS server certificate is specified", NULL, NULL); |
059ec3d9 PH |
536 | DEBUG(D_tls) debug_printf("no TLS client certificate is specified\n"); |
537 | } | |
538 | ||
539 | /* Set the trusted CAs file if one is provided, and then add the CRL if one is | |
540 | provided. Experiment shows that, if the certificate file is empty, an unhelpful | |
541 | error message is provided. However, if we just refrain from setting anything up | |
542 | in that case, certificate verification fails, which seems to be the correct | |
543 | behaviour. */ | |
544 | ||
545 | if (cas != NULL) | |
546 | { | |
547 | struct stat statbuf; | |
548 | ||
549 | if (!expand_check(cas, US"tls_verify_certificates", &cas_expanded)) | |
550 | return DEFER; | |
551 | ||
552 | if (stat(CS cas_expanded, &statbuf) < 0) | |
553 | { | |
554 | log_write(0, LOG_MAIN|LOG_PANIC, "could not stat %s " | |
555 | "(tls_verify_certificates): %s", cas_expanded, strerror(errno)); | |
556 | return DEFER; | |
557 | } | |
558 | ||
b1c749bb PH |
559 | DEBUG(D_tls) debug_printf("verify certificates = %s size=" OFF_T_FMT "\n", |
560 | cas_expanded, statbuf.st_size); | |
059ec3d9 PH |
561 | |
562 | /* If the cert file is empty, there's no point in loading the CRL file. */ | |
563 | ||
564 | if (statbuf.st_size > 0) | |
565 | { | |
566 | rc = gnutls_certificate_set_x509_trust_file(x509_cred, CS cas_expanded, | |
567 | GNUTLS_X509_FMT_PEM); | |
7199e1ee | 568 | if (rc < 0) return tls_error(US"setup_certs", host, gnutls_strerror(rc)); |
059ec3d9 PH |
569 | |
570 | if (crl != NULL && *crl != 0) | |
571 | { | |
572 | if (!expand_check(crl, US"tls_crl", &crl_expanded)) | |
573 | return DEFER; | |
574 | DEBUG(D_tls) debug_printf("loading CRL file = %s\n", crl_expanded); | |
575 | rc = gnutls_certificate_set_x509_crl_file(x509_cred, CS crl_expanded, | |
576 | GNUTLS_X509_FMT_PEM); | |
7199e1ee | 577 | if (rc < 0) return tls_error(US"CRL setup", host, gnutls_strerror(rc)); |
059ec3d9 PH |
578 | } |
579 | } | |
580 | } | |
581 | ||
582 | /* Associate the parameters with the x509 credentials structure. */ | |
583 | ||
584 | gnutls_certificate_set_dh_params(x509_cred, dh_params); | |
059ec3d9 PH |
585 | |
586 | DEBUG(D_tls) debug_printf("initialized certificate stuff\n"); | |
587 | return OK; | |
588 | } | |
589 | ||
590 | ||
591 | ||
592 | ||
593 | /************************************************* | |
83da1223 | 594 | * Remove from a priority list * |
059ec3d9 PH |
595 | *************************************************/ |
596 | ||
597 | /* Cautiously written so that it will remove duplicates if present. | |
598 | ||
599 | Arguments: | |
600 | list a zero-terminated list | |
601 | remove_list a zero-terminated list to be removed | |
602 | ||
603 | Returns: nothing | |
604 | */ | |
605 | ||
606 | static void | |
83da1223 | 607 | remove_priority(int *list, int *remove_list) |
059ec3d9 PH |
608 | { |
609 | for (; *remove_list != 0; remove_list++) | |
610 | { | |
611 | int *p = list; | |
612 | while (*p != 0) | |
613 | { | |
614 | if (*p == *remove_list) | |
615 | { | |
616 | int *pp = p; | |
617 | do { pp[0] = pp[1]; pp++; } while (*pp != 0); | |
618 | } | |
619 | else p++; | |
620 | } | |
621 | } | |
622 | } | |
623 | ||
624 | ||
625 | ||
626 | /************************************************* | |
83da1223 | 627 | * Add to a priority list * |
059ec3d9 PH |
628 | *************************************************/ |
629 | ||
630 | /* Cautiously written to check the list size | |
631 | ||
632 | Arguments: | |
633 | list a zero-terminated list | |
634 | list_max maximum offset in the list | |
635 | add_list a zero-terminated list to be added | |
636 | ||
637 | Returns: TRUE if OK; FALSE if list overflows | |
638 | */ | |
639 | ||
640 | static BOOL | |
83da1223 | 641 | add_priority(int *list, int list_max, int *add_list) |
059ec3d9 PH |
642 | { |
643 | int next = 0; | |
644 | while (list[next] != 0) next++; | |
645 | while (*add_list != 0) | |
646 | { | |
647 | if (next >= list_max) return FALSE; | |
648 | list[next++] = *add_list++; | |
649 | } | |
650 | list[next] = 0; | |
651 | return TRUE; | |
652 | } | |
653 | ||
654 | ||
655 | ||
83da1223 PH |
656 | /************************************************* |
657 | * Adjust a priority list * | |
658 | *************************************************/ | |
659 | ||
660 | /* This function is called to adjust the lists of cipher algorithms, MAC | |
661 | algorithms, key-exchange methods, and protocols. | |
662 | ||
663 | Arguments: | |
664 | plist the appropriate priority list | |
665 | psize the length of the list | |
666 | s the configuation string | |
667 | index the index of recognized strings | |
668 | isize the length of the index | |
669 | ||
670 | ||
671 | which text for an error message | |
672 | ||
673 | Returns: FALSE if the table overflows, else TRUE | |
674 | */ | |
675 | ||
676 | static BOOL | |
677 | set_priority(int *plist, int psize, uschar *s, pri_item *index, int isize, | |
678 | uschar *which) | |
679 | { | |
680 | int sep = 0; | |
681 | BOOL first = TRUE; | |
682 | uschar *t; | |
683 | ||
684 | while ((t = string_nextinlist(&s, &sep, big_buffer, big_buffer_size)) != NULL) | |
685 | { | |
686 | int i; | |
687 | BOOL exclude = t[0] == '!'; | |
688 | if (first && !exclude) plist[0] = 0; | |
689 | first = FALSE; | |
690 | for (i = 0; i < isize; i++) | |
691 | { | |
692 | uschar *ss = strstric(t, index[i].name, FALSE); | |
693 | if (ss != NULL) | |
694 | { | |
695 | uschar *endss = ss + Ustrlen(index[i].name); | |
696 | if ((ss == t || !isalnum(ss[-1])) && !isalnum(*endss)) | |
697 | { | |
698 | if (exclude) | |
699 | remove_priority(plist, index[i].values); | |
700 | else | |
701 | { | |
702 | if (!add_priority(plist, psize, index[i].values)) | |
703 | { | |
704 | log_write(0, LOG_MAIN|LOG_PANIC, "GnuTLS init failed: %s " | |
705 | "priority table overflow", which); | |
706 | return FALSE; | |
707 | } | |
708 | } | |
709 | } | |
710 | } | |
711 | } | |
712 | } | |
713 | ||
714 | DEBUG(D_tls) | |
715 | { | |
716 | int *ptr = plist; | |
717 | debug_printf("adjusted %s priorities:", which); | |
718 | while (*ptr != 0) debug_printf(" %d", *ptr++); | |
719 | debug_printf("\n"); | |
720 | } | |
721 | ||
722 | return TRUE; | |
723 | } | |
724 | ||
725 | ||
726 | ||
727 | ||
059ec3d9 PH |
728 | /************************************************* |
729 | * Initialize a single GNUTLS session * | |
730 | *************************************************/ | |
731 | ||
732 | /* Set the algorithm, the db backend, whether to request certificates etc. | |
733 | ||
734 | TLS in Exim was first implemented using OpenSSL. This has a function to which | |
735 | you pass a list of cipher suites that are permitted/not permitted. GnuTLS works | |
736 | differently. It operates using priority lists for the different components of | |
737 | cipher suites. | |
738 | ||
739 | For compatibility of configuration, we scan a list of cipher suites and set | |
740 | priorities therefrom. However, at the moment, we pay attention only to the bulk | |
741 | cipher. | |
742 | ||
743 | Arguments: | |
744 | side one of GNUTLS_SERVER, GNUTLS_CLIENT | |
83da1223 PH |
745 | expciphers expanded ciphers list or NULL |
746 | expmac expanded MAC list or NULL | |
747 | expkx expanded key-exchange list or NULL | |
748 | expproto expanded protocol list or NULL | |
059ec3d9 PH |
749 | |
750 | Returns: a gnutls_session, or NULL if there is a problem | |
751 | */ | |
752 | ||
753 | static gnutls_session | |
83da1223 PH |
754 | tls_session_init(int side, uschar *expciphers, uschar *expmac, uschar *expkx, |
755 | uschar *expproto) | |
059ec3d9 PH |
756 | { |
757 | gnutls_session session; | |
758 | ||
759 | gnutls_init(&session, side); | |
760 | ||
83da1223 PH |
761 | /* Initialize the lists of permitted protocols, key-exchange methods, ciphers, |
762 | and MACs. */ | |
059ec3d9 PH |
763 | |
764 | memcpy(cipher_priority, default_cipher_priority, sizeof(cipher_priority)); | |
83da1223 PH |
765 | memcpy(mac_priority, default_mac_priority, sizeof(mac_priority)); |
766 | memcpy(kx_priority, default_kx_priority, sizeof(kx_priority)); | |
767 | memcpy(proto_priority, default_proto_priority, sizeof(proto_priority)); | |
768 | ||
769 | /* The names OpenSSL uses in tls_require_ciphers are of the form DES-CBC3-SHA, | |
770 | using hyphen separators. GnuTLS uses underscore separators. So that I can use | |
771 | either form for tls_require_ciphers in my tests, and also for general | |
772 | convenience, we turn hyphens into underscores before scanning the list. */ | |
059ec3d9 PH |
773 | |
774 | if (expciphers != NULL) | |
775 | { | |
059ec3d9 PH |
776 | uschar *s = expciphers; |
777 | while (*s != 0) { if (*s == '-') *s = '_'; s++; } | |
83da1223 | 778 | } |
059ec3d9 | 779 | |
83da1223 PH |
780 | if ((expciphers != NULL && |
781 | !set_priority(cipher_priority, sizeof(cipher_priority)/sizeof(int), | |
782 | expciphers, cipher_index, sizeof(cipher_index)/sizeof(pri_item), | |
783 | US"cipher")) || | |
784 | (expmac != NULL && | |
785 | !set_priority(mac_priority, sizeof(mac_priority)/sizeof(int), | |
786 | expmac, mac_index, sizeof(mac_index)/sizeof(pri_item), | |
787 | US"MAC")) || | |
788 | (expkx != NULL && | |
789 | !set_priority(kx_priority, sizeof(kx_priority)/sizeof(int), | |
790 | expkx, kx_index, sizeof(kx_index)/sizeof(pri_item), | |
791 | US"key-exchange")) || | |
792 | (expproto != NULL && | |
793 | !set_priority(proto_priority, sizeof(proto_priority)/sizeof(int), | |
794 | expproto, proto_index, sizeof(proto_index)/sizeof(pri_item), | |
795 | US"protocol"))) | |
796 | { | |
797 | gnutls_deinit(session); | |
798 | return NULL; | |
059ec3d9 PH |
799 | } |
800 | ||
801 | /* Define the various priorities */ | |
802 | ||
803 | gnutls_cipher_set_priority(session, cipher_priority); | |
804 | gnutls_compression_set_priority(session, comp_priority); | |
805 | gnutls_kx_set_priority(session, kx_priority); | |
83da1223 | 806 | gnutls_protocol_set_priority(session, proto_priority); |
059ec3d9 PH |
807 | gnutls_mac_set_priority(session, mac_priority); |
808 | ||
809 | gnutls_cred_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred); | |
810 | ||
811 | gnutls_dh_set_prime_bits(session, DH_BITS); | |
812 | ||
813 | /* Request or demand a certificate of the peer, as configured. This will | |
814 | happen only in a server. */ | |
815 | ||
816 | if (verify_requirement != VERIFY_NONE) | |
817 | gnutls_certificate_server_set_request(session, | |
818 | (verify_requirement == VERIFY_OPTIONAL)? | |
819 | GNUTLS_CERT_REQUEST : GNUTLS_CERT_REQUIRE); | |
820 | ||
821 | gnutls_db_set_cache_expiration(session, ssl_session_timeout); | |
822 | ||
e6060e2c NM |
823 | /* Reduce security in favour of increased compatibility, if the admin |
824 | decides to make that trade-off. */ | |
825 | if (gnutls_compat_mode) | |
826 | { | |
827 | #if LIBGNUTLS_VERSION_NUMBER >= 0x020104 | |
828 | DEBUG(D_tls) debug_printf("lowering GnuTLS security, compatibility mode\n"); | |
829 | gnutls_session_enable_compatibility_mode(session); | |
830 | #else | |
831 | DEBUG(D_tls) debug_printf("Unable to set gnutls_compat_mode - GnuTLS version too old\n"); | |
832 | #endif | |
833 | } | |
834 | ||
059ec3d9 PH |
835 | DEBUG(D_tls) debug_printf("initialized GnuTLS session\n"); |
836 | return session; | |
837 | } | |
838 | ||
839 | ||
840 | ||
841 | /************************************************* | |
842 | * Get name of cipher in use * | |
843 | *************************************************/ | |
844 | ||
845 | /* The answer is left in a static buffer, and tls_cipher is set to point | |
846 | to it. | |
847 | ||
848 | Argument: pointer to a GnuTLS session | |
849 | Returns: nothing | |
850 | */ | |
851 | ||
852 | static void | |
853 | construct_cipher_name(gnutls_session session) | |
854 | { | |
855 | static uschar cipherbuf[256]; | |
856 | uschar *ver; | |
edc33b5f | 857 | int c, kx, mac; |
44bbabb5 | 858 | #ifdef GNUTLS_CB_TLS_UNIQUE |
f1e05cc7 | 859 | int rc; |
44bbabb5 PP |
860 | gnutls_datum_t channel; |
861 | #endif | |
059ec3d9 PH |
862 | |
863 | ver = string_copy( | |
864 | US gnutls_protocol_get_name(gnutls_protocol_get_version(session))); | |
865 | if (Ustrncmp(ver, "TLS ", 4) == 0) ver[3] = '-'; /* Don't want space */ | |
866 | ||
867 | c = gnutls_cipher_get(session); | |
edc33b5f PP |
868 | /* returns size in "bytes" */ |
869 | tls_bits = gnutls_cipher_get_key_size(c) * 8; | |
059ec3d9 PH |
870 | |
871 | mac = gnutls_mac_get(session); | |
872 | kx = gnutls_kx_get(session); | |
873 | ||
874 | string_format(cipherbuf, sizeof(cipherbuf), "%s:%s:%u", ver, | |
edc33b5f | 875 | gnutls_cipher_suite_get_name(kx, c, mac), tls_bits); |
059ec3d9 PH |
876 | tls_cipher = cipherbuf; |
877 | ||
878 | DEBUG(D_tls) debug_printf("cipher: %s\n", cipherbuf); | |
44bbabb5 PP |
879 | |
880 | if (tls_channelbinding_b64) | |
881 | free(tls_channelbinding_b64); | |
882 | tls_channelbinding_b64 = NULL; | |
883 | ||
884 | #ifdef GNUTLS_CB_TLS_UNIQUE | |
885 | channel = { NULL, 0 }; | |
886 | rc = gnutls_session_channel_binding(session, GNUTLS_CB_TLS_UNIQUE, &channel); | |
887 | if (rc) { | |
888 | DEBUG(D_tls) debug_printf("Channel binding error: %s\n", gnutls_strerror(rc)); | |
889 | } else { | |
890 | tls_channelbinding_b64 = auth_b64encode(channel.data, (int)channel.size); | |
891 | DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage.\n"); | |
892 | } | |
893 | #endif | |
059ec3d9 PH |
894 | } |
895 | ||
896 | ||
897 | ||
898 | /************************************************* | |
899 | * Start a TLS session in a server * | |
900 | *************************************************/ | |
901 | ||
902 | /* This is called when Exim is running as a server, after having received | |
903 | the STARTTLS command. It must respond to that command, and then negotiate | |
904 | a TLS session. | |
905 | ||
906 | Arguments: | |
83da1223 PH |
907 | require_ciphers list of allowed ciphers or NULL |
908 | require_mac list of allowed MACs or NULL | |
909 | require_kx list of allowed key_exchange methods or NULL | |
910 | require_proto list of allowed protocols or NULL | |
059ec3d9 PH |
911 | |
912 | Returns: OK on success | |
913 | DEFER for errors before the start of the negotiation | |
914 | FAIL for errors during the negotation; the server can't | |
915 | continue running. | |
916 | */ | |
917 | ||
918 | int | |
83da1223 PH |
919 | tls_server_start(uschar *require_ciphers, uschar *require_mac, |
920 | uschar *require_kx, uschar *require_proto) | |
059ec3d9 PH |
921 | { |
922 | int rc; | |
7199e1ee | 923 | const char *error; |
059ec3d9 | 924 | uschar *expciphers = NULL; |
83da1223 PH |
925 | uschar *expmac = NULL; |
926 | uschar *expkx = NULL; | |
927 | uschar *expproto = NULL; | |
059ec3d9 PH |
928 | |
929 | /* Check for previous activation */ | |
930 | ||
931 | if (tls_active >= 0) | |
932 | { | |
7199e1ee | 933 | tls_error("STARTTLS received after TLS started", NULL, ""); |
059ec3d9 PH |
934 | smtp_printf("554 Already in TLS\r\n"); |
935 | return FAIL; | |
936 | } | |
937 | ||
938 | /* Initialize the library. If it fails, it will already have logged the error | |
939 | and sent an SMTP response. */ | |
940 | ||
941 | DEBUG(D_tls) debug_printf("initializing GnuTLS as a server\n"); | |
942 | ||
943 | rc = tls_init(NULL, tls_certificate, tls_privatekey, tls_verify_certificates, | |
944 | tls_crl); | |
945 | if (rc != OK) return rc; | |
946 | ||
83da1223 PH |
947 | if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers) || |
948 | !expand_check(require_mac, US"gnutls_require_mac", &expmac) || | |
949 | !expand_check(require_kx, US"gnutls_require_kx", &expkx) || | |
950 | !expand_check(require_proto, US"gnutls_require_proto", &expproto)) | |
059ec3d9 PH |
951 | return FAIL; |
952 | ||
953 | /* If this is a host for which certificate verification is mandatory or | |
954 | optional, set up appropriately. */ | |
955 | ||
956 | tls_certificate_verified = FALSE; | |
957 | verify_requirement = VERIFY_NONE; | |
958 | ||
959 | if (verify_check_host(&tls_verify_hosts) == OK) | |
960 | verify_requirement = VERIFY_REQUIRED; | |
961 | else if (verify_check_host(&tls_try_verify_hosts) == OK) | |
962 | verify_requirement = VERIFY_OPTIONAL; | |
963 | ||
964 | /* Prepare for new connection */ | |
965 | ||
83da1223 PH |
966 | tls_session = tls_session_init(GNUTLS_SERVER, expciphers, expmac, expkx, |
967 | expproto); | |
059ec3d9 | 968 | if (tls_session == NULL) |
7199e1ee TF |
969 | return tls_error(US"tls_session_init", NULL, |
970 | gnutls_strerror(GNUTLS_E_MEMORY_ERROR)); | |
059ec3d9 PH |
971 | |
972 | /* Set context and tell client to go ahead, except in the case of TLS startup | |
973 | on connection, where outputting anything now upsets the clients and tends to | |
974 | make them disconnect. We need to have an explicit fflush() here, to force out | |
975 | the response. Other smtp_printf() calls do not need it, because in non-TLS | |
976 | mode, the fflush() happens when smtp_getc() is called. */ | |
977 | ||
978 | if (!tls_on_connect) | |
979 | { | |
980 | smtp_printf("220 TLS go ahead\r\n"); | |
981 | fflush(smtp_out); | |
982 | } | |
983 | ||
984 | /* Now negotiate the TLS session. We put our own timer on it, since it seems | |
985 | that the GnuTLS library doesn't. */ | |
986 | ||
56f5d9bd PH |
987 | gnutls_transport_set_ptr2(tls_session, (gnutls_transport_ptr)fileno(smtp_in), |
988 | (gnutls_transport_ptr)fileno(smtp_out)); | |
059ec3d9 PH |
989 | |
990 | sigalrm_seen = FALSE; | |
991 | if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); | |
992 | rc = gnutls_handshake(tls_session); | |
993 | alarm(0); | |
994 | ||
995 | if (rc < 0) | |
996 | { | |
7199e1ee TF |
997 | tls_error(US"gnutls_handshake", NULL, |
998 | sigalrm_seen ? "timed out" : gnutls_strerror(rc)); | |
059ec3d9 PH |
999 | |
1000 | /* It seems that, except in the case of a timeout, we have to close the | |
1001 | connection right here; otherwise if the other end is running OpenSSL it hangs | |
1002 | until the server times out. */ | |
1003 | ||
1004 | if (!sigalrm_seen) | |
1005 | { | |
f1e894f3 PH |
1006 | (void)fclose(smtp_out); |
1007 | (void)fclose(smtp_in); | |
059ec3d9 PH |
1008 | } |
1009 | ||
1010 | return FAIL; | |
1011 | } | |
1012 | ||
1013 | DEBUG(D_tls) debug_printf("gnutls_handshake was successful\n"); | |
1014 | ||
1015 | if (verify_requirement != VERIFY_NONE && | |
1016 | !verify_certificate(tls_session, &error)) | |
1017 | { | |
7199e1ee | 1018 | tls_error(US"certificate verification failed", NULL, error); |
059ec3d9 PH |
1019 | return FAIL; |
1020 | } | |
1021 | ||
1022 | construct_cipher_name(tls_session); | |
1023 | ||
1024 | /* TLS has been set up. Adjust the input functions to read via TLS, | |
1025 | and initialize appropriately. */ | |
1026 | ||
1027 | ssl_xfer_buffer = store_malloc(ssl_xfer_buffer_size); | |
1028 | ssl_xfer_buffer_lwm = ssl_xfer_buffer_hwm = 0; | |
1029 | ssl_xfer_eof = ssl_xfer_error = 0; | |
1030 | ||
1031 | receive_getc = tls_getc; | |
1032 | receive_ungetc = tls_ungetc; | |
1033 | receive_feof = tls_feof; | |
1034 | receive_ferror = tls_ferror; | |
58eb016e | 1035 | receive_smtp_buffered = tls_smtp_buffered; |
059ec3d9 PH |
1036 | |
1037 | tls_active = fileno(smtp_out); | |
1038 | ||
1039 | return OK; | |
1040 | } | |
1041 | ||
1042 | ||
1043 | ||
1044 | ||
1045 | /************************************************* | |
1046 | * Start a TLS session in a client * | |
1047 | *************************************************/ | |
1048 | ||
1049 | /* Called from the smtp transport after STARTTLS has been accepted. | |
1050 | ||
1051 | Arguments: | |
1052 | fd the fd of the connection | |
1053 | host connected host (for messages) | |
83da1223 | 1054 | addr the first address (not used) |
059ec3d9 PH |
1055 | dhparam DH parameter file |
1056 | certificate certificate file | |
1057 | privatekey private key file | |
1058 | verify_certs file for certificate verify | |
1059 | verify_crl CRL for verify | |
83da1223 PH |
1060 | require_ciphers list of allowed ciphers or NULL |
1061 | require_mac list of allowed MACs or NULL | |
1062 | require_kx list of allowed key_exchange methods or NULL | |
1063 | require_proto list of allowed protocols or NULL | |
059ec3d9 PH |
1064 | timeout startup timeout |
1065 | ||
1066 | Returns: OK/DEFER/FAIL (because using common functions), | |
1067 | but for a client, DEFER and FAIL have the same meaning | |
1068 | */ | |
1069 | ||
1070 | int | |
1071 | tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam, | |
1072 | uschar *certificate, uschar *privatekey, uschar *verify_certs, | |
83da1223 PH |
1073 | uschar *verify_crl, uschar *require_ciphers, uschar *require_mac, |
1074 | uschar *require_kx, uschar *require_proto, int timeout) | |
059ec3d9 PH |
1075 | { |
1076 | const gnutls_datum *server_certs; | |
1077 | uschar *expciphers = NULL; | |
83da1223 PH |
1078 | uschar *expmac = NULL; |
1079 | uschar *expkx = NULL; | |
1080 | uschar *expproto = NULL; | |
7199e1ee | 1081 | const char *error; |
059ec3d9 PH |
1082 | unsigned int server_certs_size; |
1083 | int rc; | |
1084 | ||
1085 | DEBUG(D_tls) debug_printf("initializing GnuTLS as a client\n"); | |
1086 | ||
059ec3d9 PH |
1087 | verify_requirement = (verify_certs == NULL)? VERIFY_NONE : VERIFY_REQUIRED; |
1088 | rc = tls_init(host, certificate, privatekey, verify_certs, verify_crl); | |
1089 | if (rc != OK) return rc; | |
1090 | ||
83da1223 PH |
1091 | if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers) || |
1092 | !expand_check(require_mac, US"gnutls_require_mac", &expmac) || | |
1093 | !expand_check(require_kx, US"gnutls_require_kx", &expkx) || | |
1094 | !expand_check(require_proto, US"gnutls_require_proto", &expproto)) | |
059ec3d9 PH |
1095 | return FAIL; |
1096 | ||
83da1223 PH |
1097 | tls_session = tls_session_init(GNUTLS_CLIENT, expciphers, expmac, expkx, |
1098 | expproto); | |
1099 | ||
059ec3d9 | 1100 | if (tls_session == NULL) |
7199e1ee TF |
1101 | return tls_error(US "tls_session_init", host, |
1102 | gnutls_strerror(GNUTLS_E_MEMORY_ERROR)); | |
059ec3d9 PH |
1103 | |
1104 | gnutls_transport_set_ptr(tls_session, (gnutls_transport_ptr)fd); | |
1105 | ||
1106 | /* There doesn't seem to be a built-in timeout on connection. */ | |
1107 | ||
1108 | sigalrm_seen = FALSE; | |
1109 | alarm(timeout); | |
1110 | rc = gnutls_handshake(tls_session); | |
1111 | alarm(0); | |
1112 | ||
1113 | if (rc < 0) | |
7199e1ee TF |
1114 | return tls_error(US "gnutls_handshake", host, |
1115 | sigalrm_seen ? "timed out" : gnutls_strerror(rc)); | |
059ec3d9 PH |
1116 | |
1117 | server_certs = gnutls_certificate_get_peers(tls_session, &server_certs_size); | |
1118 | ||
1119 | if (server_certs != NULL) | |
1120 | { | |
1121 | uschar buff[1024]; | |
1122 | gnutls_x509_crt gcert; | |
1123 | ||
1124 | gnutls_x509_crt_init(&gcert); | |
1125 | tls_peerdn = US"unknown"; | |
1126 | ||
1127 | if (gnutls_x509_crt_import(gcert, server_certs, GNUTLS_X509_FMT_DER) == 0) | |
1128 | { | |
1129 | size_t bufsize = sizeof(buff); | |
1130 | if (gnutls_x509_crt_get_dn(gcert, CS buff, &bufsize) >= 0) | |
1131 | tls_peerdn = string_copy_malloc(buff); | |
1132 | } | |
1133 | } | |
1134 | ||
1135 | /* Should we also verify the hostname here? */ | |
1136 | ||
1137 | if (verify_requirement != VERIFY_NONE && | |
1138 | !verify_certificate(tls_session, &error)) | |
7199e1ee | 1139 | return tls_error(US"certificate verification failed", host, error); |
059ec3d9 PH |
1140 | |
1141 | construct_cipher_name(tls_session); /* Sets tls_cipher */ | |
1142 | tls_active = fd; | |
1143 | return OK; | |
1144 | } | |
1145 | ||
1146 | ||
1147 | ||
1148 | /************************************************* | |
1149 | * Deal with logging errors during I/O * | |
1150 | *************************************************/ | |
1151 | ||
1152 | /* We have to get the identity of the peer from saved data. | |
1153 | ||
1154 | Argument: | |
1155 | ec the GnuTLS error code, or 0 if it's a local error | |
1156 | when text identifying read or write | |
1157 | text local error text when ec is 0 | |
1158 | ||
1159 | Returns: nothing | |
1160 | */ | |
1161 | ||
1162 | static void | |
1163 | record_io_error(int ec, uschar *when, uschar *text) | |
1164 | { | |
7199e1ee | 1165 | const char *msg; |
059ec3d9 PH |
1166 | |
1167 | if (ec == GNUTLS_E_FATAL_ALERT_RECEIVED) | |
7199e1ee | 1168 | msg = string_sprintf("%s: %s", gnutls_strerror(ec), |
059ec3d9 | 1169 | gnutls_alert_get_name(gnutls_alert_get(tls_session))); |
059ec3d9 | 1170 | else |
7199e1ee TF |
1171 | msg = gnutls_strerror(ec); |
1172 | ||
1173 | tls_error(when, client_host, msg); | |
059ec3d9 PH |
1174 | } |
1175 | ||
1176 | ||
1177 | ||
1178 | /************************************************* | |
1179 | * TLS version of getc * | |
1180 | *************************************************/ | |
1181 | ||
1182 | /* This gets the next byte from the TLS input buffer. If the buffer is empty, | |
1183 | it refills the buffer via the GnuTLS reading function. | |
1184 | ||
1185 | Arguments: none | |
1186 | Returns: the next character or EOF | |
1187 | */ | |
1188 | ||
1189 | int | |
1190 | tls_getc(void) | |
1191 | { | |
1192 | if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) | |
1193 | { | |
1194 | int inbytes; | |
1195 | ||
1196 | DEBUG(D_tls) debug_printf("Calling gnutls_record_recv(%lx, %lx, %u)\n", | |
1197 | (long) tls_session, (long) ssl_xfer_buffer, ssl_xfer_buffer_size); | |
1198 | ||
1199 | if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); | |
1200 | inbytes = gnutls_record_recv(tls_session, CS ssl_xfer_buffer, | |
1201 | ssl_xfer_buffer_size); | |
1202 | alarm(0); | |
1203 | ||
1204 | /* A zero-byte return appears to mean that the TLS session has been | |
1205 | closed down, not that the socket itself has been closed down. Revert to | |
1206 | non-TLS handling. */ | |
1207 | ||
1208 | if (inbytes == 0) | |
1209 | { | |
1210 | DEBUG(D_tls) debug_printf("Got TLS_EOF\n"); | |
1211 | ||
1212 | receive_getc = smtp_getc; | |
1213 | receive_ungetc = smtp_ungetc; | |
1214 | receive_feof = smtp_feof; | |
1215 | receive_ferror = smtp_ferror; | |
58eb016e | 1216 | receive_smtp_buffered = smtp_buffered; |
059ec3d9 PH |
1217 | |
1218 | gnutls_deinit(tls_session); | |
1219 | tls_session = NULL; | |
1220 | tls_active = -1; | |
1221 | tls_cipher = NULL; | |
1222 | tls_peerdn = NULL; | |
1223 | ||
1224 | return smtp_getc(); | |
1225 | } | |
1226 | ||
1227 | /* Handle genuine errors */ | |
1228 | ||
1229 | else if (inbytes < 0) | |
1230 | { | |
1231 | record_io_error(inbytes, US"recv", NULL); | |
1232 | ssl_xfer_error = 1; | |
1233 | return EOF; | |
1234 | } | |
80a47a2c TK |
1235 | #ifndef DISABLE_DKIM |
1236 | dkim_exim_verify_feed(ssl_xfer_buffer, inbytes); | |
1237 | #endif | |
059ec3d9 PH |
1238 | ssl_xfer_buffer_hwm = inbytes; |
1239 | ssl_xfer_buffer_lwm = 0; | |
1240 | } | |
1241 | ||
1242 | ||
1243 | /* Something in the buffer; return next uschar */ | |
1244 | ||
1245 | return ssl_xfer_buffer[ssl_xfer_buffer_lwm++]; | |
1246 | } | |
1247 | ||
1248 | ||
1249 | ||
1250 | /************************************************* | |
1251 | * Read bytes from TLS channel * | |
1252 | *************************************************/ | |
1253 | ||
1254 | /* | |
1255 | Arguments: | |
1256 | buff buffer of data | |
1257 | len size of buffer | |
1258 | ||
1259 | Returns: the number of bytes read | |
1260 | -1 after a failed read | |
1261 | */ | |
1262 | ||
1263 | int | |
1264 | tls_read(uschar *buff, size_t len) | |
1265 | { | |
1266 | int inbytes; | |
1267 | ||
1268 | DEBUG(D_tls) debug_printf("Calling gnutls_record_recv(%lx, %lx, %u)\n", | |
1269 | (long) tls_session, (long) buff, len); | |
1270 | ||
1271 | inbytes = gnutls_record_recv(tls_session, CS buff, len); | |
1272 | if (inbytes > 0) return inbytes; | |
1273 | if (inbytes == 0) | |
1274 | { | |
1275 | DEBUG(D_tls) debug_printf("Got TLS_EOF\n"); | |
1276 | } | |
1277 | else record_io_error(inbytes, US"recv", NULL); | |
1278 | ||
1279 | return -1; | |
1280 | } | |
1281 | ||
1282 | ||
1283 | ||
1284 | /************************************************* | |
1285 | * Write bytes down TLS channel * | |
1286 | *************************************************/ | |
1287 | ||
1288 | /* | |
1289 | Arguments: | |
1290 | buff buffer of data | |
1291 | len number of bytes | |
1292 | ||
1293 | Returns: the number of bytes after a successful write, | |
1294 | -1 after a failed write | |
1295 | */ | |
1296 | ||
1297 | int | |
1298 | tls_write(const uschar *buff, size_t len) | |
1299 | { | |
1300 | int outbytes; | |
1301 | int left = len; | |
1302 | ||
1303 | DEBUG(D_tls) debug_printf("tls_do_write(%lx, %d)\n", (long) buff, left); | |
1304 | while (left > 0) | |
1305 | { | |
1306 | DEBUG(D_tls) debug_printf("gnutls_record_send(SSL, %lx, %d)\n", (long)buff, | |
1307 | left); | |
1308 | outbytes = gnutls_record_send(tls_session, CS buff, left); | |
1309 | ||
1310 | DEBUG(D_tls) debug_printf("outbytes=%d\n", outbytes); | |
1311 | if (outbytes < 0) | |
1312 | { | |
1313 | record_io_error(outbytes, US"send", NULL); | |
1314 | return -1; | |
1315 | } | |
1316 | if (outbytes == 0) | |
1317 | { | |
1318 | record_io_error(0, US"send", US"TLS channel closed on write"); | |
1319 | return -1; | |
1320 | } | |
1321 | ||
1322 | left -= outbytes; | |
1323 | buff += outbytes; | |
1324 | } | |
1325 | ||
1326 | return len; | |
1327 | } | |
1328 | ||
1329 | ||
1330 | ||
1331 | /************************************************* | |
1332 | * Close down a TLS session * | |
1333 | *************************************************/ | |
1334 | ||
1335 | /* This is also called from within a delivery subprocess forked from the | |
1336 | daemon, to shut down the TLS library, without actually doing a shutdown (which | |
1337 | would tamper with the TLS session in the parent process). | |
1338 | ||
1339 | Arguments: TRUE if gnutls_bye is to be called | |
1340 | Returns: nothing | |
1341 | */ | |
1342 | ||
1343 | void | |
1344 | tls_close(BOOL shutdown) | |
1345 | { | |
1346 | if (tls_active < 0) return; /* TLS was not active */ | |
1347 | ||
1348 | if (shutdown) | |
1349 | { | |
1350 | DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS\n"); | |
1351 | gnutls_bye(tls_session, GNUTLS_SHUT_WR); | |
1352 | } | |
1353 | ||
1354 | gnutls_deinit(tls_session); | |
1355 | tls_session = NULL; | |
1356 | gnutls_global_deinit(); | |
1357 | ||
1358 | tls_active = -1; | |
1359 | } | |
1360 | ||
36f12725 NM |
1361 | |
1362 | ||
1363 | ||
1364 | /************************************************* | |
1365 | * Report the library versions. * | |
1366 | *************************************************/ | |
1367 | ||
1368 | /* See a description in tls-openssl.c for an explanation of why this exists. | |
1369 | ||
1370 | Arguments: a FILE* to print the results to | |
1371 | Returns: nothing | |
1372 | */ | |
1373 | ||
1374 | void | |
1375 | tls_version_report(FILE *f) | |
1376 | { | |
754a0503 PP |
1377 | fprintf(f, "Library version: GnuTLS: Compile: %s\n" |
1378 | " Runtime: %s\n", | |
1379 | LIBGNUTLS_VERSION, | |
1380 | gnutls_check_version(NULL)); | |
36f12725 NM |
1381 | } |
1382 | ||
059ec3d9 | 1383 | /* End of tls-gnu.c */ |