NeoMutt  2020-06-26-30-g76c339
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 "lib.h"
41 #include "muttlib.h"
42 #include "options.h"
43 #include "ssl.h"
44 
45 // clang-format off
46 /* certificate error bitmap values */
47 #define CERTERR_VALID 0
48 #define CERTERR_EXPIRED (1 << 0)
49 #define CERTERR_NOTYETVALID (1 << 1)
50 #define CERTERR_REVOKED (1 << 2)
51 #define CERTERR_NOTTRUSTED (1 << 3)
52 #define CERTERR_HOSTNAME (1 << 4)
53 #define CERTERR_SIGNERNOTCA (1 << 5)
54 #define CERTERR_INSECUREALG (1 << 6)
55 #define CERTERR_OTHER (1 << 7)
56 // clang-format on
57 
58 const int dialog_row_len = 128;
59 
60 #define CERT_SEP "-----BEGIN"
61 
62 #ifndef HAVE_GNUTLS_PRIORITY_SET_DIRECT
63 /* This array needs to be large enough to hold all the possible values support
64  * by NeoMutt. The initialized values are just placeholders--the array gets
65  * overwrriten in tls_negotiate() depending on the $ssl_use_* options.
66  *
67  * Note: gnutls_protocol_set_priority() was removed in GnuTLS version
68  * 3.4 (2015-04). TLS 1.3 support wasn't added until version 3.6.5.
69  * Therefore, no attempt is made to support $ssl_use_tlsv1_3 in this code.
70  */
71 static int protocol_priority[] = { GNUTLS_TLS1_2, GNUTLS_TLS1_1, GNUTLS_TLS1,
72  GNUTLS_SSL3, 0 };
73 #endif
74 
79 {
80  gnutls_session_t state;
81  gnutls_certificate_credentials_t xcred;
82 };
83 
89 static int tls_init(void)
90 {
91  static bool init_complete = false;
92  int err;
93 
94  if (init_complete)
95  return 0;
96 
97  err = gnutls_global_init();
98  if (err < 0)
99  {
100  mutt_error("gnutls_global_init: %s", gnutls_strerror(err));
101  return -1;
102  }
103 
104  init_complete = true;
105  return 0;
106 }
107 
119 static int tls_verify_peers(gnutls_session_t tlsstate, gnutls_certificate_status_t *certstat)
120 {
121  /* gnutls_certificate_verify_peers2() chains to
122  * gnutls_x509_trust_list_verify_crt2(). That function's documentation says:
123  *
124  * When a certificate chain of cert_list_size with more than one
125  * certificates is provided, the verification status will apply to
126  * the first certificate in the chain that failed
127  * verification. The verification process starts from the end of
128  * the chain(from CA to end certificate). The first certificate
129  * in the chain must be the end-certificate while the rest of the
130  * members may be sorted or not.
131  *
132  * This is why tls_check_certificate() loops from CA to host in that order,
133  * calling the menu, and recalling tls_verify_peers() for each approved
134  * cert in the chain.
135  */
136  int rc = gnutls_certificate_verify_peers2(tlsstate, certstat);
137 
138  /* certstat was set */
139  if (rc == 0)
140  return 0;
141 
142  if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND)
143  mutt_error(_("Unable to get certificate from peer"));
144  else
145  mutt_error(_("Certificate verification error (%s)"), gnutls_strerror(rc));
146 
147  return rc;
148 }
149 
157 static void tls_fingerprint(gnutls_digest_algorithm_t algo, char *buf,
158  size_t buflen, const gnutls_datum_t *data)
159 {
160  unsigned char md[64];
161  size_t n;
162 
163  n = 64;
164 
165  if (gnutls_fingerprint(algo, data, (char *) md, &n) < 0)
166  {
167  snprintf(buf, buflen, _("[unable to calculate]"));
168  }
169  else
170  {
171  for (int i = 0; i < (int) n; i++)
172  {
173  char ch[8];
174  snprintf(ch, 8, "%02X%s", md[i], ((i % 2) ? " " : ""));
175  mutt_str_cat(buf, buflen, ch);
176  }
177  buf[2 * n + n / 2 - 1] = '\0'; /* don't want trailing space */
178  }
179 }
180 
188 static int tls_check_stored_hostname(const gnutls_datum_t *cert, const char *hostname)
189 {
190  char *linestr = NULL;
191  size_t linestrsize = 0;
192 
193  /* try checking against names stored in stored certs file */
194  FILE *fp = mutt_file_fopen(C_CertificateFile, "r");
195  if (!fp)
196  return 0;
197 
198  char buf[80];
199  buf[0] = '\0';
200  tls_fingerprint(GNUTLS_DIG_MD5, buf, sizeof(buf), cert);
201  while ((linestr = mutt_file_read_line(linestr, &linestrsize, fp, NULL, 0)))
202  {
203  regmatch_t *match = mutt_prex_capture(PREX_GNUTLS_CERT_HOST_HASH, linestr);
204  if (match)
205  {
206  regmatch_t *mhost = &match[PREX_GNUTLS_CERT_HOST_HASH_MATCH_HOST];
207  regmatch_t *mhash = &match[PREX_GNUTLS_CERT_HOST_HASH_MATCH_HASH];
208  linestr[mutt_regmatch_end(mhost)] = '\0';
209  linestr[mutt_regmatch_end(mhash)] = '\0';
210  if ((strcmp(linestr + mutt_regmatch_start(mhost), hostname) == 0) &&
211  (strcmp(linestr + mutt_regmatch_start(mhash), buf) == 0))
212  {
213  FREE(&linestr);
214  mutt_file_fclose(&fp);
215  return 1;
216  }
217  }
218  }
219 
220  mutt_file_fclose(&fp);
221 
222  /* not found a matching name */
223  return 0;
224 }
225 
232 static int tls_compare_certificates(const gnutls_datum_t *peercert)
233 {
234  gnutls_datum_t cert = { 0 };
235  unsigned char *ptr = NULL;
236  gnutls_datum_t b64_data = { 0 };
237  unsigned char *b64_data_data = NULL;
238  struct stat filestat;
239 
240  if (stat(C_CertificateFile, &filestat) == -1)
241  return 0;
242 
243  b64_data.size = filestat.st_size;
244  b64_data_data = mutt_mem_calloc(1, b64_data.size + 1);
245  b64_data.data = b64_data_data;
246 
247  FILE *fp = mutt_file_fopen(C_CertificateFile, "r");
248  if (!fp)
249  return 0;
250 
251  b64_data.size = fread(b64_data.data, 1, b64_data.size, fp);
252  mutt_file_fclose(&fp);
253 
254  do
255  {
256  const int ret = gnutls_pem_base64_decode_alloc(NULL, &b64_data, &cert);
257  if (ret != 0)
258  {
259  FREE(&b64_data_data);
260  return 0;
261  }
262 
263  /* find start of cert, skipping junk */
264  ptr = (unsigned char *) strstr((char *) b64_data.data, CERT_SEP);
265  if (!ptr)
266  {
267  gnutls_free(cert.data);
268  FREE(&b64_data_data);
269  return 0;
270  }
271  /* find start of next cert */
272  ptr = (unsigned char *) strstr((char *) ptr + 1, CERT_SEP);
273 
274  b64_data.size = b64_data.size - (ptr - b64_data.data);
275  b64_data.data = ptr;
276 
277  if (cert.size == peercert->size)
278  {
279  if (memcmp(cert.data, peercert->data, cert.size) == 0)
280  {
281  /* match found */
282  gnutls_free(cert.data);
283  FREE(&b64_data_data);
284  return 1;
285  }
286  }
287 
288  gnutls_free(cert.data);
289  } while (ptr);
290 
291  /* no match found */
292  FREE(&b64_data_data);
293  return 0;
294 }
295 
307 static int tls_check_preauth(const gnutls_datum_t *certdata,
308  gnutls_certificate_status_t certstat, const char *hostname,
309  int chainidx, int *certerr, int *savedcert)
310 {
311  gnutls_x509_crt_t cert;
312 
313  *certerr = CERTERR_VALID;
314  *savedcert = 0;
315 
316  if (gnutls_x509_crt_init(&cert) < 0)
317  {
318  mutt_error(_("Error initialising gnutls certificate data"));
319  return -1;
320  }
321 
322  if (gnutls_x509_crt_import(cert, certdata, GNUTLS_X509_FMT_DER) < 0)
323  {
324  mutt_error(_("Error processing certificate data"));
325  gnutls_x509_crt_deinit(cert);
326  return -1;
327  }
328 
329  /* Note: tls_negotiate() contains a call to
330  * gnutls_certificate_set_verify_flags() with a flag disabling
331  * GnuTLS checking of the dates. So certstat shouldn't have the
332  * GNUTLS_CERT_EXPIRED and GNUTLS_CERT_NOT_ACTIVATED bits set. */
333  if (C_SslVerifyDates != MUTT_NO)
334  {
335  if (gnutls_x509_crt_get_expiration_time(cert) < mutt_date_epoch())
336  *certerr |= CERTERR_EXPIRED;
337  if (gnutls_x509_crt_get_activation_time(cert) > mutt_date_epoch())
338  *certerr |= CERTERR_NOTYETVALID;
339  }
340 
341  if ((chainidx == 0) && (C_SslVerifyHost != MUTT_NO) &&
342  !gnutls_x509_crt_check_hostname(cert, hostname) &&
343  !tls_check_stored_hostname(certdata, hostname))
344  {
345  *certerr |= CERTERR_HOSTNAME;
346  }
347 
348  if (certstat & GNUTLS_CERT_REVOKED)
349  {
350  *certerr |= CERTERR_REVOKED;
351  certstat ^= GNUTLS_CERT_REVOKED;
352  }
353 
354  /* see whether certificate is in our cache (certificates file) */
355  if (tls_compare_certificates(certdata))
356  {
357  *savedcert = 1;
358 
359  /* We check above for certs with bad dates or that are revoked.
360  * These must be accepted manually each time. Otherwise, we
361  * accept saved certificates as valid. */
362  if (*certerr == CERTERR_VALID)
363  {
364  gnutls_x509_crt_deinit(cert);
365  return 0;
366  }
367  }
368 
369  if (certstat & GNUTLS_CERT_INVALID)
370  {
371  *certerr |= CERTERR_NOTTRUSTED;
372  certstat ^= GNUTLS_CERT_INVALID;
373  }
374 
375  if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)
376  {
377  /* NB: already cleared if cert in cache */
378  *certerr |= CERTERR_NOTTRUSTED;
379  certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
380  }
381 
382  if (certstat & GNUTLS_CERT_SIGNER_NOT_CA)
383  {
384  /* NB: already cleared if cert in cache */
385  *certerr |= CERTERR_SIGNERNOTCA;
386  certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
387  }
388 
389  if (certstat & GNUTLS_CERT_INSECURE_ALGORITHM)
390  {
391  /* NB: already cleared if cert in cache */
392  *certerr |= CERTERR_INSECUREALG;
393  certstat ^= GNUTLS_CERT_INSECURE_ALGORITHM;
394  }
395 
396  /* we've been zeroing the interesting bits in certstat -
397  * don't return OK if there are any unhandled bits we don't
398  * understand */
399  if (certstat != 0)
400  *certerr |= CERTERR_OTHER;
401 
402  gnutls_x509_crt_deinit(cert);
403 
404  if (*certerr == CERTERR_VALID)
405  return 0;
406 
407  return -1;
408 }
409 
417 static void add_cert(const char *title, gnutls_x509_crt_t cert, bool issuer,
418  struct ListHead *list)
419 {
420  static const char *part[] = {
421  GNUTLS_OID_X520_COMMON_NAME, // CN
422  GNUTLS_OID_PKCS9_EMAIL, // Email
423  GNUTLS_OID_X520_ORGANIZATION_NAME, // O
424  GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, // OU
425  GNUTLS_OID_X520_LOCALITY_NAME, // L
426  GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, // ST
427  GNUTLS_OID_X520_COUNTRY_NAME, // C
428  };
429 
430  char buf[128];
431  int rc;
432 
433  // Allocate formatted strings and let the ListHead take ownership
434  mutt_list_insert_tail(list, mutt_str_dup(title));
435 
436  for (size_t i = 0; i < mutt_array_size(part); i++)
437  {
438  size_t buflen = sizeof(buf);
439  if (issuer)
440  rc = gnutls_x509_crt_get_issuer_dn_by_oid(cert, part[i], 0, 0, buf, &buflen);
441  else
442  rc = gnutls_x509_crt_get_dn_by_oid(cert, part[i], 0, 0, buf, &buflen);
443  if (rc != 0)
444  continue;
445 
446  char *line = NULL;
447  mutt_str_asprintf(&line, " %s", buf);
448  mutt_list_insert_tail(list, line);
449  }
450 }
451 
462 static int tls_check_one_certificate(const gnutls_datum_t *certdata,
463  gnutls_certificate_status_t certstat,
464  const char *hostname, int idx, size_t len)
465 {
466  struct ListHead list = STAILQ_HEAD_INITIALIZER(list);
467  int certerr, savedcert;
468  gnutls_x509_crt_t cert;
469  char fpbuf[128];
470  time_t t;
471  char datestr[30];
472  char title[256];
473  gnutls_datum_t pemdata = { 0 };
474 
475  if (tls_check_preauth(certdata, certstat, hostname, idx, &certerr, &savedcert) == 0)
476  return 1;
477 
478  /* interactive check from user */
479  if (gnutls_x509_crt_init(&cert) < 0)
480  {
481  mutt_error(_("Error initialising gnutls certificate data"));
482  return 0;
483  }
484 
485  if (gnutls_x509_crt_import(cert, certdata, GNUTLS_X509_FMT_DER) < 0)
486  {
487  mutt_error(_("Error processing certificate data"));
488  gnutls_x509_crt_deinit(cert);
489  return 0;
490  }
491 
492  add_cert(_("This certificate belongs to:"), cert, false, &list);
493  mutt_list_insert_tail(&list, NULL);
494  add_cert(_("This certificate was issued by:"), cert, true, &list);
495 
496  mutt_list_insert_tail(&list, NULL);
497  mutt_list_insert_tail(&list, mutt_str_dup(_("This certificate is valid")));
498 
499  char *line = NULL;
500  t = gnutls_x509_crt_get_activation_time(cert);
501  mutt_date_make_tls(datestr, sizeof(datestr), t);
502  mutt_str_asprintf(&line, _(" from %s"), datestr);
503  mutt_list_insert_tail(&list, line);
504 
505  t = gnutls_x509_crt_get_expiration_time(cert);
506  mutt_date_make_tls(datestr, sizeof(datestr), t);
507  mutt_str_asprintf(&line, _(" to %s"), datestr);
508  mutt_list_insert_tail(&list, line);
509  mutt_list_insert_tail(&list, NULL);
510 
511  fpbuf[0] = '\0';
512  tls_fingerprint(GNUTLS_DIG_SHA, fpbuf, sizeof(fpbuf), certdata);
513  mutt_str_asprintf(&line, _("SHA1 Fingerprint: %s"), fpbuf);
514  mutt_list_insert_tail(&list, line);
515  fpbuf[0] = '\0';
516  fpbuf[40] = '\0'; /* Ensure the second printed line is null terminated */
517  tls_fingerprint(GNUTLS_DIG_SHA256, fpbuf, sizeof(fpbuf), certdata);
518  fpbuf[39] = '\0'; /* Divide into two lines of output */
519  mutt_str_asprintf(&line, "%s%s", _("SHA256 Fingerprint: "), fpbuf);
520  mutt_list_insert_tail(&list, line);
521  mutt_str_asprintf(&line, "%*s%s", (int) mutt_str_len(_("SHA256 Fingerprint: ")),
522  "", fpbuf + 40);
523  mutt_list_insert_tail(&list, line);
524 
525  if (certerr)
526  mutt_list_insert_tail(&list, NULL);
527 
528  if (certerr & CERTERR_NOTYETVALID)
529  {
531  &list, mutt_str_dup(_("WARNING: Server certificate is not yet valid")));
532  }
533  if (certerr & CERTERR_EXPIRED)
534  {
536  &list, mutt_str_dup(_("WARNING: Server certificate has expired")));
537  }
538  if (certerr & CERTERR_REVOKED)
539  {
541  &list, mutt_str_dup(_("WARNING: Server certificate has been revoked")));
542  }
543  if (certerr & CERTERR_HOSTNAME)
544  {
546  &list,
547  mutt_str_dup(_("WARNING: Server hostname does not match certificate")));
548  }
549  if (certerr & CERTERR_SIGNERNOTCA)
550  {
552  &list,
553  mutt_str_dup(_("WARNING: Signer of server certificate is not a CA")));
554  }
555  if (certerr & CERTERR_INSECUREALG)
556  {
558  &list, mutt_str_dup(_("Warning: Server certificate was signed using "
559  "an insecure algorithm")));
560  }
561 
562  snprintf(title, sizeof(title),
563  _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
564 
565  const bool allow_always =
566  (C_CertificateFile && !savedcert &&
567  !(certerr & (CERTERR_EXPIRED | CERTERR_NOTYETVALID | CERTERR_REVOKED)));
568  int rc = dlg_verify_cert(title, &list, allow_always, false);
569  if (rc == 3) // Accept always
570  {
571  bool saved = false;
572  FILE *fp = mutt_file_fopen(C_CertificateFile, "a");
573  if (fp)
574  {
575  if (certerr & CERTERR_HOSTNAME) // Save hostname if necessary
576  {
577  fpbuf[0] = '\0';
578  tls_fingerprint(GNUTLS_DIG_MD5, fpbuf, sizeof(fpbuf), certdata);
579  fprintf(fp, "#H %s %s\n", hostname, fpbuf);
580  saved = true;
581  }
582  if (certerr ^ CERTERR_HOSTNAME) // Save the cert for all other errors
583  {
584  int rc2 = gnutls_pem_base64_encode_alloc("CERTIFICATE", certdata, &pemdata);
585  if (rc2 == 0)
586  {
587  if (fwrite(pemdata.data, pemdata.size, 1, fp) == 1)
588  {
589  saved = true;
590  }
591  gnutls_free(pemdata.data);
592  }
593  }
594  mutt_file_fclose(&fp);
595  }
596  if (!saved)
597  mutt_message(_("Certificate saved"));
598  else
599  mutt_error(_("Warning: Couldn't save certificate"));
600  }
601 
602  mutt_list_free(&list);
603  gnutls_x509_crt_deinit(cert);
604  return (rc > 1);
605 }
606 
613 static int tls_check_certificate(struct Connection *conn)
614 {
615  struct TlsSockData *data = conn->sockdata;
616  gnutls_session_t state = data->state;
617  const gnutls_datum_t *cert_list = NULL;
618  unsigned int cert_list_size = 0;
619  gnutls_certificate_status_t certstat;
620  int certerr, savedcert, rc = 0;
621  int max_preauth_pass = -1;
622 
623  /* tls_verify_peers() calls gnutls_certificate_verify_peers2(),
624  * which verifies the auth_type is GNUTLS_CRD_CERTIFICATE
625  * and that get_certificate_type() for the server is GNUTLS_CRT_X509.
626  * If it returns 0, certstat will be set with failure codes for the first
627  * cert in the chain(from CA to host) with an error.
628  */
629  if (tls_verify_peers(state, &certstat) != 0)
630  return 0;
631 
632  cert_list = gnutls_certificate_get_peers(state, &cert_list_size);
633  if (!cert_list)
634  {
635  mutt_error(_("Unable to get certificate from peer"));
636  return 0;
637  }
638 
639  /* tls_verify_peers doesn't check hostname or expiration, so walk
640  * from most specific to least checking these. If we see a saved certificate,
641  * its status short-circuits the remaining checks. */
642  int preauthrc = 0;
643  for (int i = 0; i < cert_list_size; i++)
644  {
645  rc = tls_check_preauth(&cert_list[i], certstat, conn->account.host, i,
646  &certerr, &savedcert);
647  preauthrc += rc;
648  if (!preauthrc)
649  max_preauth_pass = i;
650 
651  if (savedcert)
652  {
653  if (preauthrc == 0)
654  return 1;
655  break;
656  }
657  }
658 
659  /* then check interactively, starting from chain root */
660  for (int i = cert_list_size - 1; i >= 0; i--)
661  {
662  rc = tls_check_one_certificate(&cert_list[i], certstat, conn->account.host,
663  i, cert_list_size);
664 
665  /* Stop checking if the menu cert is aborted or rejected. */
666  if (rc == 0)
667  break;
668 
669  /* add signers to trust set, then reverify */
670  if (i)
671  {
672  int rcsettrust = gnutls_certificate_set_x509_trust_mem(
673  data->xcred, &cert_list[i], GNUTLS_X509_FMT_DER);
674  if (rcsettrust != 1)
675  mutt_debug(LL_DEBUG1, "error trusting certificate %d: %d\n", i, rcsettrust);
676 
677  if (tls_verify_peers(state, &certstat) != 0)
678  return 0;
679 
680  /* If the cert chain now verifies, and all lower certs already
681  * passed preauth, we are done. */
682  if (!certstat && (max_preauth_pass >= (i - 1)))
683  return 1;
684  }
685  }
686 
687  return rc;
688 }
689 
694 static void tls_get_client_cert(struct Connection *conn)
695 {
696  struct TlsSockData *data = conn->sockdata;
697  gnutls_x509_crt_t clientcrt;
698  char *dn = NULL;
699  char *cn = NULL;
700  char *cnend = NULL;
701  size_t dnlen;
702 
703  /* get our cert CN if we have one */
704  const gnutls_datum_t *crtdata = gnutls_certificate_get_ours(data->state);
705  if (!crtdata)
706  return;
707 
708  if (gnutls_x509_crt_init(&clientcrt) < 0)
709  {
710  mutt_debug(LL_DEBUG1, "Failed to init gnutls crt\n");
711  return;
712  }
713  if (gnutls_x509_crt_import(clientcrt, crtdata, GNUTLS_X509_FMT_DER) < 0)
714  {
715  mutt_debug(LL_DEBUG1, "Failed to import gnutls client crt\n");
716  goto err_crt;
717  }
718  /* get length of DN */
719  dnlen = 0;
720  gnutls_x509_crt_get_dn(clientcrt, NULL, &dnlen);
721  dn = mutt_mem_calloc(1, dnlen);
722 
723  gnutls_x509_crt_get_dn(clientcrt, dn, &dnlen);
724  mutt_debug(LL_DEBUG2, "client certificate DN: %s\n", dn);
725 
726  /* extract CN to use as external user name */
727  cn = strstr(dn, "CN=");
728  if (!cn)
729  {
730  mutt_debug(LL_DEBUG1, "no CN found in DN\n");
731  goto err_dn;
732  }
733 
734  cnend = strstr(dn, ",EMAIL=");
735  if (cnend)
736  *cnend = '\0';
737 
738  /* if we are using a client cert, SASL may expect an external auth name */
739  if (mutt_account_getuser(&conn->account) < 0)
740  mutt_debug(LL_DEBUG1, "Couldn't get user info\n");
741 
742 err_dn:
743  FREE(&dn);
744 err_crt:
745  gnutls_x509_crt_deinit(clientcrt);
746 }
747 
748 #ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT
749 
755 static int tls_set_priority(struct TlsSockData *data)
756 {
757  size_t nproto = 5;
758  int rv = -1;
759 
760  struct Buffer *priority = mutt_buffer_pool_get();
761 
762  if (C_SslCiphers)
763  mutt_buffer_strcpy(priority, C_SslCiphers);
764  else
765  mutt_buffer_strcpy(priority, "NORMAL");
766 
767  if (!C_SslUseTlsv13)
768  {
769  nproto--;
770  mutt_buffer_addstr(priority, ":-VERS-TLS1.3");
771  }
772  if (!C_SslUseTlsv12)
773  {
774  nproto--;
775  mutt_buffer_addstr(priority, ":-VERS-TLS1.2");
776  }
777  if (!C_SslUseTlsv11)
778  {
779  nproto--;
780  mutt_buffer_addstr(priority, ":-VERS-TLS1.1");
781  }
782  if (!C_SslUseTlsv1)
783  {
784  nproto--;
785  mutt_buffer_addstr(priority, ":-VERS-TLS1.0");
786  }
787  if (!C_SslUseSslv3)
788  {
789  nproto--;
790  mutt_buffer_addstr(priority, ":-VERS-SSL3.0");
791  }
792 
793  if (nproto == 0)
794  {
795  mutt_error(_("All available protocols for TLS/SSL connection disabled"));
796  goto cleanup;
797  }
798 
799  int err = gnutls_priority_set_direct(data->state, mutt_b2s(priority), NULL);
800  if (err < 0)
801  {
802  mutt_error("gnutls_priority_set_direct(%s): %s", mutt_b2s(priority),
803  gnutls_strerror(err));
804  goto cleanup;
805  }
806 
807  rv = 0;
808 
809 cleanup:
810  mutt_buffer_pool_release(&priority);
811  return rv;
812 }
813 
814 #else
815 
821 static int tls_set_priority(struct TlsSockData *data)
822 {
823  size_t nproto = 0; /* number of tls/ssl protocols */
824 
825  if (C_SslUseTlsv12)
826  protocol_priority[nproto++] = GNUTLS_TLS1_2;
827  if (C_SslUseTlsv11)
828  protocol_priority[nproto++] = GNUTLS_TLS1_1;
829  if (C_SslUseTlsv1)
830  protocol_priority[nproto++] = GNUTLS_TLS1;
831  if (C_SslUseSslv3)
832  protocol_priority[nproto++] = GNUTLS_SSL3;
833  protocol_priority[nproto] = 0;
834 
835  if (nproto == 0)
836  {
837  mutt_error(_("All available protocols for TLS/SSL connection disabled"));
838  return -1;
839  }
840 
841  if (C_SslCiphers)
842  {
843  mutt_error(
844  _("Explicit ciphersuite selection via $ssl_ciphers not supported"));
845  }
846  if (certerr & CERTERR_INSECUREALG)
847  {
848  row++;
849  strfcpy(menu->dialog[row], _("Warning: Server certificate was signed using an insecure algorithm"),
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 
870 static 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  gnutls_certificate_set_x509_trust_file(data->xcred, C_CertificateFile, GNUTLS_X509_FMT_PEM);
883  /* ignore errors, maybe file doesn't exist yet */
884 
886  {
887  gnutls_certificate_set_x509_trust_file(data->xcred, C_SslCaCertificatesFile,
888  GNUTLS_X509_FMT_PEM);
889  }
890 
891  if (C_SslClientCert)
892  {
893  mutt_debug(LL_DEBUG2, "Using client certificate %s\n", C_SslClientCert);
894  gnutls_certificate_set_x509_key_file(data->xcred, C_SslClientCert,
895  C_SslClientCert, GNUTLS_X509_FMT_PEM);
896  }
897 
898 #ifdef HAVE_DECL_GNUTLS_VERIFY_DISABLE_TIME_CHECKS
899  /* disable checking certificate activation/expiration times
900  * in gnutls, we do the checks ourselves */
901  gnutls_certificate_set_verify_flags(data->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
902 #endif
903 
904  err = gnutls_init(&data->state, GNUTLS_CLIENT);
905  if (err)
906  {
907  mutt_error("gnutls_handshake: %s", gnutls_strerror(err));
908  goto fail;
909  }
910 
911  /* set socket */
912  gnutls_transport_set_ptr(data->state, (gnutls_transport_ptr_t)(long) conn->fd);
913 
914  if (gnutls_server_name_set(data->state, GNUTLS_NAME_DNS, conn->account.host,
915  mutt_str_len(conn->account.host)))
916  {
917  mutt_error(_("Warning: unable to set TLS SNI host name"));
918  }
919 
920  if (tls_set_priority(data) < 0)
921  {
922  goto fail;
923  }
924 
925  if (C_SslMinDhPrimeBits > 0)
926  {
927  gnutls_dh_set_prime_bits(data->state, C_SslMinDhPrimeBits);
928  }
929 
930  /* gnutls_set_cred (data->state, GNUTLS_ANON, NULL); */
931 
932  gnutls_credentials_set(data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
933 
934  err = gnutls_handshake(data->state);
935 
936  while (err == GNUTLS_E_AGAIN)
937  {
938  err = gnutls_handshake(data->state);
939  }
940  if (err < 0)
941  {
942  if (err == GNUTLS_E_FATAL_ALERT_RECEIVED)
943  {
944  mutt_error("gnutls_handshake: %s(%s)", gnutls_strerror(err),
945  gnutls_alert_get_name(gnutls_alert_get(data->state)));
946  }
947  else
948  {
949  mutt_error("gnutls_handshake: %s", gnutls_strerror(err));
950  }
951  goto fail;
952  }
953 
954  if (tls_check_certificate(conn) == 0)
955  goto fail;
956 
957  /* set Security Strength Factor (SSF) for SASL */
958  /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
959  conn->ssf = gnutls_cipher_get_key_size(gnutls_cipher_get(data->state)) * 8;
960 
961  tls_get_client_cert(conn);
962 
963  if (!OptNoCurses)
964  {
965  mutt_message(_("SSL/TLS connection using %s (%s/%s/%s)"),
966  gnutls_protocol_get_name(gnutls_protocol_get_version(data->state)),
967  gnutls_kx_get_name(gnutls_kx_get(data->state)),
968  gnutls_cipher_get_name(gnutls_cipher_get(data->state)),
969  gnutls_mac_get_name(gnutls_mac_get(data->state)));
970  mutt_sleep(0);
971  }
972 
973  return 0;
974 
975 fail:
976  gnutls_certificate_free_credentials(data->xcred);
977  gnutls_deinit(data->state);
978  FREE(&conn->sockdata);
979  return -1;
980 }
981 
985 static int tls_socket_poll(struct Connection *conn, time_t wait_secs)
986 {
987  struct TlsSockData *data = conn->sockdata;
988  if (!data)
989  return -1;
990 
991  if (gnutls_record_check_pending(data->state))
992  return 1;
993 
994  return raw_socket_poll(conn, wait_secs);
995 }
996 
1000 static int tls_socket_close(struct Connection *conn)
1001 {
1002  struct TlsSockData *data = conn->sockdata;
1003  if (data)
1004  {
1005  /* shut down only the write half to avoid hanging waiting for the remote to respond.
1006  *
1007  * RFC5246 7.2.1. "Closure Alerts"
1008  *
1009  * It is not required for the initiator of the close to wait for the
1010  * responding close_notify alert before closing the read side of the
1011  * connection. */
1012  gnutls_bye(data->state, GNUTLS_SHUT_WR);
1013 
1014  gnutls_certificate_free_credentials(data->xcred);
1015  gnutls_deinit(data->state);
1016  FREE(&conn->sockdata);
1017  }
1018 
1019  return raw_socket_close(conn);
1020 }
1021 
1025 static int tls_socket_open(struct Connection *conn)
1026 {
1027  if (raw_socket_open(conn) < 0)
1028  return -1;
1029 
1030  if (tls_negotiate(conn) < 0)
1031  {
1032  tls_socket_close(conn);
1033  return -1;
1034  }
1035 
1036  return 0;
1037 }
1038 
1042 static int tls_socket_read(struct Connection *conn, char *buf, size_t count)
1043 {
1044  struct TlsSockData *data = conn->sockdata;
1045  if (!data)
1046  {
1047  mutt_error(_("Error: no TLS socket open"));
1048  return -1;
1049  }
1050 
1051  int rc;
1052  do
1053  {
1054  rc = gnutls_record_recv(data->state, buf, count);
1055  } while ((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED));
1056 
1057  if (rc < 0)
1058  {
1059  mutt_error("tls_socket_read (%s)", gnutls_strerror(rc));
1060  return -1;
1061  }
1062 
1063  return rc;
1064 }
1065 
1069 static int tls_socket_write(struct Connection *conn, const char *buf, size_t count)
1070 {
1071  struct TlsSockData *data = conn->sockdata;
1072  size_t sent = 0;
1073 
1074  if (!data)
1075  {
1076  mutt_error(_("Error: no TLS socket open"));
1077  return -1;
1078  }
1079 
1080  do
1081  {
1082  int ret;
1083  do
1084  {
1085  ret = gnutls_record_send(data->state, buf + sent, count - sent);
1086  } while ((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED));
1087 
1088  if (ret < 0)
1089  {
1090  mutt_error("tls_socket_write (%s)", gnutls_strerror(ret));
1091  return -1;
1092  }
1093 
1094  sent += ret;
1095  } while (sent < count);
1096 
1097  return sent;
1098 }
1099 
1103 static int tls_starttls_close(struct Connection *conn)
1104 {
1105  int rc;
1106 
1107  rc = tls_socket_close(conn);
1108  conn->read = raw_socket_read;
1109  conn->write = raw_socket_write;
1110  conn->close = raw_socket_close;
1111  conn->poll = raw_socket_poll;
1112 
1113  return rc;
1114 }
1115 
1123 {
1124  if (tls_init() < 0)
1125  return -1;
1126 
1127  conn->open = tls_socket_open;
1128  conn->read = tls_socket_read;
1129  conn->write = tls_socket_write;
1130  conn->close = tls_socket_close;
1131  conn->poll = tls_socket_poll;
1132 
1133  return 0;
1134 }
1135 
1143 {
1144  if (tls_init() < 0)
1145  return -1;
1146 
1147  if (tls_negotiate(conn) < 0)
1148  return -1;
1149 
1150  conn->read = tls_socket_read;
1151  conn->write = tls_socket_write;
1152  conn->close = tls_starttls_close;
1153  conn->poll = tls_socket_poll;
1154 
1155  return 0;
1156 }
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:414
const char * C_CertificateFile
Config: File containing trusted certificates.
Definition: conn_globals.c:37
static regoff_t mutt_regmatch_end(const regmatch_t *match)
Return the end of a match.
Definition: regex3.h:70
int raw_socket_close(struct Connection *conn)
Close a socket - Implements Connection::close()
Definition: raw.c:368
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
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
const char * C_SslCaCertificatesFile
Config: File containing trusted CA certificates.
Definition: conn_globals.c:51
#define CERTERR_INSECUREALG
Definition: gnutls.c:54
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:36
unsigned int ssf
Security strength factor, in bits (see below)
Definition: connection.h:37
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:157
int dlg_verify_cert(const char *title, struct ListHead *list, bool allow_always, bool allow_skip)
Ask the user to validate the certificate.
Definition: gui.c:58
int(* read)(struct Connection *conn, char *buf, size_t count)
Read from a socket Connection.
Definition: connection.h:72
bool C_SslUseTlsv11
Config: (ssl) Use TLSv1.1 for authentication.
Definition: conn_globals.c:43
#define mutt_message(...)
Definition: logging.h:83
const char * C_SslCiphers
Config: Ciphers to use when using SSL.
Definition: conn_globals.c:39
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
void * sockdata
Backend-specific socket data.
Definition: connection.h:42
static int tls_compare_certificates(const gnutls_datum_t *peercert)
Compare certificates against C_CertificateFile.
Definition: gnutls.c:232
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:1069
static int tls_negotiate(struct Connection *conn)
Negotiate TLS connection.
Definition: gnutls.c:870
static void tls_get_client_cert(struct Connection *conn)
Get the client certificate for a TLS connection.
Definition: gnutls.c:694
An open network connection (socket)
Definition: connection.h:34
const char * C_SslClientCert
Config: File containing client certificates.
Definition: conn_globals.c:40
String manipulation buffer.
Definition: buffer.h:33
bool C_SslVerifyDates
Config: (ssl) Verify the dates on the server certificate.
Definition: conn_globals.c:46
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:375
#define CERTERR_REVOKED
Definition: gnutls.c:50
#define _(a)
Definition: message.h:28
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, int flags)
Read a line from a file.
Definition: file.c:667
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:462
WHERE bool OptNoCurses
(pseudo) when sending in batch mode
Definition: options.h:48
#H [foo.com] A76D ... 65B1
Definition: prex.h:109
int raw_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block - Implements Connection::poll()
Definition: raw.c:330
gnutls_session_t state
Definition: gnutls.c:80
static int tls_set_priority(struct TlsSockData *data)
Set the priority of various protocols.
Definition: gnutls.c:821
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:613
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:417
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:1042
int(* open)(struct Connection *conn)
Note about ssf: in actuality, NeoMutt uses this as a boolean to determine if the connection is "secur...
Definition: connection.h:62
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_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:307
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1536
int mutt_date_make_tls(char *buf, size_t buflen, time_t timestamp)
Format date in TLS certificate verification style.
Definition: date.c:570
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:60
gnutls_certificate_credentials_t xcred
Definition: gnutls.c:81
int raw_socket_write(struct Connection *conn, const char *buf, size_t count)
Write data to a socket - Implements Connection::write()
Definition: raw.c:300
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
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:985
#define CERTERR_EXPIRED
Definition: gnutls.c:48
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
bool C_SslUseTlsv12
Config: (ssl) Use TLSv1.2 for authentication.
Definition: conn_globals.c:44
#define mutt_b2s(buf)
Definition: buffer.h:41
int(* close)(struct Connection *conn)
Close a socket Connection.
Definition: connection.h:100
TLS socket data.
Definition: gnutls.c:78
regmatch_t * mutt_prex_capture(enum Prex which, const char *str)
match a precompiled regex against a string
Definition: prex.c:306
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:39
int fd
Socket file descriptor.
Definition: connection.h:40
static int tls_starttls_close(struct Connection *conn)
Close a TLS connection - Implements Connection::close()
Definition: gnutls.c:1103
int raw_socket_read(struct Connection *conn, char *buf, size_t len)
Read data from a socket - Implements Connection::read()
Definition: raw.c:270
static regoff_t mutt_regmatch_start(const regmatch_t *match)
Return the start of a match.
Definition: regex3.h:60
#define CERTERR_NOTYETVALID
Definition: gnutls.c:49
bool C_SslUseSslv3
Config: (ssl) INSECURE: Use SSLv3 for authentication.
Definition: conn_globals.c:41
#define CERTERR_HOSTNAME
Definition: gnutls.c:52
static int protocol_priority[]
Definition: gnutls.c:71
int raw_socket_open(struct Connection *conn)
Open a socket - Implements Connection::open()
Definition: raw.c:124
static int tls_socket_open(struct Connection *conn)
Open a TLS socket - Implements Connection::open()
Definition: gnutls.c:1025
bool C_SslUseTlsv13
Config: (ssl) Use TLSv1.3 for authentication.
Definition: conn_globals.c:45
const int dialog_row_len
Definition: gnutls.c:58
static int tls_init(void)
Set up Gnu TLS.
Definition: gnutls.c:89
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
short C_SslMinDhPrimeBits
Config: Minimum keysize for Diffie-Hellman key exchange.
Definition: conn_globals.c:52
#define CERTERR_VALID
Definition: gnutls.c:47
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:390
[#H foo.com A76D 954B EB79 1F49 5B3A 0A0E 0681 65B1]
Definition: prex.h:36
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:639
bool C_SslUseTlsv1
Config: (ssl) Use TLSv1 for authentication.
Definition: conn_globals.c:42
int(* poll)(struct Connection *conn, time_t wait_secs)
Check whether a socket read would block.
Definition: connection.h:92
Log at debug level 1.
Definition: logging.h:40
int n
Definition: acutest.h:492
int mutt_ssl_socket_setup(struct Connection *conn)
Set up SSL socket mulitplexor.
Definition: gnutls.c:1122
#define mutt_error(...)
Definition: logging.h:84
int(* write)(struct Connection *conn, const char *buf, size_t count)
Write to a socket Connection.
Definition: connection.h:82
bool C_SslVerifyHost
Config: (ssl) Verify the server&#39;s hostname against the certificate.
Definition: conn_globals.c:47
#define FREE(x)
Definition: memory.h:40
Handling of SSL encryption.
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:321
#define CERTERR_NOTTRUSTED
Definition: gnutls.c:51
Handling of global boolean variables.
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1103
int const char int line
Definition: acutest.h:617
static int tls_check_stored_hostname(const gnutls_datum_t *cert, const char *hostname)
Does the hostname match a stored certificate?
Definition: gnutls.c:188
Convenience wrapper for the library headers.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:588
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: gnutls.c:1142
#define CERTERR_OTHER
Definition: gnutls.c:55
static int tls_socket_close(struct Connection *conn)
Close a TLS socket - Implements Connection::close()
Definition: gnutls.c:1000
#H foo.com [A76D ... 65B1]
Definition: prex.h:110
#define CERTERR_SIGNERNOTCA
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:119