NeoMutt  2021-02-05-666-ge300cd
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 "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 
80 {
81  gnutls_session_t state;
82  gnutls_certificate_credentials_t xcred;
83 };
84 
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 
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 
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 
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 
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 filestat;
242 
243  const char *const c_certificate_file =
244  cs_subset_path(NeoMutt->sub, "certificate_file");
245  if (stat(c_certificate_file, &filestat) == -1)
246  return 0;
247 
248  b64_data.size = filestat.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 
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 
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 
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  {
540  &list, mutt_str_dup(_("WARNING: Server certificate is not yet valid")));
541  }
542  if (certerr & CERTERR_EXPIRED)
543  {
545  &list, mutt_str_dup(_("WARNING: Server certificate has expired")));
546  }
547  if (certerr & CERTERR_REVOKED)
548  {
550  &list, mutt_str_dup(_("WARNING: Server certificate has been revoked")));
551  }
552  if (certerr & CERTERR_HOSTNAME)
553  {
555  &list,
556  mutt_str_dup(_("WARNING: Server hostname does not match certificate")));
557  }
558  if (certerr & CERTERR_SIGNERNOTCA)
559  {
561  &list,
562  mutt_str_dup(_("WARNING: Signer of server certificate is not a CA")));
563  }
564  if (certerr & CERTERR_INSECUREALG)
565  {
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 }
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:427
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
int mutt_account_getuser(struct ConnAccount *cac)
Retrieve username into ConnAccount, if necessary.
Definition: connaccount.c:47
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define CERTERR_INSECUREALG
Definition: gnutls.c:55
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:40
static int tls_socket_open(struct Connection *conn)
Open a TLS socket - Implements Connection::open() -.
Definition: gnutls.c:1050
unsigned int ssf
Security strength factor, in bits (see below)
Definition: connection.h:41
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:158
#define mutt_error(...)
Definition: logging.h:88
int(* read)(struct Connection *conn, char *buf, size_t count)
Definition: connection.h:82
size_t idx
Definition: mailbox.c:257
Shared functions that are private to Connections.
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
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:1010
void * sockdata
Backend-specific socket data.
Definition: connection.h:46
static int tls_compare_certificates(const gnutls_datum_t *peercert)
Compare certificates against $certificate_file
Definition: gnutls.c:235
static int tls_negotiate(struct Connection *conn)
Negotiate TLS connection.
Definition: gnutls.c:890
static void tls_get_client_cert(struct Connection *conn)
Get the client certificate for a TLS connection.
Definition: gnutls.c:708
String manipulation buffer.
Definition: buffer.h:33
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
#define CERTERR_REVOKED
Definition: gnutls.c:51
#define _(a)
Definition: message.h:28
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:471
WHERE bool OptNoCurses
(pseudo) when sending in batch mode
Definition: options.h:47
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:667
#H [foo.com] A76D ... 65B1
Definition: prex.h:109
gnutls_session_t state
Definition: gnutls.c:81
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
Container for Accounts, Notifications.
Definition: neomutt.h:36
static int tls_set_priority(struct TlsSockData *data)
Set the priority of various protocols.
Definition: gnutls.c:839
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
static int tls_check_certificate(struct Connection *conn)
Check a connection&#39;s certificate.
Definition: gnutls.c:624
Convenience wrapper for the config headers.
static void add_cert(const char *title, gnutls_x509_crt_t cert, bool issuer, struct ListHead *list)
Look up certificate info and save it to a list.
Definition: gnutls.c:426
int(* open)(struct Connection *conn)
Definition: connection.h:69
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
char host[128]
Server to login to.
Definition: connaccount.h:53
Some miscellaneous functions.
#define mutt_array_size(x)
Definition: memory.h:33
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:1094
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
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1461
int mutt_date_make_tls(char *buf, size_t buflen, time_t timestamp)
Format date in TLS certificate verification style.
Definition: date.c:583
Log at debug level 2.
Definition: logging.h:41
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
#define CERT_SEP
Definition: gnutls.c:61
Convenience wrapper for the core headers.
gnutls_certificate_credentials_t xcred
Definition: gnutls.c:82
int raw_socket_write(struct Connection *conn, const char *buf, size_t count)
Write data to a socket - Implements Connection::write() -.
Definition: raw.c:297
static int tls_socket_close(struct Connection *conn)
Close a TLS socket - Implements Connection::close() -.
Definition: gnutls.c:1025
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
#define CERTERR_EXPIRED
Definition: gnutls.c:49
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
int(* close)(struct Connection *conn)
Definition: connection.h:119
TLS socket data -.
Definition: gnutls.c:79
regmatch_t * mutt_prex_capture(enum Prex which, const char *str)
match a precompiled regex against a string
Definition: prex.c:301
int fd
Socket file descriptor.
Definition: connection.h:44
#define CERTERR_NOTYETVALID
Definition: gnutls.c:50
#define CERTERR_HOSTNAME
Definition: gnutls.c:53
static int protocol_priority[]
Definition: gnutls.c:72
int raw_socket_open(struct Connection *conn)
Open a socket - Implements Connection::open() -.
Definition: raw.c:120
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
int dlg_verify_certificate(const char *title, struct ListHead *list, bool allow_always, bool allow_skip)
Ask the user to validate the certificate.
Definition: dlgverifycert.c:92
const int dialog_row_len
Definition: gnutls.c:59
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:1067
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
static int tls_init(void)
Set up Gnu TLS.
Definition: gnutls.c:90
static regoff_t mutt_regmatch_start(const regmatch_t *match)
Return the start of a match.
Definition: regex3.h:61
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
#define CERTERR_VALID
Definition: gnutls.c:48
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:385
[#H foo.com A76D 954B EB79 1F49 5B3A 0A0E 0681 65B1]
Definition: prex.h:36
int raw_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block - Implements Connection::poll() -.
Definition: raw.c:327
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
int raw_socket_close(struct Connection *conn)
Close a socket - Implements Connection::close() -.
Definition: raw.c:365
int(* poll)(struct Connection *conn, time_t wait_secs)
Definition: connection.h:108
int raw_socket_read(struct Connection *conn, char *buf, size_t len)
Read data from a socket - Implements Connection::read() -.
Definition: raw.c:267
Log at debug level 1.
Definition: logging.h:40
int mutt_ssl_socket_setup(struct Connection *conn)
Set up SSL socket mulitplexor.
Definition: gnutls.c:1147
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:38
int(* write)(struct Connection *conn, const char *buf, size_t count)
Definition: connection.h:95
static int tls_starttls_close(struct Connection *conn)
Close a TLS connection - Implements Connection::close() -.
Definition: gnutls.c:1128
#define mutt_message(...)
Definition: logging.h:87
#define FREE(x)
Definition: memory.h:40
Handling of SSL encryption.
static regoff_t mutt_regmatch_end(const regmatch_t *match)
Return the end of a match.
Definition: regex3.h:71
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:38
#define CERTERR_NOTTRUSTED
Definition: gnutls.c:52
Handling of global boolean variables.
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1128
static int tls_check_stored_hostname(const gnutls_datum_t *cert, const char *hostname)
Does the hostname match a stored certificate?
Definition: gnutls.c:189
Convenience wrapper for the library headers.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: gnutls.c:1167
#define CERTERR_OTHER
Definition: gnutls.c:56
#H foo.com [A76D ... 65B1]
Definition: prex.h:110
#define CERTERR_SIGNERNOTCA
Definition: gnutls.c:54
static int tls_verify_peers(gnutls_session_t tlsstate, gnutls_certificate_status_t *certstat)
Wrapper for gnutls_certificate_verify_peers()
Definition: gnutls.c:120