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