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