1 /**
2 * @file
3 * Handling of GnuTLS encryption
4 *
5 * @authors
6 * Copyright (C) 2001 Marco d'Itri <md@linux.it>
7 * Copyright (C) 2001-2004 Andrew McDonald <andrew@mcdonald.org.uk>
8 *
9 * @copyright
10 * This program is free software: you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License as published by the Free Software
12 * Foundation, either version 2 of the License, or (at your option) any later
13 * version.
14 *
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 * details.
19 *
20 * You should have received a copy of the GNU General Public License along with
21 * this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 /**
25 * @page conn_gnutls GnuTLS encryption
26 *
27 * Handling of GnuTLS encryption
28 */
29
30 #include "config.h"
31 #include <gnutls/gnutls.h>
32 #include <gnutls/x509.h>
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <sys/stat.h>
37 #include "private.h"
38 #include "mutt/lib.h"
39 #include "config/lib.h"
40 #include "core/lib.h"
41 #include "lib.h"
42 #include "muttlib.h"
43 #include "options.h"
44 #include "ssl.h"
45
46 // clang-format off
47 /* certificate error bitmap values */
48 #define CERTERR_VALID 0
49 #define CERTERR_EXPIRED (1 << 0)
50 #define CERTERR_NOTYETVALID (1 << 1)
51 #define CERTERR_REVOKED (1 << 2)
52 #define CERTERR_NOTTRUSTED (1 << 3)
53 #define CERTERR_HOSTNAME (1 << 4)
54 #define CERTERR_SIGNERNOTCA (1 << 5)
55 #define CERTERR_INSECUREALG (1 << 6)
56 #define CERTERR_OTHER (1 << 7)
57 // clang-format on
58
59 const int dialog_row_len = 128;
60
61 #define CERT_SEP "-----BEGIN"
62
63 #ifndef HAVE_GNUTLS_PRIORITY_SET_DIRECT
64 /* This array needs to be large enough to hold all the possible values support
65 * by NeoMutt. The initialized values are just placeholders--the array gets
66 * overwrriten in tls_negotiate() depending on the $ssl_use_* options.
67 *
68 * Note: gnutls_protocol_set_priority() was removed in GnuTLS version
69 * 3.4 (2015-04). TLS 1.3 support wasn't added until version 3.6.5.
70 * Therefore, no attempt is made to support $ssl_use_tlsv1_3 in this code.
71 */
72 static int protocol_priority[] = { GNUTLS_TLS1_2, GNUTLS_TLS1_1, GNUTLS_TLS1,
73 GNUTLS_SSL3, 0 };
74 #endif
75
76 /**
77 * struct TlsSockData - TLS socket data - @extends Connection
78 */
79 struct TlsSockData
80 {
81 gnutls_session_t state;
82 gnutls_certificate_credentials_t xcred;
83 };
84
85 /**
86 * tls_init - Set up Gnu TLS
87 * @retval 0 Success
88 * @retval -1 Error
89 */
tls_init(void)90 static int tls_init(void)
91 {
92 static bool init_complete = false;
93 int err;
94
95 if (init_complete)
96 return 0;
97
98 err = gnutls_global_init();
99 if (err < 0)
100 {
101 mutt_error("gnutls_global_init: %s", gnutls_strerror(err));
102 return -1;
103 }
104
105 init_complete = true;
106 return 0;
107 }
108
109 /**
110 * tls_verify_peers - Wrapper for gnutls_certificate_verify_peers()
111 * @param tlsstate TLS state
112 * @param certstat Certificate state, e.g. GNUTLS_CERT_INVALID
113 * @retval 0 Success If certstat was set. note: this does not mean success
114 * @retval >0 Error
115 *
116 * Wrapper with sanity-checking.
117 *
118 * certstat is technically a bitwise-or of gnutls_certificate_status_t values.
119 */
tls_verify_peers(gnutls_session_t tlsstate,gnutls_certificate_status_t * certstat)120 static int tls_verify_peers(gnutls_session_t tlsstate, gnutls_certificate_status_t *certstat)
121 {
122 /* gnutls_certificate_verify_peers2() chains to
123 * gnutls_x509_trust_list_verify_crt2(). That function's documentation says:
124 *
125 * When a certificate chain of cert_list_size with more than one
126 * certificates is provided, the verification status will apply to
127 * the first certificate in the chain that failed
128 * verification. The verification process starts from the end of
129 * the chain(from CA to end certificate). The first certificate
130 * in the chain must be the end-certificate while the rest of the
131 * members may be sorted or not.
132 *
133 * This is why tls_check_certificate() loops from CA to host in that order,
134 * calling the menu, and recalling tls_verify_peers() for each approved
135 * cert in the chain.
136 */
137 int rc = gnutls_certificate_verify_peers2(tlsstate, certstat);
138
139 /* certstat was set */
140 if (rc == 0)
141 return 0;
142
143 if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND)
144 mutt_error(_("Unable to get certificate from peer"));
145 else
146 mutt_error(_("Certificate verification error (%s)"), gnutls_strerror(rc));
147
148 return rc;
149 }
150
151 /**
152 * tls_fingerprint - Create a fingerprint of a TLS Certificate
153 * @param algo Fingerprint algorithm, e.g. GNUTLS_MAC_SHA256
154 * @param buf Buffer for the fingerprint
155 * @param buflen Length of the buffer
156 * @param data Certificate
157 */
tls_fingerprint(gnutls_digest_algorithm_t algo,char * buf,size_t buflen,const gnutls_datum_t * data)158 static void tls_fingerprint(gnutls_digest_algorithm_t algo, char *buf,
159 size_t buflen, const gnutls_datum_t *data)
160 {
161 unsigned char md[64];
162 size_t n;
163
164 n = 64;
165
166 if (gnutls_fingerprint(algo, data, (char *) md, &n) < 0)
167 {
168 snprintf(buf, buflen, _("[unable to calculate]"));
169 }
170 else
171 {
172 for (int i = 0; i < (int) n; i++)
173 {
174 char ch[8];
175 snprintf(ch, 8, "%02X%s", md[i], ((i % 2) ? " " : ""));
176 mutt_str_cat(buf, buflen, ch);
177 }
178 buf[2 * n + n / 2 - 1] = '\0'; /* don't want trailing space */
179 }
180 }
181
182 /**
183 * tls_check_stored_hostname - Does the hostname match a stored certificate?
184 * @param cert Certificate
185 * @param hostname Hostname
186 * @retval 1 Hostname match found
187 * @retval 0 Error, or no match
188 */
tls_check_stored_hostname(const gnutls_datum_t * cert,const char * hostname)189 static int tls_check_stored_hostname(const gnutls_datum_t *cert, const char *hostname)
190 {
191 char *linestr = NULL;
192 size_t linestrsize = 0;
193
194 /* try checking against names stored in stored certs file */
195 const char *const c_certificate_file =
196 cs_subset_path(NeoMutt->sub, "certificate_file");
197 FILE *fp = mutt_file_fopen(c_certificate_file, "r");
198 if (!fp)
199 return 0;
200
201 char buf[80];
202 buf[0] = '\0';
203 tls_fingerprint(GNUTLS_DIG_MD5, buf, sizeof(buf), cert);
204 while ((linestr = mutt_file_read_line(linestr, &linestrsize, fp, NULL, MUTT_RL_NO_FLAGS)))
205 {
206 regmatch_t *match = mutt_prex_capture(PREX_GNUTLS_CERT_HOST_HASH, linestr);
207 if (match)
208 {
209 regmatch_t *mhost = &match[PREX_GNUTLS_CERT_HOST_HASH_MATCH_HOST];
210 regmatch_t *mhash = &match[PREX_GNUTLS_CERT_HOST_HASH_MATCH_HASH];
211 linestr[mutt_regmatch_end(mhost)] = '\0';
212 linestr[mutt_regmatch_end(mhash)] = '\0';
213 if ((strcmp(linestr + mutt_regmatch_start(mhost), hostname) == 0) &&
214 (strcmp(linestr + mutt_regmatch_start(mhash), buf) == 0))
215 {
216 FREE(&linestr);
217 mutt_file_fclose(&fp);
218 return 1;
219 }
220 }
221 }
222
223 mutt_file_fclose(&fp);
224
225 /* not found a matching name */
226 return 0;
227 }
228
229 /**
230 * tls_compare_certificates - Compare certificates against `$certificate_file`
231 * @param peercert Certificate
232 * @retval 1 Certificate matches file
233 * @retval 0 Error, or no match
234 */
tls_compare_certificates(const gnutls_datum_t * peercert)235 static int tls_compare_certificates(const gnutls_datum_t *peercert)
236 {
237 gnutls_datum_t cert = { 0 };
238 unsigned char *ptr = NULL;
239 gnutls_datum_t b64_data = { 0 };
240 unsigned char *b64_data_data = NULL;
241 struct stat st = { 0 };
242
243 const char *const c_certificate_file =
244 cs_subset_path(NeoMutt->sub, "certificate_file");
245 if (stat(c_certificate_file, &st) == -1)
246 return 0;
247
248 b64_data.size = st.st_size;
249 b64_data_data = mutt_mem_calloc(1, b64_data.size + 1);
250 b64_data.data = b64_data_data;
251
252 FILE *fp = mutt_file_fopen(c_certificate_file, "r");
253 if (!fp)
254 return 0;
255
256 b64_data.size = fread(b64_data.data, 1, b64_data.size, fp);
257 mutt_file_fclose(&fp);
258
259 do
260 {
261 const int ret = gnutls_pem_base64_decode_alloc(NULL, &b64_data, &cert);
262 if (ret != 0)
263 {
264 FREE(&b64_data_data);
265 return 0;
266 }
267
268 /* find start of cert, skipping junk */
269 ptr = (unsigned char *) strstr((char *) b64_data.data, CERT_SEP);
270 if (!ptr)
271 {
272 gnutls_free(cert.data);
273 FREE(&b64_data_data);
274 return 0;
275 }
276 /* find start of next cert */
277 ptr = (unsigned char *) strstr((char *) ptr + 1, CERT_SEP);
278
279 b64_data.size = b64_data.size - (ptr - b64_data.data);
280 b64_data.data = ptr;
281
282 if (cert.size == peercert->size)
283 {
284 if (memcmp(cert.data, peercert->data, cert.size) == 0)
285 {
286 /* match found */
287 gnutls_free(cert.data);
288 FREE(&b64_data_data);
289 return 1;
290 }
291 }
292
293 gnutls_free(cert.data);
294 } while (ptr);
295
296 /* no match found */
297 FREE(&b64_data_data);
298 return 0;
299 }
300
301 /**
302 * tls_check_preauth - Prepare a certificate for authentication
303 * @param[in] certdata List of GnuTLS certificates
304 * @param[in] certstat GnuTLS certificate status
305 * @param[in] hostname Hostname
306 * @param[in] chainidx Index in the certificate chain
307 * @param[out] certerr Result, e.g. #CERTERR_VALID
308 * @param[out] savedcert 1 if certificate has been saved
309 * @retval 0 Success
310 * @retval -1 Error
311 */
tls_check_preauth(const gnutls_datum_t * certdata,gnutls_certificate_status_t certstat,const char * hostname,int chainidx,int * certerr,int * savedcert)312 static int tls_check_preauth(const gnutls_datum_t *certdata,
313 gnutls_certificate_status_t certstat, const char *hostname,
314 int chainidx, int *certerr, int *savedcert)
315 {
316 gnutls_x509_crt_t cert;
317
318 *certerr = CERTERR_VALID;
319 *savedcert = 0;
320
321 if (gnutls_x509_crt_init(&cert) < 0)
322 {
323 mutt_error(_("Error initialising gnutls certificate data"));
324 return -1;
325 }
326
327 if (gnutls_x509_crt_import(cert, certdata, GNUTLS_X509_FMT_DER) < 0)
328 {
329 mutt_error(_("Error processing certificate data"));
330 gnutls_x509_crt_deinit(cert);
331 return -1;
332 }
333
334 /* Note: tls_negotiate() contains a call to
335 * gnutls_certificate_set_verify_flags() with a flag disabling
336 * GnuTLS checking of the dates. So certstat shouldn't have the
337 * GNUTLS_CERT_EXPIRED and GNUTLS_CERT_NOT_ACTIVATED bits set. */
338 const bool c_ssl_verify_dates =
339 cs_subset_bool(NeoMutt->sub, "ssl_verify_dates");
340 if (c_ssl_verify_dates != MUTT_NO)
341 {
342 if (gnutls_x509_crt_get_expiration_time(cert) < mutt_date_epoch())
343 *certerr |= CERTERR_EXPIRED;
344 if (gnutls_x509_crt_get_activation_time(cert) > mutt_date_epoch())
345 *certerr |= CERTERR_NOTYETVALID;
346 }
347
348 const bool c_ssl_verify_host =
349 cs_subset_bool(NeoMutt->sub, "ssl_verify_host");
350 if ((chainidx == 0) && (c_ssl_verify_host != MUTT_NO) &&
351 !gnutls_x509_crt_check_hostname(cert, hostname) &&
352 !tls_check_stored_hostname(certdata, hostname))
353 {
354 *certerr |= CERTERR_HOSTNAME;
355 }
356
357 if (certstat & GNUTLS_CERT_REVOKED)
358 {
359 *certerr |= CERTERR_REVOKED;
360 certstat ^= GNUTLS_CERT_REVOKED;
361 }
362
363 /* see whether certificate is in our cache (certificates file) */
364 if (tls_compare_certificates(certdata))
365 {
366 *savedcert = 1;
367
368 /* We check above for certs with bad dates or that are revoked.
369 * These must be accepted manually each time. Otherwise, we
370 * accept saved certificates as valid. */
371 if (*certerr == CERTERR_VALID)
372 {
373 gnutls_x509_crt_deinit(cert);
374 return 0;
375 }
376 }
377
378 if (certstat & GNUTLS_CERT_INVALID)
379 {
380 *certerr |= CERTERR_NOTTRUSTED;
381 certstat ^= GNUTLS_CERT_INVALID;
382 }
383
384 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)
385 {
386 /* NB: already cleared if cert in cache */
387 *certerr |= CERTERR_NOTTRUSTED;
388 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
389 }
390
391 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA)
392 {
393 /* NB: already cleared if cert in cache */
394 *certerr |= CERTERR_SIGNERNOTCA;
395 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
396 }
397
398 if (certstat & GNUTLS_CERT_INSECURE_ALGORITHM)
399 {
400 /* NB: already cleared if cert in cache */
401 *certerr |= CERTERR_INSECUREALG;
402 certstat ^= GNUTLS_CERT_INSECURE_ALGORITHM;
403 }
404
405 /* we've been zeroing the interesting bits in certstat -
406 * don't return OK if there are any unhandled bits we don't
407 * understand */
408 if (certstat != 0)
409 *certerr |= CERTERR_OTHER;
410
411 gnutls_x509_crt_deinit(cert);
412
413 if (*certerr == CERTERR_VALID)
414 return 0;
415
416 return -1;
417 }
418
419 /**
420 * add_cert - Look up certificate info and save it to a list
421 * @param title Title for this block of certificate info
422 * @param cert Certificate
423 * @param issuer If true, look up the issuer rather than owner details
424 * @param list List to save info to
425 */
add_cert(const char * title,gnutls_x509_crt_t cert,bool issuer,struct ListHead * list)426 static void add_cert(const char *title, gnutls_x509_crt_t cert, bool issuer,
427 struct ListHead *list)
428 {
429 static const char *part[] = {
430 GNUTLS_OID_X520_COMMON_NAME, // CN
431 GNUTLS_OID_PKCS9_EMAIL, // Email
432 GNUTLS_OID_X520_ORGANIZATION_NAME, // O
433 GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, // OU
434 GNUTLS_OID_X520_LOCALITY_NAME, // L
435 GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, // ST
436 GNUTLS_OID_X520_COUNTRY_NAME, // C
437 };
438
439 char buf[128];
440 int rc;
441
442 // Allocate formatted strings and let the ListHead take ownership
443 mutt_list_insert_tail(list, mutt_str_dup(title));
444
445 for (size_t i = 0; i < mutt_array_size(part); i++)
446 {
447 size_t buflen = sizeof(buf);
448 if (issuer)
449 rc = gnutls_x509_crt_get_issuer_dn_by_oid(cert, part[i], 0, 0, buf, &buflen);
450 else
451 rc = gnutls_x509_crt_get_dn_by_oid(cert, part[i], 0, 0, buf, &buflen);
452 if (rc != 0)
453 continue;
454
455 char *line = NULL;
456 mutt_str_asprintf(&line, " %s", buf);
457 mutt_list_insert_tail(list, line);
458 }
459 }
460
461 /**
462 * tls_check_one_certificate - Check a GnuTLS certificate
463 * @param certdata List of GnuTLS certificates
464 * @param certstat GnuTLS certificate status
465 * @param hostname Hostname
466 * @param idx Index into certificate list
467 * @param len Length of certificate list
468 * @retval 1 Success
469 * @retval 0 Failure
470 */
tls_check_one_certificate(const gnutls_datum_t * certdata,gnutls_certificate_status_t certstat,const char * hostname,int idx,size_t len)471 static int tls_check_one_certificate(const gnutls_datum_t *certdata,
472 gnutls_certificate_status_t certstat,
473 const char *hostname, int idx, size_t len)
474 {
475 struct ListHead list = STAILQ_HEAD_INITIALIZER(list);
476 int certerr, savedcert;
477 gnutls_x509_crt_t cert;
478 char fpbuf[128];
479 time_t t;
480 char datestr[30];
481 char title[256];
482 gnutls_datum_t pemdata = { 0 };
483
484 if (tls_check_preauth(certdata, certstat, hostname, idx, &certerr, &savedcert) == 0)
485 return 1;
486
487 /* interactive check from user */
488 if (gnutls_x509_crt_init(&cert) < 0)
489 {
490 mutt_error(_("Error initialising gnutls certificate data"));
491 return 0;
492 }
493
494 if (gnutls_x509_crt_import(cert, certdata, GNUTLS_X509_FMT_DER) < 0)
495 {
496 mutt_error(_("Error processing certificate data"));
497 gnutls_x509_crt_deinit(cert);
498 return 0;
499 }
500
501 add_cert(_("This certificate belongs to:"), cert, false, &list);
502 mutt_list_insert_tail(&list, NULL);
503 add_cert(_("This certificate was issued by:"), cert, true, &list);
504
505 mutt_list_insert_tail(&list, NULL);
506 mutt_list_insert_tail(&list, mutt_str_dup(_("This certificate is valid")));
507
508 char *line = NULL;
509 t = gnutls_x509_crt_get_activation_time(cert);
510 mutt_date_make_tls(datestr, sizeof(datestr), t);
511 mutt_str_asprintf(&line, _(" from %s"), datestr);
512 mutt_list_insert_tail(&list, line);
513
514 t = gnutls_x509_crt_get_expiration_time(cert);
515 mutt_date_make_tls(datestr, sizeof(datestr), t);
516 mutt_str_asprintf(&line, _(" to %s"), datestr);
517 mutt_list_insert_tail(&list, line);
518 mutt_list_insert_tail(&list, NULL);
519
520 fpbuf[0] = '\0';
521 tls_fingerprint(GNUTLS_DIG_SHA, fpbuf, sizeof(fpbuf), certdata);
522 mutt_str_asprintf(&line, _("SHA1 Fingerprint: %s"), fpbuf);
523 mutt_list_insert_tail(&list, line);
524 fpbuf[0] = '\0';
525 fpbuf[40] = '\0'; /* Ensure the second printed line is null terminated */
526 tls_fingerprint(GNUTLS_DIG_SHA256, fpbuf, sizeof(fpbuf), certdata);
527 fpbuf[39] = '\0'; /* Divide into two lines of output */
528 mutt_str_asprintf(&line, "%s%s", _("SHA256 Fingerprint: "), fpbuf);
529 mutt_list_insert_tail(&list, line);
530 mutt_str_asprintf(&line, "%*s%s", (int) mutt_str_len(_("SHA256 Fingerprint: ")),
531 "", fpbuf + 40);
532 mutt_list_insert_tail(&list, line);
533
534 if (certerr)
535 mutt_list_insert_tail(&list, NULL);
536
537 if (certerr & CERTERR_NOTYETVALID)
538 {
539 mutt_list_insert_tail(
540 &list, mutt_str_dup(_("WARNING: Server certificate is not yet valid")));
541 }
542 if (certerr & CERTERR_EXPIRED)
543 {
544 mutt_list_insert_tail(
545 &list, mutt_str_dup(_("WARNING: Server certificate has expired")));
546 }
547 if (certerr & CERTERR_REVOKED)
548 {
549 mutt_list_insert_tail(
550 &list, mutt_str_dup(_("WARNING: Server certificate has been revoked")));
551 }
552 if (certerr & CERTERR_HOSTNAME)
553 {
554 mutt_list_insert_tail(
555 &list,
556 mutt_str_dup(_("WARNING: Server hostname does not match certificate")));
557 }
558 if (certerr & CERTERR_SIGNERNOTCA)
559 {
560 mutt_list_insert_tail(
561 &list,
562 mutt_str_dup(_("WARNING: Signer of server certificate is not a CA")));
563 }
564 if (certerr & CERTERR_INSECUREALG)
565 {
566 mutt_list_insert_tail(
567 &list, mutt_str_dup(_("Warning: Server certificate was signed using "
568 "an insecure algorithm")));
569 }
570
571 snprintf(title, sizeof(title),
572 _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
573
574 const char *const c_certificate_file =
575 cs_subset_path(NeoMutt->sub, "certificate_file");
576 const bool allow_always =
577 (c_certificate_file && !savedcert &&
578 !(certerr & (CERTERR_EXPIRED | CERTERR_NOTYETVALID | CERTERR_REVOKED)));
579 int rc = dlg_verify_certificate(title, &list, allow_always, false);
580 if (rc == 3) // Accept always
581 {
582 bool saved = false;
583 FILE *fp = mutt_file_fopen(c_certificate_file, "a");
584 if (fp)
585 {
586 if (certerr & CERTERR_HOSTNAME) // Save hostname if necessary
587 {
588 fpbuf[0] = '\0';
589 tls_fingerprint(GNUTLS_DIG_MD5, fpbuf, sizeof(fpbuf), certdata);
590 fprintf(fp, "#H %s %s\n", hostname, fpbuf);
591 saved = true;
592 }
593 if (certerr ^ CERTERR_HOSTNAME) // Save the cert for all other errors
594 {
595 int rc2 = gnutls_pem_base64_encode_alloc("CERTIFICATE", certdata, &pemdata);
596 if (rc2 == 0)
597 {
598 if (fwrite(pemdata.data, pemdata.size, 1, fp) == 1)
599 {
600 saved = true;
601 }
602 gnutls_free(pemdata.data);
603 }
604 }
605 mutt_file_fclose(&fp);
606 }
607 if (!saved)
608 mutt_message(_("Certificate saved"));
609 else
610 mutt_error(_("Warning: Couldn't save certificate"));
611 }
612
613 mutt_list_free(&list);
614 gnutls_x509_crt_deinit(cert);
615 return (rc > 1);
616 }
617
618 /**
619 * tls_check_certificate - Check a connection's certificate
620 * @param conn Connection to a server
621 * @retval 1 Certificate is valid
622 * @retval 0 Error, or certificate is invalid
623 */
tls_check_certificate(struct Connection * conn)624 static int tls_check_certificate(struct Connection *conn)
625 {
626 struct TlsSockData *data = conn->sockdata;
627 gnutls_session_t state = data->state;
628 const gnutls_datum_t *cert_list = NULL;
629 unsigned int cert_list_size = 0;
630 gnutls_certificate_status_t certstat;
631 int certerr, savedcert, rc = 0;
632 int max_preauth_pass = -1;
633
634 /* tls_verify_peers() calls gnutls_certificate_verify_peers2(),
635 * which verifies the auth_type is GNUTLS_CRD_CERTIFICATE
636 * and that get_certificate_type() for the server is GNUTLS_CRT_X509.
637 * If it returns 0, certstat will be set with failure codes for the first
638 * cert in the chain(from CA to host) with an error.
639 */
640 if (tls_verify_peers(state, &certstat) != 0)
641 return 0;
642
643 cert_list = gnutls_certificate_get_peers(state, &cert_list_size);
644 if (!cert_list)
645 {
646 mutt_error(_("Unable to get certificate from peer"));
647 return 0;
648 }
649
650 /* tls_verify_peers doesn't check hostname or expiration, so walk
651 * from most specific to least checking these. If we see a saved certificate,
652 * its status short-circuits the remaining checks. */
653 int preauthrc = 0;
654 for (int i = 0; i < cert_list_size; i++)
655 {
656 rc = tls_check_preauth(&cert_list[i], certstat, conn->account.host, i,
657 &certerr, &savedcert);
658 preauthrc += rc;
659 if (!preauthrc)
660 max_preauth_pass = i;
661
662 if (savedcert)
663 {
664 if (preauthrc == 0)
665 return 1;
666 break;
667 }
668 }
669
670 /* then check interactively, starting from chain root */
671 for (int i = cert_list_size - 1; i >= 0; i--)
672 {
673 rc = tls_check_one_certificate(&cert_list[i], certstat, conn->account.host,
674 i, cert_list_size);
675
676 /* Stop checking if the menu cert is aborted or rejected. */
677 if (rc == 0)
678 break;
679
680 /* add signers to trust set, then reverify */
681 if (i)
682 {
683 int rcsettrust = gnutls_certificate_set_x509_trust_mem(
684 data->xcred, &cert_list[i], GNUTLS_X509_FMT_DER);
685 if (rcsettrust != 1)
686 mutt_debug(LL_DEBUG1, "error trusting certificate %d: %d\n", i, rcsettrust);
687
688 if (tls_verify_peers(state, &certstat) != 0)
689 return 0;
690
691 /* If the cert chain now verifies, and all lower certs already
692 * passed preauth, we are done. */
693 if (!certstat && (max_preauth_pass >= (i - 1)))
694 return 1;
695 }
696 }
697
698 return rc;
699 }
700
701 /**
702 * tls_get_client_cert - Get the client certificate for a TLS connection
703 * @param conn Connection to a server
704 *
705 * @note This function grabs the CN out of the client cert but appears to do
706 * nothing with it. It does contain a call to mutt_account_getuser().
707 */
tls_get_client_cert(struct Connection * conn)708 static void tls_get_client_cert(struct Connection *conn)
709 {
710 struct TlsSockData *data = conn->sockdata;
711 gnutls_x509_crt_t clientcrt;
712 char *cn = NULL;
713 size_t cnlen = 0;
714 int rc;
715
716 /* get our cert CN if we have one */
717 const gnutls_datum_t *crtdata = gnutls_certificate_get_ours(data->state);
718 if (!crtdata)
719 return;
720
721 if (gnutls_x509_crt_init(&clientcrt) < 0)
722 {
723 mutt_debug(LL_DEBUG1, "Failed to init gnutls crt\n");
724 return;
725 }
726
727 if (gnutls_x509_crt_import(clientcrt, crtdata, GNUTLS_X509_FMT_DER) < 0)
728 {
729 mutt_debug(LL_DEBUG1, "Failed to import gnutls client crt\n");
730 goto err;
731 }
732
733 /* get length of CN, then grab it. */
734 rc = gnutls_x509_crt_get_dn_by_oid(clientcrt, GNUTLS_OID_X520_COMMON_NAME, 0,
735 0, NULL, &cnlen);
736 if (((rc >= 0) || (rc == GNUTLS_E_SHORT_MEMORY_BUFFER)) && (cnlen > 0))
737 {
738 cn = mutt_mem_calloc(1, cnlen);
739 if (gnutls_x509_crt_get_dn_by_oid(clientcrt, GNUTLS_OID_X520_COMMON_NAME, 0,
740 0, cn, &cnlen) < 0)
741 {
742 goto err;
743 }
744 mutt_debug(LL_DEBUG2, "client certificate CN: %s\n", cn);
745
746 /* if we are using a client cert, SASL may expect an external auth name */
747 if (mutt_account_getuser(&conn->account) < 0)
748 mutt_debug(LL_DEBUG1, "Couldn't get user info\n");
749 }
750
751 err:
752 FREE(&cn);
753 gnutls_x509_crt_deinit(clientcrt);
754 }
755
756 #ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT
757 /**
758 * tls_set_priority - Set TLS algorithm priorities
759 * @param data TLS socket data
760 * @retval 0 Success
761 * @retval -1 Error
762 */
tls_set_priority(struct TlsSockData * data)763 static int tls_set_priority(struct TlsSockData *data)
764 {
765 size_t nproto = 5;
766 int rv = -1;
767
768 struct Buffer *priority = mutt_buffer_pool_get();
769
770 const char *const c_ssl_ciphers =
771 cs_subset_string(NeoMutt->sub, "ssl_ciphers");
772 if (c_ssl_ciphers)
773 mutt_buffer_strcpy(priority, c_ssl_ciphers);
774 else
775 mutt_buffer_strcpy(priority, "NORMAL");
776
777 const bool c_ssl_use_tlsv1_3 =
778 cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_3");
779 if (!c_ssl_use_tlsv1_3)
780 {
781 nproto--;
782 mutt_buffer_addstr(priority, ":-VERS-TLS1.3");
783 }
784 const bool c_ssl_use_tlsv1_2 =
785 cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_2");
786 if (!c_ssl_use_tlsv1_2)
787 {
788 nproto--;
789 mutt_buffer_addstr(priority, ":-VERS-TLS1.2");
790 }
791 const bool c_ssl_use_tlsv1_1 =
792 cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_1");
793 if (!c_ssl_use_tlsv1_1)
794 {
795 nproto--;
796 mutt_buffer_addstr(priority, ":-VERS-TLS1.1");
797 }
798 const bool c_ssl_use_tlsv1 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1");
799 if (!c_ssl_use_tlsv1)
800 {
801 nproto--;
802 mutt_buffer_addstr(priority, ":-VERS-TLS1.0");
803 }
804 const bool c_ssl_use_sslv3 = cs_subset_bool(NeoMutt->sub, "ssl_use_sslv3");
805 if (!c_ssl_use_sslv3)
806 {
807 nproto--;
808 mutt_buffer_addstr(priority, ":-VERS-SSL3.0");
809 }
810
811 if (nproto == 0)
812 {
813 mutt_error(_("All available protocols for TLS/SSL connection disabled"));
814 goto cleanup;
815 }
816
817 int err = gnutls_priority_set_direct(data->state, mutt_buffer_string(priority), NULL);
818 if (err < 0)
819 {
820 mutt_error("gnutls_priority_set_direct(%s): %s",
821 mutt_buffer_string(priority), gnutls_strerror(err));
822 goto cleanup;
823 }
824
825 rv = 0;
826
827 cleanup:
828 mutt_buffer_pool_release(&priority);
829 return rv;
830 }
831
832 #else
833 /**
834 * tls_set_priority - Set the priority of various protocols
835 * @param data TLS socket data
836 * @retval 0 Success
837 * @retval -1 Error
838 */
tls_set_priority(struct TlsSockData * data)839 static int tls_set_priority(struct TlsSockData *data)
840 {
841 size_t nproto = 0; /* number of tls/ssl protocols */
842
843 const bool c_ssl_use_tlsv1_2 =
844 cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_2");
845 if (c_ssl_use_tlsv1_2)
846 protocol_priority[nproto++] = GNUTLS_TLS1_2;
847 const bool c_ssl_use_tlsv1_1 =
848 cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_1");
849 if (c_ssl_use_tlsv1_1)
850 protocol_priority[nproto++] = GNUTLS_TLS1_1;
851 const bool c_ssl_use_tlsv1 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1");
852 if (c_ssl_use_tlsv1)
853 protocol_priority[nproto++] = GNUTLS_TLS1;
854 const bool c_ssl_use_sslv3 = cs_subset_bool(NeoMutt->sub, "ssl_use_sslv3");
855 if (c_ssl_use_sslv3)
856 protocol_priority[nproto++] = GNUTLS_SSL3;
857 protocol_priority[nproto] = 0;
858
859 if (nproto == 0)
860 {
861 mutt_error(_("All available protocols for TLS/SSL connection disabled"));
862 return -1;
863 }
864
865 const char *const c_ssl_ciphers =
866 cs_subset_string(NeoMutt->sub, "ssl_ciphers");
867 if (c_ssl_ciphers)
868 {
869 mutt_error(
870 _("Explicit ciphersuite selection via $ssl_ciphers not supported"));
871 }
872
873 /* We use default priorities (see gnutls documentation),
874 * except for protocol version */
875 gnutls_set_default_priority(data->state);
876 gnutls_protocol_set_priority(data->state, protocol_priority);
877 return 0;
878 }
879 #endif
880
881 /**
882 * tls_negotiate - Negotiate TLS connection
883 * @param conn Connection to a server
884 * @retval 0 Success
885 * @retval -1 Error
886 *
887 * After TLS state has been initialized, attempt to negotiate TLS over the
888 * wire, including certificate checks.
889 */
tls_negotiate(struct Connection * conn)890 static int tls_negotiate(struct Connection *conn)
891 {
892 struct TlsSockData *data = mutt_mem_calloc(1, sizeof(struct TlsSockData));
893 conn->sockdata = data;
894 int err = gnutls_certificate_allocate_credentials(&data->xcred);
895 if (err < 0)
896 {
897 FREE(&conn->sockdata);
898 mutt_error("gnutls_certificate_allocate_credentials: %s", gnutls_strerror(err));
899 return -1;
900 }
901
902 const char *const c_certificate_file =
903 cs_subset_path(NeoMutt->sub, "certificate_file");
904 gnutls_certificate_set_x509_trust_file(data->xcred, c_certificate_file, GNUTLS_X509_FMT_PEM);
905 /* ignore errors, maybe file doesn't exist yet */
906
907 const char *const c_ssl_ca_certificates_file =
908 cs_subset_path(NeoMutt->sub, "ssl_ca_certificates_file");
909 if (c_ssl_ca_certificates_file)
910 {
911 gnutls_certificate_set_x509_trust_file(data->xcred, c_ssl_ca_certificates_file,
912 GNUTLS_X509_FMT_PEM);
913 }
914
915 const char *const c_ssl_client_cert =
916 cs_subset_path(NeoMutt->sub, "ssl_client_cert");
917 if (c_ssl_client_cert)
918 {
919 mutt_debug(LL_DEBUG2, "Using client certificate %s\n", c_ssl_client_cert);
920 gnutls_certificate_set_x509_key_file(data->xcred, c_ssl_client_cert,
921 c_ssl_client_cert, GNUTLS_X509_FMT_PEM);
922 }
923
924 #ifdef HAVE_DECL_GNUTLS_VERIFY_DISABLE_TIME_CHECKS
925 /* disable checking certificate activation/expiration times
926 * in gnutls, we do the checks ourselves */
927 gnutls_certificate_set_verify_flags(data->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
928 #endif
929
930 err = gnutls_init(&data->state, GNUTLS_CLIENT);
931 if (err)
932 {
933 mutt_error("gnutls_init: %s", gnutls_strerror(err));
934 goto fail;
935 }
936
937 /* set socket */
938 gnutls_transport_set_ptr(data->state, (gnutls_transport_ptr_t) (long) conn->fd);
939
940 if (gnutls_server_name_set(data->state, GNUTLS_NAME_DNS, conn->account.host,
941 mutt_str_len(conn->account.host)))
942 {
943 mutt_error(_("Warning: unable to set TLS SNI host name"));
944 }
945
946 if (tls_set_priority(data) < 0)
947 {
948 goto fail;
949 }
950
951 const short c_ssl_min_dh_prime_bits =
952 cs_subset_number(NeoMutt->sub, "ssl_min_dh_prime_bits");
953 if (c_ssl_min_dh_prime_bits > 0)
954 {
955 gnutls_dh_set_prime_bits(data->state, c_ssl_min_dh_prime_bits);
956 }
957
958 gnutls_credentials_set(data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
959
960 do
961 {
962 err = gnutls_handshake(data->state);
963 } while ((err == GNUTLS_E_AGAIN) || (err == GNUTLS_E_INTERRUPTED));
964
965 if (err < 0)
966 {
967 if (err == GNUTLS_E_FATAL_ALERT_RECEIVED)
968 {
969 mutt_error("gnutls_handshake: %s(%s)", gnutls_strerror(err),
970 gnutls_alert_get_name(gnutls_alert_get(data->state)));
971 }
972 else
973 {
974 mutt_error("gnutls_handshake: %s", gnutls_strerror(err));
975 }
976 goto fail;
977 }
978
979 if (tls_check_certificate(conn) == 0)
980 goto fail;
981
982 /* set Security Strength Factor (SSF) for SASL */
983 /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
984 conn->ssf = gnutls_cipher_get_key_size(gnutls_cipher_get(data->state)) * 8;
985
986 tls_get_client_cert(conn);
987
988 if (!OptNoCurses)
989 {
990 mutt_message(_("SSL/TLS connection using %s (%s/%s/%s)"),
991 gnutls_protocol_get_name(gnutls_protocol_get_version(data->state)),
992 gnutls_kx_get_name(gnutls_kx_get(data->state)),
993 gnutls_cipher_get_name(gnutls_cipher_get(data->state)),
994 gnutls_mac_get_name(gnutls_mac_get(data->state)));
995 mutt_sleep(0);
996 }
997
998 return 0;
999
1000 fail:
1001 gnutls_certificate_free_credentials(data->xcred);
1002 gnutls_deinit(data->state);
1003 FREE(&conn->sockdata);
1004 return -1;
1005 }
1006
1007 /**
1008 * tls_socket_poll - Check whether a socket read would block - Implements Connection::poll() - @ingroup connection_poll
1009 */
tls_socket_poll(struct Connection * conn,time_t wait_secs)1010 static int tls_socket_poll(struct Connection *conn, time_t wait_secs)
1011 {
1012 struct TlsSockData *data = conn->sockdata;
1013 if (!data)
1014 return -1;
1015
1016 if (gnutls_record_check_pending(data->state))
1017 return 1;
1018
1019 return raw_socket_poll(conn, wait_secs);
1020 }
1021
1022 /**
1023 * tls_socket_close - Close a TLS socket - Implements Connection::close() - @ingroup connection_close
1024 */
tls_socket_close(struct Connection * conn)1025 static int tls_socket_close(struct Connection *conn)
1026 {
1027 struct TlsSockData *data = conn->sockdata;
1028 if (data)
1029 {
1030 /* shut down only the write half to avoid hanging waiting for the remote to respond.
1031 *
1032 * RFC5246 7.2.1. "Closure Alerts"
1033 *
1034 * It is not required for the initiator of the close to wait for the
1035 * responding close_notify alert before closing the read side of the
1036 * connection. */
1037 gnutls_bye(data->state, GNUTLS_SHUT_WR);
1038
1039 gnutls_certificate_free_credentials(data->xcred);
1040 gnutls_deinit(data->state);
1041 FREE(&conn->sockdata);
1042 }
1043
1044 return raw_socket_close(conn);
1045 }
1046
1047 /**
1048 * tls_socket_open - Open a TLS socket - Implements Connection::open() - @ingroup connection_open
1049 */
tls_socket_open(struct Connection * conn)1050 static int tls_socket_open(struct Connection *conn)
1051 {
1052 if (raw_socket_open(conn) < 0)
1053 return -1;
1054
1055 if (tls_negotiate(conn) < 0)
1056 {
1057 tls_socket_close(conn);
1058 return -1;
1059 }
1060
1061 return 0;
1062 }
1063
1064 /**
1065 * tls_socket_read - Read data from a TLS socket - Implements Connection::read() - @ingroup connection_read
1066 */
tls_socket_read(struct Connection * conn,char * buf,size_t count)1067 static int tls_socket_read(struct Connection *conn, char *buf, size_t count)
1068 {
1069 struct TlsSockData *data = conn->sockdata;
1070 if (!data)
1071 {
1072 mutt_error(_("Error: no TLS socket open"));
1073 return -1;
1074 }
1075
1076 int rc;
1077 do
1078 {
1079 rc = gnutls_record_recv(data->state, buf, count);
1080 } while ((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED));
1081
1082 if (rc < 0)
1083 {
1084 mutt_error("tls_socket_read (%s)", gnutls_strerror(rc));
1085 return -1;
1086 }
1087
1088 return rc;
1089 }
1090
1091 /**
1092 * tls_socket_write - Write data to a TLS socket - Implements Connection::write() - @ingroup connection_write
1093 */
tls_socket_write(struct Connection * conn,const char * buf,size_t count)1094 static int tls_socket_write(struct Connection *conn, const char *buf, size_t count)
1095 {
1096 struct TlsSockData *data = conn->sockdata;
1097 size_t sent = 0;
1098
1099 if (!data)
1100 {
1101 mutt_error(_("Error: no TLS socket open"));
1102 return -1;
1103 }
1104
1105 do
1106 {
1107 int ret;
1108 do
1109 {
1110 ret = gnutls_record_send(data->state, buf + sent, count - sent);
1111 } while ((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED));
1112
1113 if (ret < 0)
1114 {
1115 mutt_error("tls_socket_write (%s)", gnutls_strerror(ret));
1116 return -1;
1117 }
1118
1119 sent += ret;
1120 } while (sent < count);
1121
1122 return sent;
1123 }
1124
1125 /**
1126 * tls_starttls_close - Close a TLS connection - Implements Connection::close() - @ingroup connection_close
1127 */
tls_starttls_close(struct Connection * conn)1128 static int tls_starttls_close(struct Connection *conn)
1129 {
1130 int rc;
1131
1132 rc = tls_socket_close(conn);
1133 conn->read = raw_socket_read;
1134 conn->write = raw_socket_write;
1135 conn->close = raw_socket_close;
1136 conn->poll = raw_socket_poll;
1137
1138 return rc;
1139 }
1140
1141 /**
1142 * mutt_ssl_socket_setup - Set up SSL socket mulitplexor
1143 * @param conn Connection to a server
1144 * @retval 0 Success
1145 * @retval -1 Error
1146 */
mutt_ssl_socket_setup(struct Connection * conn)1147 int mutt_ssl_socket_setup(struct Connection *conn)
1148 {
1149 if (tls_init() < 0)
1150 return -1;
1151
1152 conn->open = tls_socket_open;
1153 conn->read = tls_socket_read;
1154 conn->write = tls_socket_write;
1155 conn->close = tls_socket_close;
1156 conn->poll = tls_socket_poll;
1157
1158 return 0;
1159 }
1160
1161 /**
1162 * mutt_ssl_starttls - Negotiate TLS over an already opened connection
1163 * @param conn Connection to a server
1164 * @retval 0 Success
1165 * @retval -1 Error
1166 */
mutt_ssl_starttls(struct Connection * conn)1167 int mutt_ssl_starttls(struct Connection *conn)
1168 {
1169 if (tls_init() < 0)
1170 return -1;
1171
1172 if (tls_negotiate(conn) < 0)
1173 return -1;
1174
1175 conn->read = tls_socket_read;
1176 conn->write = tls_socket_write;
1177 conn->close = tls_starttls_close;
1178 conn->poll = tls_socket_poll;
1179
1180 return 0;
1181 }
1182