NeoMutt  2022-04-29-215-gc12b98
Teaching an old dog new tricks
DOXYGEN
gnutls.c
Go to the documentation of this file.
1
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 <time.h>
38#include "private.h"
39#include "mutt/lib.h"
40#include "config/lib.h"
41#include "core/lib.h"
42#include "lib.h"
43#include "muttlib.h"
44#include "options.h"
45#include "ssl.h"
46
47// clang-format off
48/* certificate error bitmap values */
49#define CERTERR_VALID 0
50#define CERTERR_EXPIRED (1 << 0)
51#define CERTERR_NOTYETVALID (1 << 1)
52#define CERTERR_REVOKED (1 << 2)
53#define CERTERR_NOTTRUSTED (1 << 3)
54#define CERTERR_HOSTNAME (1 << 4)
55#define CERTERR_SIGNERNOTCA (1 << 5)
56#define CERTERR_INSECUREALG (1 << 6)
57#define CERTERR_OTHER (1 << 7)
58// clang-format on
59
60const int dialog_row_len = 128;
61
62#define CERT_SEP "-----BEGIN"
63
64#ifndef HAVE_GNUTLS_PRIORITY_SET_DIRECT
65/* This array needs to be large enough to hold all the possible values support
66 * by NeoMutt. The initialized values are just placeholders--the array gets
67 * overwrriten in tls_negotiate() depending on the $ssl_use_* options.
68 *
69 * Note: gnutls_protocol_set_priority() was removed in GnuTLS version
70 * 3.4 (2015-04). TLS 1.3 support wasn't added until version 3.6.5.
71 * Therefore, no attempt is made to support $ssl_use_tlsv1_3 in this code.
72 */
73static int protocol_priority[] = { GNUTLS_TLS1_2, GNUTLS_TLS1_1, GNUTLS_TLS1,
74 GNUTLS_SSL3, 0 };
75#endif
76
81{
82 gnutls_session_t state;
83 gnutls_certificate_credentials_t xcred;
84};
85
91static int tls_init(void)
92{
93 static bool init_complete = false;
94 int err;
95
96 if (init_complete)
97 return 0;
98
99 err = gnutls_global_init();
100 if (err < 0)
101 {
102 mutt_error("gnutls_global_init: %s", gnutls_strerror(err));
103 return -1;
104 }
105
106 init_complete = true;
107 return 0;
108}
109
121static int tls_verify_peers(gnutls_session_t tlsstate, gnutls_certificate_status_t *certstat)
122{
123 /* gnutls_certificate_verify_peers2() chains to
124 * gnutls_x509_trust_list_verify_crt2(). That function's documentation says:
125 *
126 * When a certificate chain of cert_list_size with more than one
127 * certificates is provided, the verification status will apply to
128 * the first certificate in the chain that failed
129 * verification. The verification process starts from the end of
130 * the chain(from CA to end certificate). The first certificate
131 * in the chain must be the end-certificate while the rest of the
132 * members may be sorted or not.
133 *
134 * This is why tls_check_certificate() loops from CA to host in that order,
135 * calling the menu, and recalling tls_verify_peers() for each approved
136 * cert in the chain.
137 */
138 int rc = gnutls_certificate_verify_peers2(tlsstate, certstat);
139
140 /* certstat was set */
141 if (rc == 0)
142 return 0;
143
144 if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND)
145 mutt_error(_("Unable to get certificate from peer"));
146 else
147 mutt_error(_("Certificate verification error (%s)"), gnutls_strerror(rc));
148
149 return rc;
150}
151
159static void tls_fingerprint(gnutls_digest_algorithm_t algo, char *buf,
160 size_t buflen, const gnutls_datum_t *data)
161{
162 unsigned char md[64];
163 size_t n;
164
165 n = 64;
166
167 if (gnutls_fingerprint(algo, data, (char *) md, &n) < 0)
168 {
169 snprintf(buf, buflen, _("[unable to calculate]"));
170 }
171 else
172 {
173 for (int i = 0; i < (int) n; i++)
174 {
175 char ch[8] = { 0 };
176 snprintf(ch, 8, "%02X%s", md[i], ((i % 2) ? " " : ""));
177 mutt_str_cat(buf, buflen, ch);
178 }
179 buf[2 * n + n / 2 - 1] = '\0'; /* don't want trailing space */
180 }
181}
182
190static bool tls_check_stored_hostname(const gnutls_datum_t *cert, const char *hostname)
191{
192 char *linestr = NULL;
193 size_t linestrsize = 0;
194
195 /* try checking against names stored in stored certs file */
196 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
197 FILE *fp = mutt_file_fopen(c_certificate_file, "r");
198 if (!fp)
199 return false;
200
201 char buf[80] = { 0 };
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 true;
219 }
220 }
221 }
222
223 mutt_file_fclose(&fp);
224
225 /* not found a matching name */
226 return false;
227}
228
235static 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 = cs_subset_path(NeoMutt->sub, "certificate_file");
244 if (stat(c_certificate_file, &st) == -1)
245 return 0;
246
247 b64_data.size = st.st_size;
248 b64_data_data = mutt_mem_calloc(1, b64_data.size + 1);
249 b64_data.data = b64_data_data;
250
251 FILE *fp = mutt_file_fopen(c_certificate_file, "r");
252 if (!fp)
253 return 0;
254
255 b64_data.size = fread(b64_data.data, 1, b64_data.size, fp);
256 b64_data.data[b64_data.size] = '\0';
257 mutt_file_fclose(&fp);
258
259 do
260 {
261 const int rc = gnutls_pem_base64_decode_alloc(NULL, &b64_data, &cert);
262 if (rc != 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
312static 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 = cs_subset_bool(NeoMutt->sub, "ssl_verify_dates");
339 if (c_ssl_verify_dates != MUTT_NO)
340 {
341 if (gnutls_x509_crt_get_expiration_time(cert) < mutt_date_epoch())
342 *certerr |= CERTERR_EXPIRED;
343 if (gnutls_x509_crt_get_activation_time(cert) > mutt_date_epoch())
344 *certerr |= CERTERR_NOTYETVALID;
345 }
346
347 const bool c_ssl_verify_host = cs_subset_bool(NeoMutt->sub, "ssl_verify_host");
348 if ((chainidx == 0) && (c_ssl_verify_host != MUTT_NO) &&
349 !gnutls_x509_crt_check_hostname(cert, hostname) &&
350 !tls_check_stored_hostname(certdata, hostname))
351 {
352 *certerr |= CERTERR_HOSTNAME;
353 }
354
355 if (certstat & GNUTLS_CERT_REVOKED)
356 {
357 *certerr |= CERTERR_REVOKED;
358 certstat ^= GNUTLS_CERT_REVOKED;
359 }
360
361 /* see whether certificate is in our cache (certificates file) */
362 if (tls_compare_certificates(certdata))
363 {
364 *savedcert = 1;
365
366 /* We check above for certs with bad dates or that are revoked.
367 * These must be accepted manually each time. Otherwise, we
368 * accept saved certificates as valid. */
369 if (*certerr == CERTERR_VALID)
370 {
371 gnutls_x509_crt_deinit(cert);
372 return 0;
373 }
374 }
375
376 if (certstat & GNUTLS_CERT_INVALID)
377 {
378 *certerr |= CERTERR_NOTTRUSTED;
379 certstat ^= GNUTLS_CERT_INVALID;
380 }
381
382 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)
383 {
384 /* NB: already cleared if cert in cache */
385 *certerr |= CERTERR_NOTTRUSTED;
386 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
387 }
388
389 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA)
390 {
391 /* NB: already cleared if cert in cache */
392 *certerr |= CERTERR_SIGNERNOTCA;
393 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
394 }
395
396 if (certstat & GNUTLS_CERT_INSECURE_ALGORITHM)
397 {
398 /* NB: already cleared if cert in cache */
399 *certerr |= CERTERR_INSECUREALG;
400 certstat ^= GNUTLS_CERT_INSECURE_ALGORITHM;
401 }
402
403 /* we've been zeroing the interesting bits in certstat -
404 * don't return OK if there are any unhandled bits we don't
405 * understand */
406 if (certstat != 0)
407 *certerr |= CERTERR_OTHER;
408
409 gnutls_x509_crt_deinit(cert);
410
411 if (*certerr == CERTERR_VALID)
412 return 0;
413
414 return -1;
415}
416
424static void add_cert(const char *title, gnutls_x509_crt_t cert, bool issuer,
425 struct CertArray *carr)
426{
427 static const char *part[] = {
428 GNUTLS_OID_X520_COMMON_NAME, // CN
429 GNUTLS_OID_PKCS9_EMAIL, // Email
430 GNUTLS_OID_X520_ORGANIZATION_NAME, // O
431 GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, // OU
432 GNUTLS_OID_X520_LOCALITY_NAME, // L
433 GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, // ST
434 GNUTLS_OID_X520_COUNTRY_NAME, // C
435 };
436
437 char buf[128] = { 0 };
438 int rc;
439
440 // Allocate formatted strings and let the array take ownership
441 ARRAY_ADD(carr, mutt_str_dup(title));
442
443 for (size_t i = 0; i < mutt_array_size(part); i++)
444 {
445 size_t buflen = sizeof(buf);
446 if (issuer)
447 rc = gnutls_x509_crt_get_issuer_dn_by_oid(cert, part[i], 0, 0, buf, &buflen);
448 else
449 rc = gnutls_x509_crt_get_dn_by_oid(cert, part[i], 0, 0, buf, &buflen);
450 if (rc != 0)
451 continue;
452
453 char *line = NULL;
454 mutt_str_asprintf(&line, " %s", buf);
455 ARRAY_ADD(carr, line);
456 }
457}
458
469static int tls_check_one_certificate(const gnutls_datum_t *certdata,
470 gnutls_certificate_status_t certstat,
471 const char *hostname, int idx, size_t len)
472{
473 struct CertArray carr = ARRAY_HEAD_INITIALIZER;
474 int certerr, savedcert;
475 gnutls_x509_crt_t cert;
476 char fpbuf[128] = { 0 };
477 time_t t;
478 char datestr[30] = { 0 };
479 char title[256] = { 0 };
480 gnutls_datum_t pemdata = { 0 };
481
482 if (tls_check_preauth(certdata, certstat, hostname, idx, &certerr, &savedcert) == 0)
483 return 1;
484
485 /* interactive check from user */
486 if (gnutls_x509_crt_init(&cert) < 0)
487 {
488 mutt_error(_("Error initialising gnutls certificate data"));
489 return 0;
490 }
491
492 if (gnutls_x509_crt_import(cert, certdata, GNUTLS_X509_FMT_DER) < 0)
493 {
494 mutt_error(_("Error processing certificate data"));
495 gnutls_x509_crt_deinit(cert);
496 return 0;
497 }
498
499 add_cert(_("This certificate belongs to:"), cert, false, &carr);
500 ARRAY_ADD(&carr, NULL);
501 add_cert(_("This certificate was issued by:"), cert, true, &carr);
502
503 ARRAY_ADD(&carr, NULL);
504 ARRAY_ADD(&carr, mutt_str_dup(_("This certificate is valid")));
505
506 char *line = NULL;
507 t = gnutls_x509_crt_get_activation_time(cert);
508 mutt_date_make_tls(datestr, sizeof(datestr), t);
509 mutt_str_asprintf(&line, _(" from %s"), datestr);
510 ARRAY_ADD(&carr, line);
511
512 t = gnutls_x509_crt_get_expiration_time(cert);
513 mutt_date_make_tls(datestr, sizeof(datestr), t);
514 mutt_str_asprintf(&line, _(" to %s"), datestr);
515 ARRAY_ADD(&carr, line);
516 ARRAY_ADD(&carr, NULL);
517
518 fpbuf[0] = '\0';
519 tls_fingerprint(GNUTLS_DIG_SHA, fpbuf, sizeof(fpbuf), certdata);
520 mutt_str_asprintf(&line, _("SHA1 Fingerprint: %s"), fpbuf);
521 ARRAY_ADD(&carr, line);
522 fpbuf[0] = '\0';
523 fpbuf[40] = '\0'; /* Ensure the second printed line is null terminated */
524 tls_fingerprint(GNUTLS_DIG_SHA256, fpbuf, sizeof(fpbuf), certdata);
525 fpbuf[39] = '\0'; /* Divide into two carr of output */
526 mutt_str_asprintf(&line, "%s%s", _("SHA256 Fingerprint: "), fpbuf);
527 ARRAY_ADD(&carr, line);
528 mutt_str_asprintf(&line, "%*s%s", (int) mutt_str_len(_("SHA256 Fingerprint: ")),
529 "", fpbuf + 40);
530 ARRAY_ADD(&carr, line);
531
532 if (certerr)
533 ARRAY_ADD(&carr, NULL);
534
535 if (certerr & CERTERR_NOTYETVALID)
536 {
537 ARRAY_ADD(&carr, mutt_str_dup(_("WARNING: Server certificate is not yet valid")));
538 }
539 if (certerr & CERTERR_EXPIRED)
540 {
541 ARRAY_ADD(&carr, mutt_str_dup(_("WARNING: Server certificate has expired")));
542 }
543 if (certerr & CERTERR_REVOKED)
544 {
545 ARRAY_ADD(&carr, mutt_str_dup(_("WARNING: Server certificate has been revoked")));
546 }
547 if (certerr & CERTERR_HOSTNAME)
548 {
549 ARRAY_ADD(&carr, mutt_str_dup(_("WARNING: Server hostname does not match certificate")));
550 }
551 if (certerr & CERTERR_SIGNERNOTCA)
552 {
553 ARRAY_ADD(&carr, mutt_str_dup(_("WARNING: Signer of server certificate is not a CA")));
554 }
555 if (certerr & CERTERR_INSECUREALG)
556 {
557 ARRAY_ADD(&carr, mutt_str_dup(_("Warning: Server certificate was signed using an insecure algorithm")));
558 }
559
560 snprintf(title, sizeof(title),
561 _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
562
563 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
564 const bool allow_always = (c_certificate_file && !savedcert &&
566 int rc = dlg_verify_certificate(title, &carr, allow_always, false);
567 if (rc == 3) // Accept always
568 {
569 bool saved = false;
570 FILE *fp = mutt_file_fopen(c_certificate_file, "a");
571 if (fp)
572 {
573 if (certerr & CERTERR_HOSTNAME) // Save hostname if necessary
574 {
575 fpbuf[0] = '\0';
576 tls_fingerprint(GNUTLS_DIG_MD5, fpbuf, sizeof(fpbuf), certdata);
577 fprintf(fp, "#H %s %s\n", hostname, fpbuf);
578 saved = true;
579 }
580 if (certerr ^ CERTERR_HOSTNAME) // Save the cert for all other errors
581 {
582 int rc2 = gnutls_pem_base64_encode_alloc("CERTIFICATE", certdata, &pemdata);
583 if (rc2 == 0)
584 {
585 if (fwrite(pemdata.data, pemdata.size, 1, fp) == 1)
586 {
587 saved = true;
588 }
589 gnutls_free(pemdata.data);
590 }
591 }
592 mutt_file_fclose(&fp);
593 }
594 if (!saved)
595 mutt_message(_("Certificate saved"));
596 else
597 mutt_error(_("Warning: Couldn't save certificate"));
598 }
599
600 cert_array_clear(&carr);
601 ARRAY_FREE(&carr);
602 gnutls_x509_crt_deinit(cert);
603 return (rc > 1);
604}
605
612static int tls_check_certificate(struct Connection *conn)
613{
614 struct TlsSockData *data = conn->sockdata;
615 gnutls_session_t state = data->state;
616 const gnutls_datum_t *cert_list = NULL;
617 unsigned int cert_list_size = 0;
618 gnutls_certificate_status_t certstat;
619 int certerr, savedcert, rc = 0;
620 int max_preauth_pass = -1;
621
622 /* tls_verify_peers() calls gnutls_certificate_verify_peers2(),
623 * which verifies the auth_type is GNUTLS_CRD_CERTIFICATE
624 * and that get_certificate_type() for the server is GNUTLS_CRT_X509.
625 * If it returns 0, certstat will be set with failure codes for the first
626 * cert in the chain(from CA to host) with an error.
627 */
628 if (tls_verify_peers(state, &certstat) != 0)
629 return 0;
630
631 cert_list = gnutls_certificate_get_peers(state, &cert_list_size);
632 if (!cert_list)
633 {
634 mutt_error(_("Unable to get certificate from peer"));
635 return 0;
636 }
637
638 /* tls_verify_peers doesn't check hostname or expiration, so walk
639 * from most specific to least checking these. If we see a saved certificate,
640 * its status short-circuits the remaining checks. */
641 int preauthrc = 0;
642 for (int i = 0; i < cert_list_size; i++)
643 {
644 rc = tls_check_preauth(&cert_list[i], certstat, conn->account.host, i,
645 &certerr, &savedcert);
646 preauthrc += rc;
647 if (!preauthrc)
648 max_preauth_pass = i;
649
650 if (savedcert)
651 {
652 if (preauthrc == 0)
653 return 1;
654 break;
655 }
656 }
657
658 /* then check interactively, starting from chain root */
659 for (int i = cert_list_size - 1; i >= 0; i--)
660 {
661 rc = tls_check_one_certificate(&cert_list[i], certstat, conn->account.host,
662 i, cert_list_size);
663
664 /* Stop checking if the menu cert is aborted or rejected. */
665 if (rc == 0)
666 break;
667
668 /* add signers to trust set, then reverify */
669 if (i)
670 {
671 int rcsettrust = gnutls_certificate_set_x509_trust_mem(data->xcred, &cert_list[i],
672 GNUTLS_X509_FMT_DER);
673 if (rcsettrust != 1)
674 mutt_debug(LL_DEBUG1, "error trusting certificate %d: %d\n", i, rcsettrust);
675
676 if (tls_verify_peers(state, &certstat) != 0)
677 return 0;
678
679 /* If the cert chain now verifies, and all lower certs already
680 * passed preauth, we are done. */
681 if (!certstat && (max_preauth_pass >= (i - 1)))
682 return 1;
683 }
684 }
685
686 return rc;
687}
688
696static void tls_get_client_cert(struct Connection *conn)
697{
698 struct TlsSockData *data = conn->sockdata;
699 gnutls_x509_crt_t clientcrt;
700 char *cn = NULL;
701 size_t cnlen = 0;
702 int rc;
703
704 /* get our cert CN if we have one */
705 const gnutls_datum_t *crtdata = gnutls_certificate_get_ours(data->state);
706 if (!crtdata)
707 return;
708
709 if (gnutls_x509_crt_init(&clientcrt) < 0)
710 {
711 mutt_debug(LL_DEBUG1, "Failed to init gnutls crt\n");
712 return;
713 }
714
715 if (gnutls_x509_crt_import(clientcrt, crtdata, GNUTLS_X509_FMT_DER) < 0)
716 {
717 mutt_debug(LL_DEBUG1, "Failed to import gnutls client crt\n");
718 goto err;
719 }
720
721 /* get length of CN, then grab it. */
722 rc = gnutls_x509_crt_get_dn_by_oid(clientcrt, GNUTLS_OID_X520_COMMON_NAME, 0,
723 0, NULL, &cnlen);
724 if (((rc >= 0) || (rc == GNUTLS_E_SHORT_MEMORY_BUFFER)) && (cnlen > 0))
725 {
726 cn = mutt_mem_calloc(1, cnlen);
727 if (gnutls_x509_crt_get_dn_by_oid(clientcrt, GNUTLS_OID_X520_COMMON_NAME, 0,
728 0, cn, &cnlen) < 0)
729 {
730 goto err;
731 }
732 mutt_debug(LL_DEBUG2, "client certificate CN: %s\n", cn);
733
734 /* if we are using a client cert, SASL may expect an external auth name */
735 if (mutt_account_getuser(&conn->account) < 0)
736 mutt_debug(LL_DEBUG1, "Couldn't get user info\n");
737 }
738
739err:
740 FREE(&cn);
741 gnutls_x509_crt_deinit(clientcrt);
742}
743
744#ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT
751static int tls_set_priority(struct TlsSockData *data)
752{
753 size_t nproto = 5;
754 int rv = -1;
755
756 struct Buffer *priority = mutt_buffer_pool_get();
757
758 const char *const c_ssl_ciphers = cs_subset_string(NeoMutt->sub, "ssl_ciphers");
759 if (c_ssl_ciphers)
760 mutt_buffer_strcpy(priority, c_ssl_ciphers);
761 else
762 mutt_buffer_strcpy(priority, "NORMAL");
763
764 const bool c_ssl_use_tlsv1_3 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_3");
765 if (!c_ssl_use_tlsv1_3)
766 {
767 nproto--;
768 mutt_buffer_addstr(priority, ":-VERS-TLS1.3");
769 }
770 const bool c_ssl_use_tlsv1_2 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_2");
771 if (!c_ssl_use_tlsv1_2)
772 {
773 nproto--;
774 mutt_buffer_addstr(priority, ":-VERS-TLS1.2");
775 }
776 const bool c_ssl_use_tlsv1_1 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_1");
777 if (!c_ssl_use_tlsv1_1)
778 {
779 nproto--;
780 mutt_buffer_addstr(priority, ":-VERS-TLS1.1");
781 }
782 const bool c_ssl_use_tlsv1 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1");
783 if (!c_ssl_use_tlsv1)
784 {
785 nproto--;
786 mutt_buffer_addstr(priority, ":-VERS-TLS1.0");
787 }
788 const bool c_ssl_use_sslv3 = cs_subset_bool(NeoMutt->sub, "ssl_use_sslv3");
789 if (!c_ssl_use_sslv3)
790 {
791 nproto--;
792 mutt_buffer_addstr(priority, ":-VERS-SSL3.0");
793 }
794
795 if (nproto == 0)
796 {
797 mutt_error(_("All available protocols for TLS/SSL connection disabled"));
798 goto cleanup;
799 }
800
801 int err = gnutls_priority_set_direct(data->state, mutt_buffer_string(priority), NULL);
802 if (err < 0)
803 {
804 mutt_error("gnutls_priority_set_direct(%s): %s",
805 mutt_buffer_string(priority), gnutls_strerror(err));
806 goto cleanup;
807 }
808
809 rv = 0;
810
811cleanup:
812 mutt_buffer_pool_release(&priority);
813 return rv;
814}
815
816#else
824{
825 size_t nproto = 0; /* number of tls/ssl protocols */
826
827 const bool c_ssl_use_tlsv1_2 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_2");
828 if (c_ssl_use_tlsv1_2)
829 protocol_priority[nproto++] = GNUTLS_TLS1_2;
830 const bool c_ssl_use_tlsv1_1 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_1");
831 if (c_ssl_use_tlsv1_1)
832 protocol_priority[nproto++] = GNUTLS_TLS1_1;
833 const bool c_ssl_use_tlsv1 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1");
834 if (c_ssl_use_tlsv1)
835 protocol_priority[nproto++] = GNUTLS_TLS1;
836 const bool c_ssl_use_sslv3 = cs_subset_bool(NeoMutt->sub, "ssl_use_sslv3");
837 if (c_ssl_use_sslv3)
838 protocol_priority[nproto++] = GNUTLS_SSL3;
839 protocol_priority[nproto] = 0;
840
841 if (nproto == 0)
842 {
843 mutt_error(_("All available protocols for TLS/SSL connection disabled"));
844 return -1;
845 }
846
847 const char *const c_ssl_ciphers = cs_subset_string(NeoMutt->sub, "ssl_ciphers");
848 if (c_ssl_ciphers)
849 {
850 mutt_error(_("Explicit ciphersuite selection via $ssl_ciphers not supported"));
851 }
852
853 /* We use default priorities (see gnutls documentation),
854 * except for protocol version */
855 gnutls_set_default_priority(data->state);
856 gnutls_protocol_set_priority(data->state, protocol_priority);
857 return 0;
858}
859#endif
860
870static int tls_negotiate(struct Connection *conn)
871{
872 struct TlsSockData *data = mutt_mem_calloc(1, sizeof(struct TlsSockData));
873 conn->sockdata = data;
874 int err = gnutls_certificate_allocate_credentials(&data->xcred);
875 if (err < 0)
876 {
877 FREE(&conn->sockdata);
878 mutt_error("gnutls_certificate_allocate_credentials: %s", gnutls_strerror(err));
879 return -1;
880 }
881
882 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
883 gnutls_certificate_set_x509_trust_file(data->xcred, c_certificate_file, GNUTLS_X509_FMT_PEM);
884 /* ignore errors, maybe file doesn't exist yet */
885
886 const char *const c_ssl_ca_certificates_file = cs_subset_path(NeoMutt->sub, "ssl_ca_certificates_file");
887 if (c_ssl_ca_certificates_file)
888 {
889 gnutls_certificate_set_x509_trust_file(data->xcred, c_ssl_ca_certificates_file,
890 GNUTLS_X509_FMT_PEM);
891 }
892
893 const char *const c_ssl_client_cert = cs_subset_path(NeoMutt->sub, "ssl_client_cert");
894 if (c_ssl_client_cert)
895 {
896 mutt_debug(LL_DEBUG2, "Using client certificate %s\n", c_ssl_client_cert);
897 gnutls_certificate_set_x509_key_file(data->xcred, c_ssl_client_cert,
898 c_ssl_client_cert, GNUTLS_X509_FMT_PEM);
899 }
900
901#ifdef HAVE_DECL_GNUTLS_VERIFY_DISABLE_TIME_CHECKS
902 /* disable checking certificate activation/expiration times
903 * in gnutls, we do the checks ourselves */
904 gnutls_certificate_set_verify_flags(data->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
905#endif
906
907 err = gnutls_init(&data->state, GNUTLS_CLIENT);
908 if (err)
909 {
910 mutt_error("gnutls_init: %s", gnutls_strerror(err));
911 goto fail;
912 }
913
914 /* set socket */
915 gnutls_transport_set_ptr(data->state, (gnutls_transport_ptr_t) (long) conn->fd);
916
917 if (gnutls_server_name_set(data->state, GNUTLS_NAME_DNS, conn->account.host,
918 mutt_str_len(conn->account.host)))
919 {
920 mutt_error(_("Warning: unable to set TLS SNI host name"));
921 }
922
923 if (tls_set_priority(data) < 0)
924 {
925 goto fail;
926 }
927
928 const short c_ssl_min_dh_prime_bits = cs_subset_number(NeoMutt->sub, "ssl_min_dh_prime_bits");
929 if (c_ssl_min_dh_prime_bits > 0)
930 {
931 gnutls_dh_set_prime_bits(data->state, c_ssl_min_dh_prime_bits);
932 }
933
934 gnutls_credentials_set(data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
935
936 do
937 {
938 err = gnutls_handshake(data->state);
939 } while ((err == GNUTLS_E_AGAIN) || (err == GNUTLS_E_INTERRUPTED));
940
941 if (err < 0)
942 {
943 if (err == GNUTLS_E_FATAL_ALERT_RECEIVED)
944 {
945 mutt_error("gnutls_handshake: %s(%s)", gnutls_strerror(err),
946 gnutls_alert_get_name(gnutls_alert_get(data->state)));
947 }
948 else
949 {
950 mutt_error("gnutls_handshake: %s", gnutls_strerror(err));
951 }
952 goto fail;
953 }
954
955 if (tls_check_certificate(conn) == 0)
956 goto fail;
957
958 /* set Security Strength Factor (SSF) for SASL */
959 /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
960 conn->ssf = gnutls_cipher_get_key_size(gnutls_cipher_get(data->state)) * 8;
961
963
964 if (!OptNoCurses)
965 {
966 mutt_message(_("SSL/TLS connection using %s (%s/%s/%s)"),
967 gnutls_protocol_get_name(gnutls_protocol_get_version(data->state)),
968 gnutls_kx_get_name(gnutls_kx_get(data->state)),
969 gnutls_cipher_get_name(gnutls_cipher_get(data->state)),
970 gnutls_mac_get_name(gnutls_mac_get(data->state)));
971 mutt_sleep(0);
972 }
973
974 return 0;
975
976fail:
977 gnutls_certificate_free_credentials(data->xcred);
978 gnutls_deinit(data->state);
979 FREE(&conn->sockdata);
980 return -1;
981}
982
986static int tls_socket_poll(struct Connection *conn, time_t wait_secs)
987{
988 struct TlsSockData *data = conn->sockdata;
989 if (!data)
990 return -1;
991
992 if (gnutls_record_check_pending(data->state))
993 return 1;
994
995 return raw_socket_poll(conn, wait_secs);
996}
997
1001static int tls_socket_close(struct Connection *conn)
1002{
1003 struct TlsSockData *data = conn->sockdata;
1004 if (data)
1005 {
1006 /* shut down only the write half to avoid hanging waiting for the remote to respond.
1007 *
1008 * RFC5246 7.2.1. "Closure Alerts"
1009 *
1010 * It is not required for the initiator of the close to wait for the
1011 * responding close_notify alert before closing the read side of the
1012 * connection. */
1013 gnutls_bye(data->state, GNUTLS_SHUT_WR);
1014
1015 gnutls_certificate_free_credentials(data->xcred);
1016 gnutls_deinit(data->state);
1017 FREE(&conn->sockdata);
1018 }
1019
1020 return raw_socket_close(conn);
1021}
1022
1026static int tls_socket_open(struct Connection *conn)
1027{
1028 if (raw_socket_open(conn) < 0)
1029 return -1;
1030
1031 if (tls_negotiate(conn) < 0)
1032 {
1033 tls_socket_close(conn);
1034 return -1;
1035 }
1036
1037 return 0;
1038}
1039
1043static int tls_socket_read(struct Connection *conn, char *buf, size_t count)
1044{
1045 struct TlsSockData *data = conn->sockdata;
1046 if (!data)
1047 {
1048 mutt_error(_("Error: no TLS socket open"));
1049 return -1;
1050 }
1051
1052 int rc;
1053 do
1054 {
1055 rc = gnutls_record_recv(data->state, buf, count);
1056 } while ((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED));
1057
1058 if (rc < 0)
1059 {
1060 mutt_error("tls_socket_read (%s)", gnutls_strerror(rc));
1061 return -1;
1062 }
1063
1064 return rc;
1065}
1066
1070static int tls_socket_write(struct Connection *conn, const char *buf, size_t count)
1071{
1072 struct TlsSockData *data = conn->sockdata;
1073 size_t sent = 0;
1074
1075 if (!data)
1076 {
1077 mutt_error(_("Error: no TLS socket open"));
1078 return -1;
1079 }
1080
1081 do
1082 {
1083 int rc;
1084 do
1085 {
1086 rc = gnutls_record_send(data->state, buf + sent, count - sent);
1087 } while ((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED));
1088
1089 if (rc < 0)
1090 {
1091 mutt_error("tls_socket_write (%s)", gnutls_strerror(rc));
1092 return -1;
1093 }
1094
1095 sent += rc;
1096 } while (sent < count);
1097
1098 return sent;
1099}
1100
1104static int tls_starttls_close(struct Connection *conn)
1105{
1106 int rc;
1107
1108 rc = tls_socket_close(conn);
1109 conn->read = raw_socket_read;
1110 conn->write = raw_socket_write;
1111 conn->close = raw_socket_close;
1112 conn->poll = raw_socket_poll;
1113
1114 return rc;
1115}
1116
1124{
1125 if (tls_init() < 0)
1126 return -1;
1127
1128 conn->open = tls_socket_open;
1129 conn->read = tls_socket_read;
1130 conn->write = tls_socket_write;
1131 conn->close = tls_socket_close;
1132 conn->poll = tls_socket_poll;
1133
1134 return 0;
1135}
1136
1144{
1145 if (tls_init() < 0)
1146 return -1;
1147
1148 if (tls_negotiate(conn) < 0)
1149 return -1;
1150
1151 conn->read = tls_socket_read;
1152 conn->write = tls_socket_write;
1153 conn->close = tls_starttls_close;
1154 conn->poll = tls_socket_poll;
1155
1156 return 0;
1157}
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition: array.h:155
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:203
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:57
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:327
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:233
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
Convenience wrapper for the config headers.
int mutt_account_getuser(struct ConnAccount *cac)
Retrieve username into ConnAccount, if necessary.
Definition: connaccount.c:50
Convenience wrapper for the core headers.
int mutt_date_make_tls(char *buf, size_t buflen, time_t timestamp)
Format date in TLS certificate verification style.
Definition: date.c:584
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:428
void cert_array_clear(struct CertArray *carr)
Free all memory of a CertArray.
int dlg_verify_certificate(const char *title, struct CertArray *carr, bool allow_always, bool allow_skip)
Ask the user to validate the certificate.
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
Read a line from a file.
Definition: file.c:720
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:618
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:38
#define CERTERR_INSECUREALG
Definition: gnutls.c:56
#define CERTERR_VALID
Definition: gnutls.c:49
static int tls_check_preauth(const gnutls_datum_t *certdata, gnutls_certificate_status_t certstat, const char *hostname, int chainidx, int *certerr, int *savedcert)
Prepare a certificate for authentication.
Definition: gnutls.c:312
static int tls_check_certificate(struct Connection *conn)
Check a connection's certificate.
Definition: gnutls.c:612
#define CERTERR_HOSTNAME
Definition: gnutls.c:54
#define CERTERR_EXPIRED
Definition: gnutls.c:50
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: gnutls.c:1143
#define CERT_SEP
Definition: gnutls.c:62
static bool tls_check_stored_hostname(const gnutls_datum_t *cert, const char *hostname)
Does the hostname match a stored certificate?
Definition: gnutls.c:190
#define CERTERR_NOTTRUSTED
Definition: gnutls.c:53
static int tls_verify_peers(gnutls_session_t tlsstate, gnutls_certificate_status_t *certstat)
Wrapper for gnutls_certificate_verify_peers()
Definition: gnutls.c:121
#define CERTERR_NOTYETVALID
Definition: gnutls.c:51
static int tls_compare_certificates(const gnutls_datum_t *peercert)
Compare certificates against $certificate_file
Definition: gnutls.c:235
static int protocol_priority[]
Definition: gnutls.c:73
static int tls_init(void)
Set up Gnu TLS.
Definition: gnutls.c:91
static void tls_fingerprint(gnutls_digest_algorithm_t algo, char *buf, size_t buflen, const gnutls_datum_t *data)
Create a fingerprint of a TLS Certificate.
Definition: gnutls.c:159
#define CERTERR_OTHER
Definition: gnutls.c:57
const int dialog_row_len
Definition: gnutls.c:60
static void tls_get_client_cert(struct Connection *conn)
Get the client certificate for a TLS connection.
Definition: gnutls.c:696
static int tls_check_one_certificate(const gnutls_datum_t *certdata, gnutls_certificate_status_t certstat, const char *hostname, int idx, size_t len)
Check a GnuTLS certificate.
Definition: gnutls.c:469
static int tls_negotiate(struct Connection *conn)
Negotiate TLS connection.
Definition: gnutls.c:870
int mutt_ssl_socket_setup(struct Connection *conn)
Set up SSL socket mulitplexor.
Definition: gnutls.c:1123
#define CERTERR_SIGNERNOTCA
Definition: gnutls.c:55
static void add_cert(const char *title, gnutls_x509_crt_t cert, bool issuer, struct CertArray *carr)
Look up certificate info and save it to a list.
Definition: gnutls.c:424
static int tls_set_priority(struct TlsSockData *data)
Set the priority of various protocols.
Definition: gnutls.c:823
#define CERTERR_REVOKED
Definition: gnutls.c:52
static int tls_starttls_close(struct Connection *conn)
Close a TLS connection - Implements Connection::close() -.
Definition: gnutls.c:1104
static int tls_socket_close(struct Connection *conn)
Close a TLS socket - Implements Connection::close() -.
Definition: gnutls.c:1001
int raw_socket_close(struct Connection *conn)
Close a socket - Implements Connection::close() -.
Definition: raw.c:362
static int tls_socket_open(struct Connection *conn)
Open a TLS socket - Implements Connection::open() -.
Definition: gnutls.c:1026
int raw_socket_open(struct Connection *conn)
Open a socket - Implements Connection::open() -.
Definition: raw.c:117
static int tls_socket_poll(struct Connection *conn, time_t wait_secs)
Check whether a socket read would block - Implements Connection::poll() -.
Definition: gnutls.c:986
int raw_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block - Implements Connection::poll() -.
Definition: raw.c:324
static int tls_socket_read(struct Connection *conn, char *buf, size_t count)
Read data from a TLS socket - Implements Connection::read() -.
Definition: gnutls.c:1043
int raw_socket_read(struct Connection *conn, char *buf, size_t len)
Read data from a socket - Implements Connection::read() -.
Definition: raw.c:264
int raw_socket_write(struct Connection *conn, const char *buf, size_t count)
Write data to a socket - Implements Connection::write() -.
Definition: raw.c:294
static int tls_socket_write(struct Connection *conn, const char *buf, size_t count)
Write data to a TLS socket - Implements Connection::write() -.
Definition: gnutls.c:1070
#define mutt_error(...)
Definition: logging.h:87
#define mutt_message(...)
Definition: logging.h:86
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
#define FREE(x)
Definition: memory.h:43
#define mutt_array_size(x)
Definition: memory.h:36
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1031
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:265
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1455
Some miscellaneous functions.
Handling of global boolean variables.
bool OptNoCurses
(pseudo) when sending in batch mode
Definition: options.h:53
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
regmatch_t * mutt_prex_capture(enum Prex which, const char *str)
Match a precompiled regex against a string.
Definition: prex.c:305
@ PREX_GNUTLS_CERT_HOST_HASH
[#H foo.com A76D 954B EB79 1F49 5B3A 0A0E 0681 65B1]
Definition: prex.h:36
@ PREX_GNUTLS_CERT_HOST_HASH_MATCH_HASH
#H foo.com [A76D ... 65B1]
Definition: prex.h:111
@ PREX_GNUTLS_CERT_HOST_HASH_MATCH_HOST
#H [foo.com] A76D ... 65B1
Definition: prex.h:110
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition: quad.h:38
static regoff_t mutt_regmatch_end(const regmatch_t *match)
Return the end of a match.
Definition: regex3.h:70
static regoff_t mutt_regmatch_start(const regmatch_t *match)
Return the start of a match.
Definition: regex3.h:60
GUI display the mailboxes in a side panel.
Handling of SSL encryption.
Key value store.
String manipulation buffer.
Definition: buffer.h:34
char * data
Pointer to data.
Definition: buffer.h:35
char host[128]
Server to login to.
Definition: connaccount.h:54
void * sockdata
Backend-specific socket data.
Definition: connection.h:56
int(* poll)(struct Connection *conn, time_t wait_secs)
Definition: connection.h:106
int(* write)(struct Connection *conn, const char *buf, size_t count)
Definition: connection.h:93
unsigned int ssf
Security strength factor, in bits (see notes)
Definition: connection.h:51
int(* close)(struct Connection *conn)
Definition: connection.h:117
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
int(* open)(struct Connection *conn)
Definition: connection.h:67
int fd
Socket file descriptor.
Definition: connection.h:54
int(* read)(struct Connection *conn, char *buf, size_t count)
Definition: connection.h:80
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
TLS socket data -.
Definition: gnutls.c:81
gnutls_certificate_credentials_t xcred
Definition: gnutls.c:83
gnutls_session_t state
Definition: gnutls.c:82