NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
openssl.c
Go to the documentation of this file.
1 
29 #include "config.h"
30 #include <errno.h>
31 #include <openssl/asn1.h>
32 #include <openssl/bio.h>
33 #include <openssl/err.h>
34 #include <openssl/evp.h>
35 #include <openssl/obj_mac.h>
36 #include <openssl/opensslv.h>
37 #include <openssl/ossl_typ.h>
38 #include <openssl/pem.h>
39 #include <openssl/rand.h>
40 #include <openssl/safestack.h>
41 #include <openssl/ssl.h>
42 #include <openssl/x509.h>
43 #include <openssl/x509v3.h>
44 #include <stdbool.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <strings.h>
48 #include <sys/stat.h>
49 #include <time.h>
50 #include <unistd.h>
51 #include "private.h"
52 #include "mutt/lib.h"
53 #include "address/lib.h"
54 #include "config/lib.h"
55 #include "core/lib.h"
56 #include "lib.h"
57 #include "mutt_logging.h"
58 #include "ssl.h"
59 #ifdef HAVE_RAND_EGD
60 #include "mutt_globals.h"
61 #endif
62 
63 /* LibreSSL defines OPENSSL_VERSION_NUMBER but sets it to 0x20000000L.
64  * So technically we don't need the defined(OPENSSL_VERSION_NUMBER) check. */
65 #if (defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x10100000L)) || \
66  (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2070000fL))
67 #define X509_get0_notBefore X509_get_notBefore
68 #define X509_get0_notAfter X509_get_notAfter
69 #define X509_getm_notBefore X509_get_notBefore
70 #define X509_getm_notAfter X509_get_notAfter
71 #define X509_STORE_CTX_get0_chain X509_STORE_CTX_get_chain
72 #define SSL_has_pending SSL_pending
73 #endif
74 
75 /* Unimplemented OpenSSL 1.1 api calls */
76 #if (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x2070000fL))
77 #define SSL_has_pending SSL_pending
78 #endif
79 
80 /* index for storing hostname as application specific data in SSL structure */
81 static int HostExDataIndex = -1;
82 
83 /* Index for storing the "skip mode" state in SSL structure. When the
84  * user skips a certificate in the chain, the stored value will be
85  * non-null. */
86 static int SkipModeExDataIndex = -1;
87 
88 /* keep a handle on accepted certificates in case we want to
89  * open up another connection to the same server in this session */
90 static STACK_OF(X509) *SslSessionCerts = NULL;
91 
92 static int ssl_socket_close(struct Connection *conn);
93 
97 struct SslSockData
98 {
99  SSL_CTX *sctx;
100  SSL *ssl;
101  unsigned char isopen;
102 };
103 
116 static int ssl_load_certificates(SSL_CTX *ctx)
117 {
118  int rc = 1;
119 
120  mutt_debug(LL_DEBUG2, "loading trusted certificates\n");
121  X509_STORE *store = SSL_CTX_get_cert_store(ctx);
122  if (!store)
123  {
124  store = X509_STORE_new();
125  SSL_CTX_set_cert_store(ctx, store);
126  }
127 
128  const char *const c_certificate_file =
129  cs_subset_path(NeoMutt->sub, "certificate_file");
130  FILE *fp = mutt_file_fopen(c_certificate_file, "rt");
131  if (!fp)
132  return 0;
133 
134  X509 *cert = NULL;
135  while (NULL != PEM_read_X509(fp, &cert, NULL, NULL))
136  {
137  if ((X509_cmp_current_time(X509_get0_notBefore(cert)) >= 0) ||
138  (X509_cmp_current_time(X509_get0_notAfter(cert)) <= 0))
139  {
140  char buf[256];
141  mutt_debug(LL_DEBUG2, "filtering expired cert: %s\n",
142  X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)));
143  }
144  else
145  {
146  X509_STORE_add_cert(store, cert);
147  }
148  }
149  /* PEM_read_X509 sets the error NO_START_LINE on eof */
150  if (ERR_GET_REASON(ERR_peek_last_error()) != PEM_R_NO_START_LINE)
151  rc = 0;
152  ERR_clear_error();
153 
154  X509_free(cert);
155  mutt_file_fclose(&fp);
156 
157  return rc;
158 }
159 
166 static int ssl_set_verify_partial(SSL_CTX *ctx)
167 {
168  int rc = 0;
169 #ifdef HAVE_SSL_PARTIAL_CHAIN
170  X509_VERIFY_PARAM *param = NULL;
171 
172  const bool c_ssl_verify_partial_chains =
173  cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
174  if (c_ssl_verify_partial_chains)
175  {
176  param = X509_VERIFY_PARAM_new();
177  if (param)
178  {
179  X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_PARTIAL_CHAIN);
180  if (SSL_CTX_set1_param(ctx, param) == 0)
181  {
182  mutt_debug(LL_DEBUG2, "SSL_CTX_set1_param() failed\n");
183  rc = -1;
184  }
185  X509_VERIFY_PARAM_free(param);
186  }
187  else
188  {
189  mutt_debug(LL_DEBUG2, "X509_VERIFY_PARAM_new() failed\n");
190  rc = -1;
191  }
192  }
193 #endif
194  return rc;
195 }
196 
203 static int add_entropy(const char *file)
204 {
205  if (!file)
206  return 0;
207 
208  struct stat st;
209  int n = -1;
210 
211  if (stat(file, &st) == -1)
212  return (errno == ENOENT) ? 0 : -1;
213 
214  mutt_message(_("Filling entropy pool: %s..."), file);
215 
216  /* check that the file permissions are secure */
217  if ((st.st_uid != getuid()) || ((st.st_mode & (S_IWGRP | S_IRGRP)) != 0) ||
218  ((st.st_mode & (S_IWOTH | S_IROTH)) != 0))
219  {
220  mutt_error(_("%s has insecure permissions"), file);
221  return -1;
222  }
223 
224 #ifdef HAVE_RAND_EGD
225  n = RAND_egd(file);
226 #endif
227  if (n <= 0)
228  n = RAND_load_file(file, -1);
229 
230  return n;
231 }
232 
238 static void ssl_err(struct SslSockData *data, int err)
239 {
240  int e = SSL_get_error(data->ssl, err);
241  switch (e)
242  {
243  case SSL_ERROR_NONE:
244  return;
245  case SSL_ERROR_ZERO_RETURN:
246  data->isopen = 0;
247  break;
248  case SSL_ERROR_SYSCALL:
249  data->isopen = 0;
250  break;
251  }
252 
253  const char *errmsg = NULL;
254  unsigned long sslerr;
255 
256  switch (e)
257  {
258  case SSL_ERROR_SYSCALL:
259  errmsg = "I/O error";
260  break;
261  case SSL_ERROR_WANT_ACCEPT:
262  errmsg = "retry accept";
263  break;
264  case SSL_ERROR_WANT_CONNECT:
265  errmsg = "retry connect";
266  break;
267  case SSL_ERROR_WANT_READ:
268  errmsg = "retry read";
269  break;
270  case SSL_ERROR_WANT_WRITE:
271  errmsg = "retry write";
272  break;
273  case SSL_ERROR_WANT_X509_LOOKUP:
274  errmsg = "retry x509 lookup";
275  break;
276  case SSL_ERROR_ZERO_RETURN:
277  errmsg = "SSL connection closed";
278  break;
279  case SSL_ERROR_SSL:
280  sslerr = ERR_get_error();
281  switch (sslerr)
282  {
283  case 0:
284  switch (err)
285  {
286  case 0:
287  errmsg = "EOF";
288  break;
289  default:
290  errmsg = strerror(errno);
291  }
292  break;
293  default:
294  errmsg = ERR_error_string(sslerr, NULL);
295  }
296  break;
297  default:
298  errmsg = "unknown error";
299  }
300 
301  mutt_debug(LL_DEBUG1, "SSL error: %s\n", errmsg);
302 }
303 
307 static void ssl_dprint_err_stack(void)
308 {
309  BIO *bio = BIO_new(BIO_s_mem());
310  if (!bio)
311  return;
312  ERR_print_errors(bio);
313 
314  char *buf = NULL;
315  long buflen = BIO_get_mem_data(bio, &buf);
316  if (buflen > 0)
317  {
318  char *output = mutt_mem_malloc(buflen + 1);
319  memcpy(output, buf, buflen);
320  output[buflen] = '\0';
321  mutt_debug(LL_DEBUG1, "SSL error stack: %s\n", output);
322  FREE(&output);
323  }
324  BIO_free(bio);
325 }
326 
336 static int ssl_passwd_cb(char *buf, int buflen, int rwflag, void *userdata)
337 {
338  struct ConnAccount *cac = userdata;
339 
340  if (mutt_account_getuser(cac) < 0)
341  return 0;
342 
343  mutt_debug(LL_DEBUG2, "getting password for %s@%s:%u\n", cac->user, cac->host, cac->port);
344 
345  if (mutt_account_getpass(cac) < 0)
346  return 0;
347 
348  return snprintf(buf, buflen, "%s", cac->pass);
349 }
350 
355 static int ssl_socket_open_err(struct Connection *conn)
356 {
357  mutt_error(_("SSL disabled due to the lack of entropy"));
358  return -1;
359 }
360 
369 static char *x509_get_part(X509_NAME *name, int nid)
370 {
371  static char data[128];
372 
373  if (!name || (X509_NAME_get_text_by_NID(name, nid, data, sizeof(data)) < 0))
374  return NULL;
375 
376  return data;
377 }
378 
386 static void x509_fingerprint(char *s, int l, X509 *cert, const EVP_MD *(*hashfunc)(void) )
387 {
388  unsigned char md[EVP_MAX_MD_SIZE];
389  unsigned int n;
390 
391  if (X509_digest(cert, hashfunc(), md, &n) == 0) // Failure
392  {
393  snprintf(s, l, "%s", _("[unable to calculate]"));
394  }
395  else
396  {
397  for (unsigned int i = 0; i < n; i++)
398  {
399  char ch[8];
400  snprintf(ch, sizeof(ch), "%02X%s", md[i], ((i % 2) ? " " : ""));
401  mutt_str_cat(s, l, ch);
402  }
403  }
404 }
405 
413 static char *asn1time_to_string(ASN1_UTCTIME *tm)
414 {
415  static char buf[64];
416  BIO *bio = NULL;
417 
418  mutt_str_copy(buf, _("[invalid date]"), sizeof(buf));
419 
420  bio = BIO_new(BIO_s_mem());
421  if (bio)
422  {
423  if (ASN1_TIME_print(bio, tm))
424  (void) BIO_read(bio, buf, sizeof(buf));
425  BIO_free(bio);
426  }
427 
428  return buf;
429 }
430 
440 static bool compare_certificates(X509 *cert, X509 *peercert,
441  unsigned char *peermd, unsigned int peermdlen)
442 {
443  unsigned char md[EVP_MAX_MD_SIZE];
444  unsigned int mdlen;
445 
446  /* Avoid CPU-intensive digest calculation if the certificates are
447  * not even remotely equal. */
448  if ((X509_subject_name_cmp(cert, peercert) != 0) ||
449  (X509_issuer_name_cmp(cert, peercert) != 0))
450  {
451  return false;
452  }
453 
454  if (!X509_digest(cert, EVP_sha256(), md, &mdlen) || (peermdlen != mdlen))
455  return false;
456 
457  if (memcmp(peermd, md, mdlen) != 0)
458  return false;
459 
460  return true;
461 }
462 
470 static bool check_certificate_expiration(X509 *peercert, bool silent)
471 {
472  const bool c_ssl_verify_dates =
473  cs_subset_bool(NeoMutt->sub, "ssl_verify_dates");
474  if (c_ssl_verify_dates == MUTT_NO)
475  return true;
476 
477  if (X509_cmp_current_time(X509_get0_notBefore(peercert)) >= 0)
478  {
479  if (!silent)
480  {
481  mutt_debug(LL_DEBUG2, "Server certificate is not yet valid\n");
482  mutt_error(_("Server certificate is not yet valid"));
483  }
484  return false;
485  }
486 
487  if (X509_cmp_current_time(X509_get0_notAfter(peercert)) <= 0)
488  {
489  if (!silent)
490  {
491  mutt_debug(LL_DEBUG2, "Server certificate has expired\n");
492  mutt_error(_("Server certificate has expired"));
493  }
494  return false;
495  }
496 
497  return true;
498 }
499 
506 static bool hostname_match(const char *hostname, const char *certname)
507 {
508  const char *cmp1 = NULL, *cmp2 = NULL;
509 
510  if (mutt_strn_equal(certname, "*.", 2))
511  {
512  cmp1 = certname + 2;
513  cmp2 = strchr(hostname, '.');
514  if (!cmp2)
515  return false;
516 
517  cmp2++;
518  }
519  else
520  {
521  cmp1 = certname;
522  cmp2 = hostname;
523  }
524 
525  if ((*cmp1 == '\0') || (*cmp2 == '\0'))
526  {
527  return false;
528  }
529 
530  if (strcasecmp(cmp1, cmp2) != 0)
531  {
532  return false;
533  }
534 
535  return true;
536 }
537 
552 static int ssl_init(void)
553 {
554  static bool init_complete = false;
555 
556  if (init_complete)
557  return 0;
558 
559  if (RAND_status() != 1)
560  {
561  /* load entropy from files */
562  struct Buffer *path = mutt_buffer_pool_get();
563  const char *const c_entropy_file =
564  cs_subset_path(NeoMutt->sub, "entropy_file");
565  add_entropy(c_entropy_file);
566  add_entropy(RAND_file_name(path->data, path->dsize));
567 
568 /* load entropy from egd sockets */
569 #ifdef HAVE_RAND_EGD
570  add_entropy(mutt_str_getenv("EGDSOCKET"));
571  mutt_buffer_printf(path, "%s/.entropy", NONULL(HomeDir));
573  add_entropy(TMPDIR "/entropy");
574 #endif
575 
576  /* shuffle $RANDFILE (or ~/.rnd if unset) */
577  RAND_write_file(RAND_file_name(path->data, path->dsize));
579 
581  if (RAND_status() != 1)
582  {
583  mutt_error(_("Failed to find enough entropy on your system"));
584  return -1;
585  }
586  }
587 
588 /* OpenSSL performs automatic initialization as of 1.1.
589  * However LibreSSL does not (as of 2.8.3). */
590 #if (defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x10100000L)) || \
591  (defined(LIBRESSL_VERSION_NUMBER))
592  /* I don't think you can do this just before reading the error. The call
593  * itself might clobber the last SSL error. */
594  SSL_load_error_strings();
595  SSL_library_init();
596 #endif
597  init_complete = true;
598  return 0;
599 }
600 
606 static void ssl_get_client_cert(struct SslSockData *ssldata, struct Connection *conn)
607 {
608  const char *const c_ssl_client_cert =
609  cs_subset_path(NeoMutt->sub, "ssl_client_cert");
610  if (!c_ssl_client_cert)
611  return;
612 
613  mutt_debug(LL_DEBUG2, "Using client certificate %s\n", c_ssl_client_cert);
614  SSL_CTX_set_default_passwd_cb_userdata(ssldata->sctx, &conn->account);
615  SSL_CTX_set_default_passwd_cb(ssldata->sctx, ssl_passwd_cb);
616  SSL_CTX_use_certificate_file(ssldata->sctx, c_ssl_client_cert, SSL_FILETYPE_PEM);
617  SSL_CTX_use_PrivateKey_file(ssldata->sctx, c_ssl_client_cert, SSL_FILETYPE_PEM);
618 
619  /* if we are using a client cert, SASL may expect an external auth name */
620  if (mutt_account_getuser(&conn->account) < 0)
621  mutt_debug(LL_DEBUG1, "Couldn't get user info\n");
622 }
623 
627 static int ssl_socket_close_and_restore(struct Connection *conn)
628 {
629  int rc = ssl_socket_close(conn);
630  conn->read = raw_socket_read;
631  conn->write = raw_socket_write;
632  conn->close = raw_socket_close;
633  conn->poll = raw_socket_poll;
634 
635  return rc;
636 }
637 
643 static bool check_certificate_cache(X509 *peercert)
644 {
645  unsigned char peermd[EVP_MAX_MD_SIZE];
646  unsigned int peermdlen;
647  X509 *cert = NULL;
648 
649  if (!X509_digest(peercert, EVP_sha256(), peermd, &peermdlen) || !SslSessionCerts)
650  {
651  return false;
652  }
653 
654  for (int i = sk_X509_num(SslSessionCerts); i-- > 0;)
655  {
656  cert = sk_X509_value(SslSessionCerts, i);
657  if (compare_certificates(cert, peercert, peermd, peermdlen))
658  {
659  return true;
660  }
661  }
662 
663  return false;
664 }
665 
672 static bool check_certificate_file(X509 *peercert)
673 {
674  unsigned char peermd[EVP_MAX_MD_SIZE];
675  unsigned int peermdlen;
676  X509 *cert = NULL;
677  int pass = false;
678  FILE *fp = NULL;
679 
680  const char *const c_certificate_file =
681  cs_subset_path(NeoMutt->sub, "certificate_file");
682  fp = mutt_file_fopen(c_certificate_file, "rt");
683  if (!fp)
684  return false;
685 
686  if (!X509_digest(peercert, EVP_sha256(), peermd, &peermdlen))
687  {
688  mutt_file_fclose(&fp);
689  return false;
690  }
691 
692  while (PEM_read_X509(fp, &cert, NULL, NULL))
693  {
694  if (compare_certificates(cert, peercert, peermd, peermdlen) &&
695  check_certificate_expiration(cert, true))
696  {
697  pass = true;
698  break;
699  }
700  }
701  /* PEM_read_X509 sets an error on eof */
702  if (!pass)
703  ERR_clear_error();
704  X509_free(cert);
705  mutt_file_fclose(&fp);
706 
707  return pass;
708 }
709 
719 static int check_host(X509 *x509cert, const char *hostname, char *err, size_t errlen)
720 {
721  int rc = 0;
722  /* hostname in ASCII format: */
723  char *hostname_ascii = NULL;
724  /* needed to get the common name: */
725  X509_NAME *x509_subject = NULL;
726  char *buf = NULL;
727  int bufsize;
728  /* needed to get the DNS subjectAltNames: */
729  STACK_OF(GENERAL_NAME) * subj_alt_names;
730  int subj_alt_names_count;
731  GENERAL_NAME *subj_alt_name = NULL;
732  /* did we find a name matching hostname? */
733  bool match_found;
734 
735  /* Check if 'hostname' matches the one of the subjectAltName extensions of
736  * type DNS or the Common Name (CN). */
737 
738 #ifdef HAVE_LIBIDN
739  if (mutt_idna_to_ascii_lz(hostname, &hostname_ascii, 0) != 0)
740  {
741  hostname_ascii = mutt_str_dup(hostname);
742  }
743 #else
744  hostname_ascii = mutt_str_dup(hostname);
745 #endif
746 
747  /* Try the DNS subjectAltNames. */
748  match_found = false;
749  subj_alt_names = X509_get_ext_d2i(x509cert, NID_subject_alt_name, NULL, NULL);
750  if (subj_alt_names)
751  {
752  subj_alt_names_count = sk_GENERAL_NAME_num(subj_alt_names);
753  for (int i = 0; i < subj_alt_names_count; i++)
754  {
755  subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
756  if (subj_alt_name->type == GEN_DNS)
757  {
758  if ((subj_alt_name->d.ia5->length >= 0) &&
759  (mutt_str_len((char *) subj_alt_name->d.ia5->data) ==
760  (size_t) subj_alt_name->d.ia5->length) &&
761  (match_found = hostname_match(hostname_ascii,
762  (char *) (subj_alt_name->d.ia5->data))))
763  {
764  break;
765  }
766  }
767  }
768  GENERAL_NAMES_free(subj_alt_names);
769  }
770 
771  if (!match_found)
772  {
773  /* Try the common name */
774  x509_subject = X509_get_subject_name(x509cert);
775  if (!x509_subject)
776  {
777  if (err && errlen)
778  mutt_str_copy(err, _("can't get certificate subject"), errlen);
779  goto out;
780  }
781 
782  /* first get the space requirements */
783  bufsize = X509_NAME_get_text_by_NID(x509_subject, NID_commonName, NULL, 0);
784  if (bufsize == -1)
785  {
786  if (err && errlen)
787  mutt_str_copy(err, _("can't get certificate common name"), errlen);
788  goto out;
789  }
790  bufsize++; /* space for the terminal nul char */
791  buf = mutt_mem_malloc((size_t) bufsize);
792  if (X509_NAME_get_text_by_NID(x509_subject, NID_commonName, buf, bufsize) == -1)
793  {
794  if (err && errlen)
795  mutt_str_copy(err, _("can't get certificate common name"), errlen);
796  goto out;
797  }
798  /* cast is safe since bufsize is incremented above, so bufsize-1 is always
799  * zero or greater. */
800  if (mutt_str_len(buf) == (size_t) bufsize - 1)
801  {
802  match_found = hostname_match(hostname_ascii, buf);
803  }
804  }
805 
806  if (!match_found)
807  {
808  if (err && errlen)
809  snprintf(err, errlen, _("certificate owner does not match hostname %s"), hostname);
810  goto out;
811  }
812 
813  rc = 1;
814 
815 out:
816  FREE(&buf);
817  FREE(&hostname_ascii);
818 
819  return rc;
820 }
821 
828 static bool check_certificate_by_digest(X509 *peercert)
829 {
830  return check_certificate_expiration(peercert, false) && check_certificate_file(peercert);
831 }
832 
839 static int ssl_cache_trusted_cert(X509 *c)
840 {
841  mutt_debug(LL_DEBUG1, "trusted\n");
842  if (!SslSessionCerts)
843  SslSessionCerts = sk_X509_new_null();
844  return sk_X509_push(SslSessionCerts, X509_dup(c));
845 }
846 
854 static void add_cert(const char *title, X509 *cert, bool issuer, struct ListHead *list)
855 {
856  static const int part[] = {
857  NID_commonName, // CN
858  NID_pkcs9_emailAddress, // Email
859  NID_organizationName, // O
860  NID_organizationalUnitName, // OU
861  NID_localityName, // L
862  NID_stateOrProvinceName, // ST
863  NID_countryName, // C
864  };
865 
866  X509_NAME *x509 = NULL;
867  if (issuer)
868  x509 = X509_get_issuer_name(cert);
869  else
870  x509 = X509_get_subject_name(cert);
871 
872  // Allocate formatted strings and let the ListHead take ownership
873  mutt_list_insert_tail(list, mutt_str_dup(title));
874 
875  char *line = NULL;
876  char *text = NULL;
877  for (size_t i = 0; i < mutt_array_size(part); i++)
878  {
879  text = x509_get_part(x509, part[i]);
880  if (text)
881  {
882  mutt_str_asprintf(&line, " %s", text);
883  mutt_list_insert_tail(list, line);
884  }
885  }
886 }
887 
898 static bool interactive_check_cert(X509 *cert, int idx, size_t len, SSL *ssl, bool allow_always)
899 {
900  char buf[256];
901  struct ListHead list = STAILQ_HEAD_INITIALIZER(list);
902 
903  add_cert(_("This certificate belongs to:"), cert, false, &list);
904  mutt_list_insert_tail(&list, NULL);
905  add_cert(_("This certificate was issued by:"), cert, true, &list);
906 
907  char *line = NULL;
908  mutt_list_insert_tail(&list, NULL);
909  mutt_list_insert_tail(&list, mutt_str_dup(_("This certificate is valid")));
910  mutt_str_asprintf(&line, _(" from %s"), asn1time_to_string(X509_getm_notBefore(cert)));
911  mutt_list_insert_tail(&list, line);
912  mutt_str_asprintf(&line, _(" to %s"), asn1time_to_string(X509_getm_notAfter(cert)));
913  mutt_list_insert_tail(&list, line);
914 
915  mutt_list_insert_tail(&list, NULL);
916  buf[0] = '\0';
917  x509_fingerprint(buf, sizeof(buf), cert, EVP_sha1);
918  mutt_str_asprintf(&line, _("SHA1 Fingerprint: %s"), buf);
919  mutt_list_insert_tail(&list, line);
920  buf[0] = '\0';
921  buf[40] = '\0'; /* Ensure the second printed line is null terminated */
922  x509_fingerprint(buf, sizeof(buf), cert, EVP_sha256);
923  buf[39] = '\0'; /* Divide into two lines of output */
924  mutt_str_asprintf(&line, "%s%s", _("SHA256 Fingerprint: "), buf);
925  mutt_list_insert_tail(&list, line);
926  mutt_str_asprintf(&line, "%*s%s",
927  (int) mutt_str_len(_("SHA256 Fingerprint: ")), "", buf + 40);
928  mutt_list_insert_tail(&list, line);
929 
930  bool allow_skip = false;
931 /* The leaf/host certificate can't be skipped. */
932 #ifdef HAVE_SSL_PARTIAL_CHAIN
933  const bool c_ssl_verify_partial_chains =
934  cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
935  if ((idx != 0) && c_ssl_verify_partial_chains)
936  allow_skip = true;
937 #endif
938 
939  char title[256];
940  snprintf(title, sizeof(title),
941  _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
942 
943  /* Inside ssl_verify_callback(), this function is guarded by a call to
944  * check_certificate_by_digest(). This means if check_certificate_expiration() is
945  * true, then check_certificate_file() must be false. Therefore we don't need
946  * to also scan the certificate file here. */
947  const char *const c_certificate_file =
948  cs_subset_path(NeoMutt->sub, "certificate_file");
949  allow_always = allow_always && c_certificate_file &&
950  check_certificate_expiration(cert, true);
951 
952  int rc = dlg_verify_certificate(title, &list, allow_always, allow_skip);
953  if ((rc == 3) && !allow_always)
954  rc = 4;
955 
956  switch (rc)
957  {
958  case 1: // Reject
959  break;
960  case 2: // Once
961  SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
963  break;
964  case 3: // Always
965  {
966  bool saved = false;
967  FILE *fp = mutt_file_fopen(c_certificate_file, "a");
968  if (fp)
969  {
970  if (PEM_write_X509(fp, cert))
971  saved = true;
972  mutt_file_fclose(&fp);
973  }
974 
975  if (saved)
976  mutt_message(_("Certificate saved"));
977  else
978  mutt_error(_("Warning: Couldn't save certificate"));
979 
980  SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
982  break;
983  }
984  case 4: // Skip
985  SSL_set_ex_data(ssl, SkipModeExDataIndex, &SkipModeExDataIndex);
986  break;
987  }
988 
989  mutt_list_free(&list);
990  return (rc > 1);
991 }
992 
1004 static int ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
1005 {
1006  char buf[256];
1007 
1008  SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
1009  if (!ssl)
1010  {
1012  "failed to retrieve SSL structure from X509_STORE_CTX\n");
1013  return false;
1014  }
1015  const char *host = SSL_get_ex_data(ssl, HostExDataIndex);
1016  if (!host)
1017  {
1018  mutt_debug(LL_DEBUG1, "failed to retrieve hostname from SSL structure\n");
1019  return false;
1020  }
1021 
1022  /* This is true when a previous entry in the certificate chain did
1023  * not verify and the user manually chose to skip it via the
1024  * $ssl_verify_partial_chains option.
1025  * In this case, all following certificates need to be treated as non-verified
1026  * until one is actually verified. */
1027  bool skip_mode = (SSL_get_ex_data(ssl, SkipModeExDataIndex));
1028 
1029  X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
1030  int pos = X509_STORE_CTX_get_error_depth(ctx);
1031  size_t len = sk_X509_num(X509_STORE_CTX_get0_chain(ctx));
1032 
1033  mutt_debug(LL_DEBUG1, "checking cert chain entry %s (preverify: %d skipmode: %d)\n",
1034  X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)),
1035  preverify_ok, skip_mode);
1036 
1037 #ifdef HAVE_SSL_PARTIAL_CHAIN
1038  /* Sometimes, when a certificate is (s)kipped, OpenSSL will pass it
1039  * a second time with preverify_ok = 1. Don't show it or the user
1040  * will think their "s" key is broken. */
1041  const bool c_ssl_verify_partial_chains =
1042  cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
1043  if (c_ssl_verify_partial_chains)
1044  {
1045  static int last_pos = 0;
1046  static X509 *last_cert = NULL;
1047  if (skip_mode && preverify_ok && (pos == last_pos) && last_cert)
1048  {
1049  unsigned char last_cert_md[EVP_MAX_MD_SIZE];
1050  unsigned int last_cert_mdlen;
1051  if (X509_digest(last_cert, EVP_sha256(), last_cert_md, &last_cert_mdlen) &&
1052  compare_certificates(cert, last_cert, last_cert_md, last_cert_mdlen))
1053  {
1054  mutt_debug(LL_DEBUG2, "ignoring duplicate skipped certificate\n");
1055  return true;
1056  }
1057  }
1058 
1059  last_pos = pos;
1060  if (last_cert)
1061  X509_free(last_cert);
1062  last_cert = X509_dup(cert);
1063  }
1064 #endif
1065 
1066  /* check session cache first */
1067  if (check_certificate_cache(cert))
1068  {
1069  mutt_debug(LL_DEBUG2, "using cached certificate\n");
1070  SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
1071  return true;
1072  }
1073 
1074  /* check hostname only for the leaf certificate */
1075  buf[0] = '\0';
1076  const bool c_ssl_verify_host =
1077  cs_subset_bool(NeoMutt->sub, "ssl_verify_host");
1078  if ((pos == 0) && (c_ssl_verify_host != MUTT_NO))
1079  {
1080  if (check_host(cert, host, buf, sizeof(buf)) == 0)
1081  {
1082  mutt_error(_("Certificate host check failed: %s"), buf);
1083  /* we disallow (a)ccept always in the prompt, because it will have no effect
1084  * for hostname mismatches. */
1085  return interactive_check_cert(cert, pos, len, ssl, false);
1086  }
1087  mutt_debug(LL_DEBUG2, "hostname check passed\n");
1088  }
1089 
1090  if (!preverify_ok || skip_mode)
1091  {
1092  /* automatic check from user's database */
1093  const char *const c_certificate_file =
1094  cs_subset_path(NeoMutt->sub, "certificate_file");
1095  if (c_certificate_file && check_certificate_by_digest(cert))
1096  {
1097  mutt_debug(LL_DEBUG2, "digest check passed\n");
1098  SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
1099  return true;
1100  }
1101 
1102  /* log verification error */
1103  int err = X509_STORE_CTX_get_error(ctx);
1104  snprintf(buf, sizeof(buf), "%s (%d)", X509_verify_cert_error_string(err), err);
1105  mutt_debug(LL_DEBUG2, "X509_verify_cert: %s\n", buf);
1106 
1107  /* prompt user */
1108  return interactive_check_cert(cert, pos, len, ssl, true);
1109  }
1110 
1111  return true;
1112 }
1113 
1124 static int ssl_negotiate(struct Connection *conn, struct SslSockData *ssldata)
1125 {
1126  int err;
1127  const char *errmsg = NULL;
1128 
1129  HostExDataIndex = SSL_get_ex_new_index(0, "host", NULL, NULL, NULL);
1130  if (HostExDataIndex == -1)
1131  {
1133  "#1 failed to get index for application specific data\n");
1134  return -1;
1135  }
1136 
1137  if (!SSL_set_ex_data(ssldata->ssl, HostExDataIndex, conn->account.host))
1138  {
1139  mutt_debug(LL_DEBUG1, "#2 failed to save hostname in SSL structure\n");
1140  return -1;
1141  }
1142 
1143  SkipModeExDataIndex = SSL_get_ex_new_index(0, "skip", NULL, NULL, NULL);
1144  if (SkipModeExDataIndex == -1)
1145  {
1147  "#3 failed to get index for application specific data\n");
1148  return -1;
1149  }
1150 
1151  if (!SSL_set_ex_data(ssldata->ssl, SkipModeExDataIndex, NULL))
1152  {
1153  mutt_debug(LL_DEBUG1, "#4 failed to save skip mode in SSL structure\n");
1154  return -1;
1155  }
1156 
1157  SSL_set_verify(ssldata->ssl, SSL_VERIFY_PEER, ssl_verify_callback);
1158  SSL_set_mode(ssldata->ssl, SSL_MODE_AUTO_RETRY);
1159 
1160  if (!SSL_set_tlsext_host_name(ssldata->ssl, conn->account.host))
1161  {
1162  /* L10N: This is a warning when trying to set the host name for
1163  TLS Server Name Indication (SNI). This allows the server to present
1164  the correct certificate if it supports multiple hosts. */
1165  mutt_error(_("Warning: unable to set TLS SNI host name"));
1166  }
1167 
1168  ERR_clear_error();
1169 
1170  err = SSL_connect(ssldata->ssl);
1171  if (err != 1)
1172  {
1173  switch (SSL_get_error(ssldata->ssl, err))
1174  {
1175  case SSL_ERROR_SYSCALL:
1176  errmsg = _("I/O error");
1177  break;
1178  case SSL_ERROR_SSL:
1179  errmsg = ERR_error_string(ERR_get_error(), NULL);
1180  break;
1181  default:
1182  errmsg = _("unknown error");
1183  }
1184 
1185  mutt_error(_("SSL failed: %s"), errmsg);
1186 
1187  return -1;
1188  }
1189 
1190  return 0;
1191 }
1192 
1198 static inline struct SslSockData *sockdata(struct Connection *conn)
1199 {
1200  return conn->sockdata;
1201 }
1202 
1209 static int ssl_setup(struct Connection *conn)
1210 {
1211  int maxbits = 0;
1212 
1213  conn->sockdata = mutt_mem_calloc(1, sizeof(struct SslSockData));
1214 
1215  sockdata(conn)->sctx = SSL_CTX_new(SSLv23_client_method());
1216  if (!sockdata(conn)->sctx)
1217  {
1218  /* L10N: an SSL context is a data structure returned by the OpenSSL
1219  function SSL_CTX_new(). In this case it returned NULL: an
1220  error condition. */
1221  mutt_error(_("Unable to create SSL context"));
1223  goto free_ssldata;
1224  }
1225 
1226  /* disable SSL protocols as needed */
1227 #ifdef SSL_OP_NO_TLSv1_3
1228  const bool c_ssl_use_tlsv1_3 =
1229  cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_3");
1230  if (!c_ssl_use_tlsv1_3)
1231  SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_3);
1232 #endif
1233 
1234 #ifdef SSL_OP_NO_TLSv1_2
1235  const bool c_ssl_use_tlsv1_2 =
1236  cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_2");
1237  if (!c_ssl_use_tlsv1_2)
1238  SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_2);
1239 #endif
1240 
1241 #ifdef SSL_OP_NO_TLSv1_1
1242  const bool c_ssl_use_tlsv1_1 =
1243  cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_1");
1244  if (!c_ssl_use_tlsv1_1)
1245  SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_1);
1246 #endif
1247 
1248 #ifdef SSL_OP_NO_TLSv1
1249  const bool c_ssl_use_tlsv1 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1");
1250  if (!c_ssl_use_tlsv1)
1251  SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1);
1252 #endif
1253 
1254  const bool c_ssl_use_sslv3 = cs_subset_bool(NeoMutt->sub, "ssl_use_sslv3");
1255  if (!c_ssl_use_sslv3)
1256  SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_SSLv3);
1257 
1258  const bool c_ssl_use_sslv2 = cs_subset_bool(NeoMutt->sub, "ssl_use_sslv2");
1259  if (!c_ssl_use_sslv2)
1260  SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_SSLv2);
1261 
1262  const bool c_ssl_use_system_certs =
1263  cs_subset_bool(NeoMutt->sub, "ssl_use_system_certs");
1264  if (c_ssl_use_system_certs)
1265  {
1266  if (!SSL_CTX_set_default_verify_paths(sockdata(conn)->sctx))
1267  {
1268  mutt_debug(LL_DEBUG1, "Error setting default verify paths\n");
1269  goto free_ctx;
1270  }
1271  }
1272 
1273  const char *const c_certificate_file =
1274  cs_subset_path(NeoMutt->sub, "certificate_file");
1275  if (c_certificate_file && !ssl_load_certificates(sockdata(conn)->sctx))
1276  mutt_debug(LL_DEBUG1, "Error loading trusted certificates\n");
1277 
1278  ssl_get_client_cert(sockdata(conn), conn);
1279 
1280  const char *const c_ssl_ciphers =
1281  cs_subset_string(NeoMutt->sub, "ssl_ciphers");
1282  if (c_ssl_ciphers)
1283  {
1284  SSL_CTX_set_cipher_list(sockdata(conn)->sctx, c_ssl_ciphers);
1285  }
1286 
1287  if (ssl_set_verify_partial(sockdata(conn)->sctx))
1288  {
1289  mutt_error(_("Warning: error enabling ssl_verify_partial_chains"));
1290  }
1291 
1292  sockdata(conn)->ssl = SSL_new(sockdata(conn)->sctx);
1293  SSL_set_fd(sockdata(conn)->ssl, conn->fd);
1294 
1295  if (ssl_negotiate(conn, sockdata(conn)))
1296  goto free_ssl;
1297 
1298  sockdata(conn)->isopen = 1;
1299  conn->ssf = SSL_CIPHER_get_bits(SSL_get_current_cipher(sockdata(conn)->ssl), &maxbits);
1300 
1301  return 0;
1302 
1303 free_ssl:
1304  SSL_free(sockdata(conn)->ssl);
1305  sockdata(conn)->ssl = NULL;
1306 free_ctx:
1307  SSL_CTX_free(sockdata(conn)->sctx);
1308  sockdata(conn)->sctx = NULL;
1309 free_ssldata:
1310  FREE(&conn->sockdata);
1311 
1312  return -1;
1313 }
1314 
1318 static int ssl_socket_poll(struct Connection *conn, time_t wait_secs)
1319 {
1320  if (!conn)
1321  return -1;
1322 
1323  if (SSL_has_pending(sockdata(conn)->ssl))
1324  return 1;
1325 
1326  return raw_socket_poll(conn, wait_secs);
1327 }
1328 
1332 static int ssl_socket_open(struct Connection *conn)
1333 {
1334  if (raw_socket_open(conn) < 0)
1335  return -1;
1336 
1337  int rc = ssl_setup(conn);
1338  if (rc)
1339  raw_socket_close(conn);
1340 
1341  return rc;
1342 }
1343 
1347 static int ssl_socket_read(struct Connection *conn, char *buf, size_t count)
1348 {
1349  struct SslSockData *data = sockdata(conn);
1350  int rc;
1351 
1352  rc = SSL_read(data->ssl, buf, count);
1353  if ((rc <= 0) || (errno == EINTR))
1354  {
1355  if (errno == EINTR)
1356  {
1357  rc = -1;
1358  }
1359  data->isopen = 0;
1360  ssl_err(data, rc);
1361  }
1362 
1363  return rc;
1364 }
1365 
1369 static int ssl_socket_write(struct Connection *conn, const char *buf, size_t count)
1370 {
1371  if (!conn || !conn->sockdata || !buf || (count == 0))
1372  return -1;
1373 
1374  int rc = SSL_write(sockdata(conn)->ssl, buf, count);
1375  if ((rc <= 0) || (errno == EINTR))
1376  {
1377  if (errno == EINTR)
1378  {
1379  rc = -1;
1380  }
1381  ssl_err(sockdata(conn), rc);
1382  }
1383 
1384  return rc;
1385 }
1386 
1390 static int ssl_socket_close(struct Connection *conn)
1391 {
1392  struct SslSockData *data = sockdata(conn);
1393 
1394  if (data)
1395  {
1396  if (data->isopen && (raw_socket_poll(conn, 0) >= 0))
1397  SSL_shutdown(data->ssl);
1398 
1399  SSL_free(data->ssl);
1400  data->ssl = NULL;
1401  SSL_CTX_free(data->sctx);
1402  data->sctx = NULL;
1403  FREE(&conn->sockdata);
1404  }
1405 
1406  return raw_socket_close(conn);
1407 }
1408 
1416 {
1417  if (ssl_init())
1418  return -1;
1419 
1420  int rc = ssl_setup(conn);
1421 
1422  /* hmm. watch out if we're starting TLS over any method other than raw. */
1423  conn->read = ssl_socket_read;
1424  conn->write = ssl_socket_write;
1426  conn->poll = ssl_socket_poll;
1427 
1428  return rc;
1429 }
1430 
1438 {
1439  if (ssl_init() < 0)
1440  {
1441  conn->open = ssl_socket_open_err;
1442  return -1;
1443  }
1444 
1445  conn->open = ssl_socket_open;
1446  conn->read = ssl_socket_read;
1447  conn->write = ssl_socket_write;
1448  conn->poll = ssl_socket_poll;
1449  conn->close = ssl_socket_close;
1450 
1451  return 0;
1452 }
static char * asn1time_to_string(ASN1_UTCTIME *tm)
Convert a time to a string.
Definition: openssl.c:413
#define NONULL(x)
Definition: string2.h:37
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
static int ssl_negotiate(struct Connection *conn, struct SslSockData *ssldata)
Attempt to negotiate SSL over the wire.
Definition: openssl.c:1124
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
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:40
unsigned int ssf
Security strength factor, in bits (see below)
Definition: connection.h:41
static int ssl_socket_open_err(struct Connection *conn)
Error callback for opening an SSL connection - Implements Connection::open() -.
Definition: openssl.c:355
static void add_cert(const char *title, X509 *cert, bool issuer, struct ListHead *list)
Look up certificate info and save it to a list.
Definition: openssl.c:854
#define mutt_error(...)
Definition: logging.h:88
int(* read)(struct Connection *conn, char *buf, size_t count)
Definition: connection.h:82
static int add_entropy(const char *file)
Add a source of random numbers.
Definition: openssl.c:203
size_t idx
Definition: mailbox.c:257
Shared functions that are private to Connections.
NeoMutt Logging.
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
static int ssl_socket_open(struct Connection *conn)
Open an SSL socket - Implements Connection::open() -.
Definition: openssl.c:1332
static void ssl_get_client_cert(struct SslSockData *ssldata, struct Connection *conn)
Get the client certificate for an SSL connection.
Definition: openssl.c:606
void * sockdata
Backend-specific socket data.
Definition: connection.h:46
String manipulation buffer.
Definition: buffer.h:33
char user[128]
Username.
Definition: connaccount.h:55
static int ssl_load_certificates(SSL_CTX *ctx)
Load certificates and filter out the expired ones.
Definition: openssl.c:116
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
static int ssl_socket_read(struct Connection *conn, char *buf, size_t count)
Read data from an SSL socket - Implements Connection::read() -.
Definition: openssl.c:1347
#define _(a)
Definition: message.h:28
static int ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
Certificate verification callback.
Definition: openssl.c:1004
static void ssl_dprint_err_stack(void)
Dump the SSL error stack.
Definition: openssl.c:307
static bool check_certificate_file(X509 *peercert)
Read and check a certificate file.
Definition: openssl.c:672
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: openssl.c:1415
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
static bool compare_certificates(X509 *cert, X509 *peercert, unsigned char *peermd, unsigned int peermdlen)
Compare two X509 certificated.
Definition: openssl.c:440
Container for Accounts, Notifications.
Definition: neomutt.h:36
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
Convenience wrapper for the config headers.
char * HomeDir
User&#39;s home directory.
Definition: mutt_globals.h:45
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:1024
int(* open)(struct Connection *conn)
Definition: connection.h:69
Email Address Handling.
static int ssl_socket_poll(struct Connection *conn, time_t wait_secs)
Check whether a socket read would block - Implements Connection::poll() -.
Definition: openssl.c:1318
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
static bool interactive_check_cert(X509 *cert, int idx, size_t len, SSL *ssl, bool allow_always)
Ask the user if a certificate is valid.
Definition: openssl.c:898
#define mutt_array_size(x)
Definition: memory.h:33
size_t dsize
Length of data.
Definition: buffer.h:37
static bool check_certificate_cache(X509 *peercert)
Is the X509 Certificate in the cache?
Definition: openssl.c:643
static int check_host(X509 *x509cert, const char *hostname, char *err, size_t errlen)
Check the host on the certificate.
Definition: openssl.c:719
int mutt_idna_to_ascii_lz(const char *input, char **output, uint8_t flags)
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
Convenience wrapper for the core headers.
char pass[256]
Password.
Definition: connaccount.h:56
static bool check_certificate_by_digest(X509 *peercert)
Validate a certificate by its digest.
Definition: openssl.c:828
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:112
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 ssl_socket_close_and_restore(struct Connection *conn)
Close an SSL Connection and restore Connection callbacks - Implements Connection::close() -...
Definition: openssl.c:627
static int ssl_setup(struct Connection *conn)
Set up SSL on the Connection.
Definition: openssl.c:1209
static int ssl_socket_close(struct Connection *conn)
Close an SSL connection - Implements Connection::close() -.
Definition: openssl.c:1390
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
unsigned short port
Port to connect to.
Definition: connaccount.h:57
int(* close)(struct Connection *conn)
Definition: connection.h:119
static void ssl_err(struct SslSockData *data, int err)
Display an SSL error message.
Definition: openssl.c:238
int fd
Socket file descriptor.
Definition: connection.h:44
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
char * data
Pointer to data.
Definition: buffer.h:35
static char * x509_get_part(X509_NAME *name, int nid)
Retrieve from X509 data.
Definition: openssl.c:369
int raw_socket_open(struct Connection *conn)
Open a socket - Implements Connection::open() -.
Definition: raw.c:120
static int ssl_socket_write(struct Connection *conn, const char *buf, size_t count)
Write data to an SSL socket - Implements Connection::write() -.
Definition: openssl.c:1369
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
static int ssl_cache_trusted_cert(X509 *c)
Cache a trusted certificate.
Definition: openssl.c:839
static STACK_OF(X509)
SSL socket data -.
Definition: openssl.c:90
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
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Login details for a remote server.
Definition: connaccount.h:51
static struct SslSockData * sockdata(struct Connection *conn)
Get a Connection&#39;s socket data.
Definition: openssl.c:1198
static void x509_fingerprint(char *s, int l, X509 *cert, const EVP_MD *(*hashfunc)(void))
Generate a fingerprint for an X509 certificate.
Definition: openssl.c:386
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:385
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
static int ssl_init(void)
Initialise the SSL library.
Definition: openssl.c:552
Log at debug level 1.
Definition: logging.h:40
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:749
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:593
static int ssl_set_verify_partial(SSL_CTX *ctx)
Allow verification using partial chains (with no root)
Definition: openssl.c:166
int(* write)(struct Connection *conn, const char *buf, size_t count)
Definition: connection.h:95
int mutt_ssl_socket_setup(struct Connection *conn)
Set up SSL socket mulitplexor.
Definition: openssl.c:1437
static int SkipModeExDataIndex
Definition: openssl.c:86
#define mutt_message(...)
Definition: logging.h:87
#define FREE(x)
Definition: memory.h:40
Handling of SSL encryption.
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:38
static bool check_certificate_expiration(X509 *peercert, bool silent)
Check if a certificate has expired.
Definition: openssl.c:470
Hundreds of global variables to back the user variables.
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
static int ssl_passwd_cb(char *buf, int buflen, int rwflag, void *userdata)
Callback to get a password.
Definition: openssl.c:336
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1128
Convenience wrapper for the library headers.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
static int HostExDataIndex
Definition: openssl.c:81
static bool hostname_match(const char *hostname, const char *certname)
Does the hostname match the certificate.
Definition: openssl.c:506
int mutt_account_getpass(struct ConnAccount *cac)
Fetch password into ConnAccount, if necessary.
Definition: connaccount.c:110