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