NeoMutt  2025-12-11-911-gd8d604
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 "module_data.h"
63#include "mutt_logging.h"
64#include "ssl.h"
65#ifdef HAVE_RAND_EGD
66#include "globals.h"
67#endif
68
69/* LibreSSL defines OPENSSL_VERSION_NUMBER but sets it to 0x20000000L.
70 * So technically we don't need the defined(OPENSSL_VERSION_NUMBER) check. */
71#if (defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x10100000L)) || \
72 (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2070000fL))
73#define X509_get0_notBefore X509_get_notBefore
74#define X509_get0_notAfter X509_get_notAfter
75#define X509_getm_notBefore X509_get_notBefore
76#define X509_getm_notAfter X509_get_notAfter
77#define X509_STORE_CTX_get0_chain X509_STORE_CTX_get_chain
78#define SSL_has_pending SSL_pending
79#endif
80
81/* Unimplemented OpenSSL 1.1 api calls */
82#if (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x2070000fL))
83#define SSL_has_pending SSL_pending
84#endif
85
87// HostExDataIndex moved to ConnModuleData
88
91// SkipModeExDataIndex moved to ConnModuleData
92
95static STACK_OF(X509) *SslSessionCerts = NULL;
96
97static int ssl_socket_close(struct Connection *conn);
98
102struct SslSockData
103{
104 SSL_CTX *sctx;
105 SSL *ssl;
106 unsigned char isopen;
107};
108
121static bool ssl_load_certificates(SSL_CTX *ctx)
122{
123 bool rc = true;
124
125 mutt_debug(LL_DEBUG2, "loading trusted certificates\n");
126 X509_STORE *store = SSL_CTX_get_cert_store(ctx);
127 if (!store)
128 {
129 store = X509_STORE_new();
130 SSL_CTX_set_cert_store(ctx, store);
131 }
132
133 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
134 FILE *fp = mutt_file_fopen(c_certificate_file, "r");
135 if (!fp)
136 return 0;
137
138 X509 *cert = NULL;
139 while (NULL != PEM_read_X509(fp, &cert, NULL, NULL))
140 {
141 if ((X509_cmp_current_time(X509_get0_notBefore(cert)) >= 0) ||
142 (X509_cmp_current_time(X509_get0_notAfter(cert)) <= 0))
143 {
144 char buf[256] = { 0 };
145 mutt_debug(LL_DEBUG2, "filtering expired cert: %s\n",
146 X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)));
147 }
148 else
149 {
150 X509_STORE_add_cert(store, cert);
151 }
152 }
153 /* PEM_read_X509 sets the error NO_START_LINE on eof */
154 if (ERR_GET_REASON(ERR_peek_last_error()) != PEM_R_NO_START_LINE)
155 rc = false;
156 ERR_clear_error();
157
158 X509_free(cert);
159 mutt_file_fclose(&fp);
160
161 return rc;
162}
163
170static bool ssl_set_verify_partial(SSL_CTX *ctx)
171{
172 bool rc = true;
173#ifdef HAVE_SSL_PARTIAL_CHAIN
174 X509_VERIFY_PARAM *param = NULL;
175
176 const bool c_ssl_verify_partial_chains = cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
177 if (c_ssl_verify_partial_chains)
178 {
179 param = X509_VERIFY_PARAM_new();
180 if (param)
181 {
182 X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_PARTIAL_CHAIN);
183 if (SSL_CTX_set1_param(ctx, param) == 0)
184 {
185 mutt_debug(LL_DEBUG2, "SSL_CTX_set1_param() failed\n");
186 rc = false;
187 }
188 X509_VERIFY_PARAM_free(param);
189 }
190 else
191 {
192 mutt_debug(LL_DEBUG2, "X509_VERIFY_PARAM_new() failed\n");
193 rc = false;
194 }
195 }
196#endif
197 return rc;
198}
199
206static int add_entropy(const char *file)
207{
208 if (!file)
209 return 0;
210
211 struct stat st = { 0 };
212 int n = -1;
213
214 if (stat(file, &st) == -1)
215 return (errno == ENOENT) ? 0 : -1;
216
217 mutt_message(_("Filling entropy pool: %s..."), file);
218
219 /* check that the file permissions are secure */
220 if ((st.st_uid != getuid()) || ((st.st_mode & (S_IWGRP | S_IRGRP)) != 0) ||
221 ((st.st_mode & (S_IWOTH | S_IROTH)) != 0))
222 {
223 mutt_error(_("%s has insecure permissions"), file);
224 return -1;
225 }
226
227#ifdef HAVE_RAND_EGD
228 n = RAND_egd(file);
229#endif
230 if (n <= 0)
231 n = RAND_load_file(file, -1);
232
233 return n;
234}
235
241static void ssl_err(struct SslSockData *data, int err)
242{
243 int e = SSL_get_error(data->ssl, err);
244 switch (e)
245 {
246 case SSL_ERROR_NONE:
247 return;
248 case SSL_ERROR_ZERO_RETURN:
249 data->isopen = 0;
250 break;
251 case SSL_ERROR_SYSCALL:
252 data->isopen = 0;
253 break;
254 }
255
256 const char *errmsg = NULL;
257 unsigned long sslerr;
258
259 switch (e)
260 {
261 case SSL_ERROR_SYSCALL:
262 errmsg = "I/O error";
263 break;
264 case SSL_ERROR_WANT_ACCEPT:
265 errmsg = "retry accept";
266 break;
267 case SSL_ERROR_WANT_CONNECT:
268 errmsg = "retry connect";
269 break;
270 case SSL_ERROR_WANT_READ:
271 errmsg = "retry read";
272 break;
273 case SSL_ERROR_WANT_WRITE:
274 errmsg = "retry write";
275 break;
276 case SSL_ERROR_WANT_X509_LOOKUP:
277 errmsg = "retry x509 lookup";
278 break;
279 case SSL_ERROR_ZERO_RETURN:
280 errmsg = "SSL connection closed";
281 break;
282 case SSL_ERROR_SSL:
283 sslerr = ERR_get_error();
284 switch (sslerr)
285 {
286 case 0:
287 switch (err)
288 {
289 case 0:
290 errmsg = "EOF";
291 break;
292 default:
293 errmsg = strerror(errno);
294 }
295 break;
296 default:
297 errmsg = ERR_error_string(sslerr, NULL);
298 }
299 break;
300 default:
301 errmsg = "unknown error";
302 }
303
304 mutt_debug(LL_DEBUG1, "SSL error: %s\n", errmsg);
305}
306
310static void ssl_dprint_err_stack(void)
311{
312 BIO *bio = BIO_new(BIO_s_mem());
313 if (!bio)
314 return;
315 ERR_print_errors(bio);
316
317 char *buf = NULL;
318 long buflen = BIO_get_mem_data(bio, &buf);
319 if (buflen > 0)
320 {
321 char *output = MUTT_MEM_MALLOC(buflen + 1, char);
322 memcpy(output, buf, buflen);
323 output[buflen] = '\0';
324 mutt_debug(LL_DEBUG1, "SSL error stack: %s\n", output);
325 FREE(&output);
326 }
327 BIO_free(bio);
328}
329
339static int ssl_passwd_cb(char *buf, int buflen, int rwflag, void *userdata)
340{
341 struct ConnAccount *cac = userdata;
342
343 if (mutt_account_getuser(cac) < 0)
344 return 0;
345
346 mutt_debug(LL_DEBUG2, "getting password for %s@%s:%u\n", cac->user, cac->host, cac->port);
347
348 if (mutt_account_getpass(cac) < 0)
349 return 0;
350
351 return snprintf(buf, buflen, "%s", cac->pass);
352}
353
358static int ssl_socket_open_err(struct Connection *conn)
359{
360 mutt_error(_("SSL disabled due to the lack of entropy"));
361 return -1;
362}
363
372static char *x509_get_part(X509_NAME *name, int nid)
373{
374 static char data[128];
375
376 if (!name || (X509_NAME_get_text_by_NID(name, nid, data, sizeof(data)) < 0))
377 return NULL;
378
379 return data;
380}
381
388static void x509_fingerprint(struct Buffer *buf, X509 *cert, const EVP_MD *(*hashfunc)(void) )
389{
390 unsigned char md[EVP_MAX_MD_SIZE];
391 unsigned int n = 0;
392
393 if (X509_digest(cert, hashfunc(), md, &n) == 0) // Failure
394 {
395 buf_strcpy(buf, _("[unable to calculate]"));
396 return;
397 }
398
399 for (unsigned int i = 0; i < n; i++)
400 {
401 buf_add_printf(buf, "%02X", md[i]);
402
403 // Put a space after a pair of bytes (except for the last one)
404 if (((i % 2) == 1) && (i < (n - 1)))
405 buf_addch(buf, ' ');
406 }
407}
408
416static char *asn1time_to_string(ASN1_UTCTIME *tm)
417{
418 static char buf[64];
419 BIO *bio = NULL;
420
421 mutt_str_copy(buf, _("[invalid date]"), sizeof(buf));
422
423 bio = BIO_new(BIO_s_mem());
424 if (bio)
425 {
426 if (ASN1_TIME_print(bio, tm))
427 (void) BIO_read(bio, buf, sizeof(buf));
428 BIO_free(bio);
429 }
430
431 return buf;
432}
433
443static bool certificates_equal(X509 *cert, X509 *peercert,
444 unsigned char *peermd, unsigned int peermdlen)
445{
446 unsigned char md[EVP_MAX_MD_SIZE];
447 unsigned int mdlen;
448
449 /* Avoid CPU-intensive digest calculation if the certificates are
450 * not even remotely equal. */
451 if ((X509_subject_name_cmp(cert, peercert) != 0) ||
452 (X509_issuer_name_cmp(cert, peercert) != 0))
453 {
454 return false;
455 }
456
457 if (!X509_digest(cert, EVP_sha256(), md, &mdlen) || (peermdlen != mdlen))
458 return false;
459
460 if (memcmp(peermd, md, mdlen) != 0)
461 return false;
462
463 return true;
464}
465
473static bool check_certificate_expiration(X509 *peercert, bool silent)
474{
475 const bool c_ssl_verify_dates = cs_subset_bool(NeoMutt->sub, "ssl_verify_dates");
476 if (c_ssl_verify_dates == MUTT_NO)
477 return true;
478
479 if (X509_cmp_current_time(X509_get0_notBefore(peercert)) >= 0)
480 {
481 if (!silent)
482 {
483 mutt_debug(LL_DEBUG2, "Server certificate is not yet valid\n");
484 mutt_error(_("Server certificate is not yet valid"));
485 }
486 return false;
487 }
488
489 if (X509_cmp_current_time(X509_get0_notAfter(peercert)) <= 0)
490 {
491 if (!silent)
492 {
493 mutt_debug(LL_DEBUG2, "Server certificate has expired\n");
494 mutt_error(_("Server certificate has expired"));
495 }
496 return false;
497 }
498
499 return true;
500}
501
508static bool hostname_match(const char *hostname, const char *certname)
509{
510 const char *cmp1 = NULL, *cmp2 = NULL;
511
512 if (mutt_strn_equal(certname, "*.", 2))
513 {
514 cmp1 = certname + 2;
515 cmp2 = strchr(hostname, '.');
516 if (!cmp2)
517 return false;
518
519 cmp2++;
520 }
521 else
522 {
523 cmp1 = certname;
524 cmp2 = hostname;
525 }
526
527 if ((*cmp1 == '\0') || (*cmp2 == '\0'))
528 {
529 return false;
530 }
531
532 if (strcasecmp(cmp1, cmp2) != 0)
533 {
534 return false;
535 }
536
537 return true;
538}
539
554static int ssl_init(void)
555{
556 static bool init_complete = false;
557
558 if (init_complete)
559 return 0;
560
561 if (RAND_status() != 1)
562 {
563 /* load entropy from files */
564 struct Buffer *path = buf_pool_get();
565 const char *const c_entropy_file = cs_subset_path(NeoMutt->sub, "entropy_file");
566 add_entropy(c_entropy_file);
567 add_entropy(RAND_file_name(path->data, path->dsize));
568
569/* load entropy from egd sockets */
570#ifdef HAVE_RAND_EGD
571 add_entropy(mutt_str_getenv("EGDSOCKET"));
572 buf_printf(path, "%s/.entropy", NONULL(NeoMutt->home_dir));
573 add_entropy(buf_string(path));
574 add_entropy(TMPDIR "/entropy");
575#endif
576
577 /* shuffle $RANDFILE (or ~/.rnd if unset) */
578 RAND_write_file(RAND_file_name(path->data, path->dsize));
579 buf_pool_release(&path);
580
582 if (RAND_status() != 1)
583 {
584 mutt_error(_("Failed to find enough entropy on your system"));
585 return -1;
586 }
587 }
588
589/* OpenSSL performs automatic initialization as of 1.1.
590 * However LibreSSL does not (as of 2.8.3). */
591#if (defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x10100000L)) || \
592 (defined(LIBRESSL_VERSION_NUMBER))
593 /* I don't think you can do this just before reading the error. The call
594 * itself might clobber the last SSL error. */
595 SSL_load_error_strings();
596 SSL_library_init();
597#endif
598 init_complete = true;
599 return 0;
600}
601
607static void ssl_get_client_cert(struct SslSockData *ssldata, struct Connection *conn)
608{
609 const char *const c_ssl_client_cert = 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
624{
625 int rc = ssl_socket_close(conn);
626 conn->read = raw_socket_read;
627 conn->write = raw_socket_write;
628 conn->close = raw_socket_close;
629 conn->poll = raw_socket_poll;
630
631 return rc;
632}
633
639static bool check_certificate_cache(X509 *peercert)
640{
641 unsigned char peermd[EVP_MAX_MD_SIZE];
642 unsigned int peermdlen;
643 X509 *cert = NULL;
644
645 if (!X509_digest(peercert, EVP_sha256(), peermd, &peermdlen) || !SslSessionCerts)
646 {
647 return false;
648 }
649
650 for (int i = sk_X509_num(SslSessionCerts) - 1; i >= 0; i--)
651 {
652 cert = sk_X509_value(SslSessionCerts, i);
653 if (certificates_equal(cert, peercert, peermd, peermdlen))
654 {
655 return true;
656 }
657 }
658
659 return false;
660}
661
668static bool check_certificate_file(X509 *peercert)
669{
670 unsigned char peermd[EVP_MAX_MD_SIZE];
671 unsigned int peermdlen;
672 X509 *cert = NULL;
673 int pass = false;
674 FILE *fp = NULL;
675
676 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
677 fp = mutt_file_fopen(c_certificate_file, "r");
678 if (!fp)
679 return false;
680
681 if (!X509_digest(peercert, EVP_sha256(), peermd, &peermdlen))
682 {
683 mutt_file_fclose(&fp);
684 return false;
685 }
686
687 while (PEM_read_X509(fp, &cert, NULL, NULL))
688 {
689 if (certificates_equal(cert, peercert, peermd, peermdlen) &&
691 {
692 pass = true;
693 break;
694 }
695 }
696 /* PEM_read_X509 sets an error on eof */
697 if (!pass)
698 ERR_clear_error();
699 X509_free(cert);
700 mutt_file_fclose(&fp);
701
702 return pass;
703}
704
714static int check_host(X509 *x509cert, const char *hostname, char *err, size_t errlen)
715{
716 int rc = 0;
717 /* hostname in ASCII format: */
718 char *hostname_ascii = NULL;
719 /* needed to get the common name: */
720 X509_NAME *x509_subject = NULL;
721 char *buf = NULL;
722 int bufsize;
723 /* needed to get the DNS subjectAltNames: */
724 STACK_OF(GENERAL_NAME) * subj_alt_names;
725 int subj_alt_names_count;
726 GENERAL_NAME *subj_alt_name = NULL;
727 /* did we find a name matching hostname? */
728 bool match_found;
729 /* did the SAN extension contain any dNSName entries? (RFC6125 §6.4.4) */
730 bool has_san_dns = false;
731
732 /* Check if 'hostname' matches the one of the subjectAltName extensions of
733 * type DNS or the Common Name (CN). */
734
735#ifdef HAVE_LIBIDN
736 if (mutt_idna_to_ascii_lz(hostname, &hostname_ascii, 0) != 0)
737 {
738 hostname_ascii = mutt_str_dup(hostname);
739 }
740#else
741 hostname_ascii = mutt_str_dup(hostname);
742#endif
743
744 /* Try the DNS subjectAltNames. */
745 match_found = false;
746 subj_alt_names = X509_get_ext_d2i(x509cert, NID_subject_alt_name, NULL, NULL);
747 if (subj_alt_names)
748 {
749 subj_alt_names_count = sk_GENERAL_NAME_num(subj_alt_names);
750 for (int i = 0; i < subj_alt_names_count; i++)
751 {
752 subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
753 if (subj_alt_name->type == GEN_DNS)
754 {
755 has_san_dns = true;
756 if ((subj_alt_name->d.ia5->length >= 0) &&
757 (mutt_str_len((char *) subj_alt_name->d.ia5->data) ==
758 (size_t) subj_alt_name->d.ia5->length) &&
759 (match_found = hostname_match(hostname_ascii,
760 (char *) (subj_alt_name->d.ia5->data))))
761 {
762 break;
763 }
764 }
765 }
766 GENERAL_NAMES_free(subj_alt_names);
767 }
768
769 if (!match_found && !has_san_dns)
770 {
771 /* Only fall back to CN when the SAN extension has no dNSName entries.
772 * RFC6125 §6.4.4: if SANs of type dNSName are present, the CN must not be checked. */
773 x509_subject = X509_get_subject_name(x509cert);
774 if (!x509_subject)
775 {
776 if (err && errlen)
777 mutt_str_copy(err, _("can't get certificate subject"), errlen);
778 goto out;
779 }
780
781 /* first get the space requirements */
782 bufsize = X509_NAME_get_text_by_NID(x509_subject, NID_commonName, NULL, 0);
783 if (bufsize == -1)
784 {
785 if (err && errlen)
786 mutt_str_copy(err, _("can't get certificate common name"), errlen);
787 goto out;
788 }
789 bufsize++; /* space for the terminal nul char */
790 buf = MUTT_MEM_MALLOC(bufsize, char);
791 if (X509_NAME_get_text_by_NID(x509_subject, NID_commonName, buf, bufsize) == -1)
792 {
793 if (err && errlen)
794 mutt_str_copy(err, _("can't get certificate common name"), errlen);
795 goto out;
796 }
797 /* cast is safe since bufsize is incremented above, so bufsize-1 is always
798 * zero or greater. */
799 if (mutt_str_len(buf) == (size_t) bufsize - 1)
800 {
801 match_found = hostname_match(hostname_ascii, buf);
802 }
803 }
804
805 if (!match_found)
806 {
807 if (err && errlen)
808 snprintf(err, errlen, _("certificate owner does not match hostname %s"), hostname);
809 goto out;
810 }
811
812 rc = 1;
813
814out:
815 FREE(&buf);
816 FREE(&hostname_ascii);
817
818 return rc;
819}
820
827static bool check_certificate_by_digest(X509 *peercert)
828{
829 return check_certificate_expiration(peercert, false) && check_certificate_file(peercert);
830}
831
838static int ssl_cache_trusted_cert(X509 *c)
839{
840 mutt_debug(LL_DEBUG1, "trusted\n");
841 if (!SslSessionCerts)
842 SslSessionCerts = sk_X509_new_null();
843 return sk_X509_push(SslSessionCerts, X509_dup(c));
844}
845
853static void add_cert(const char *title, X509 *cert, bool issuer, struct StringArray *carr)
854{
855 static const int part[] = {
856 NID_commonName, // CN
857 NID_pkcs9_emailAddress, // Email
858 NID_organizationName, // O
859 NID_organizationalUnitName, // OU
860 NID_localityName, // L
861 NID_stateOrProvinceName, // ST
862 NID_countryName, // C
863 };
864
865 X509_NAME *x509 = NULL;
866 if (issuer)
867 x509 = X509_get_issuer_name(cert);
868 else
869 x509 = X509_get_subject_name(cert);
870
871 // Allocate formatted strings and let the array take ownership
872 ARRAY_ADD(carr, mutt_str_dup(title));
873
874 char *line = NULL;
875 char *text = NULL;
876 for (size_t i = 0; i < countof(part); i++)
877 {
878 text = x509_get_part(x509, part[i]);
879 if (text)
880 {
881 mutt_str_asprintf(&line, " %s", text);
882 ARRAY_ADD(carr, line);
883 }
884 }
885}
886
897static bool interactive_check_cert(X509 *cert, int idx, size_t len, SSL *ssl, bool allow_always)
898{
900 if (!OptGui)
901 {
902 mutt_debug(LL_DEBUG1, "unable to prompt for certificate in batch mode\n");
903 mutt_error(_("Untrusted server certificate"));
904 return 0;
905 }
906
907 struct StringArray carr = ARRAY_HEAD_INITIALIZER;
908 struct Buffer *buf = buf_pool_get();
909
910 add_cert(_("This certificate belongs to:"), cert, false, &carr);
911 ARRAY_ADD(&carr, NULL);
912 add_cert(_("This certificate was issued by:"), cert, true, &carr);
913
914 char *line = NULL;
915 ARRAY_ADD(&carr, NULL);
916 ARRAY_ADD(&carr, mutt_str_dup(_("This certificate is valid")));
917 mutt_str_asprintf(&line, _(" from %s"), asn1time_to_string(X509_getm_notBefore(cert)));
918 ARRAY_ADD(&carr, line);
919 mutt_str_asprintf(&line, _(" to %s"), asn1time_to_string(X509_getm_notAfter(cert)));
920 ARRAY_ADD(&carr, line);
921
922 ARRAY_ADD(&carr, NULL);
923 x509_fingerprint(buf, cert, EVP_sha1);
924 mutt_str_asprintf(&line, _("SHA1 Fingerprint: %s"), buf_string(buf));
925 ARRAY_ADD(&carr, line);
926
927 buf_reset(buf);
928 x509_fingerprint(buf, cert, EVP_sha256);
929 buf->data[39] = '\0'; /* Divide into two lines of output */
930 mutt_str_asprintf(&line, "%s%s", _("SHA256 Fingerprint: "), buf_string(buf));
931 ARRAY_ADD(&carr, line);
932 mutt_str_asprintf(&line, "%*s%s", (int) mutt_str_len(_("SHA256 Fingerprint: ")),
933 "", buf->data + 40);
934 ARRAY_ADD(&carr, line);
935
936 bool allow_skip = false;
937/* The leaf/host certificate can't be skipped. */
938#ifdef HAVE_SSL_PARTIAL_CHAIN
939 const bool c_ssl_verify_partial_chains = cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
940 if ((idx != 0) && c_ssl_verify_partial_chains)
941 allow_skip = true;
942#endif
943
944 char title[256] = { 0 };
945 snprintf(title, sizeof(title),
946 _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
947
948 /* Inside ssl_verify_callback(), this function is guarded by a call to
949 * check_certificate_by_digest(). This means if check_certificate_expiration() is
950 * true, then check_certificate_file() must be false. Therefore we don't need
951 * to also scan the certificate file here. */
952 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
953 allow_always = allow_always && c_certificate_file &&
955
956 int rc = dlg_certificate(title, &carr, allow_always, allow_skip);
957 if ((rc == 3) && !allow_always)
958 rc = 4;
959
960 switch (rc)
961 {
962 case 1: // Reject
963 break;
964 case 2: // Once
965 SSL_set_ex_data(ssl, mod_data->skip_mode_ex_data_index, NULL);
967 break;
968 case 3: // Always
969 {
970 bool saved = false;
971 FILE *fp = mutt_file_fopen(c_certificate_file, "a");
972 if (fp)
973 {
974 if (PEM_write_X509(fp, cert))
975 saved = true;
976 mutt_file_fclose(&fp);
977 }
978
979 if (saved)
980 mutt_message(_("Certificate saved"));
981 else
982 mutt_error(_("Warning: Couldn't save certificate"));
983
984 SSL_set_ex_data(ssl, mod_data->skip_mode_ex_data_index, NULL);
986 break;
987 }
988 case 4: // Skip
989 SSL_set_ex_data(ssl, mod_data->skip_mode_ex_data_index, &mod_data->skip_mode_ex_data_index);
990 break;
991 }
992
993 string_array_clear(&carr);
994 buf_pool_release(&buf);
995
996 return (rc > 1);
997}
998
1010static int ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
1011{
1012 char buf[256] = { 0 };
1013
1014 SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
1015 if (!ssl)
1016 {
1017 mutt_debug(LL_DEBUG1, "failed to retrieve SSL structure from X509_STORE_CTX\n");
1018 return false;
1019 }
1021 const char *host = SSL_get_ex_data(ssl, mod_data->host_ex_data_index);
1022 if (!host)
1023 {
1024 mutt_debug(LL_DEBUG1, "failed to retrieve hostname from SSL structure\n");
1025 return false;
1026 }
1027
1028 /* This is true when a previous entry in the certificate chain did
1029 * not verify and the user manually chose to skip it via the
1030 * $ssl_verify_partial_chains option.
1031 * In this case, all following certificates need to be treated as non-verified
1032 * until one is actually verified. */
1033 bool skip_mode = (SSL_get_ex_data(ssl, mod_data->skip_mode_ex_data_index));
1034
1035 X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
1036 int pos = X509_STORE_CTX_get_error_depth(ctx);
1037 size_t len = sk_X509_num(X509_STORE_CTX_get0_chain(ctx));
1038
1039 mutt_debug(LL_DEBUG1, "checking cert chain entry %s (preverify: %d skipmode: %d)\n",
1040 X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)),
1041 preverify_ok, skip_mode);
1042
1043#ifdef HAVE_SSL_PARTIAL_CHAIN
1044 /* Sometimes, when a certificate is (s)kipped, OpenSSL will pass it
1045 * a second time with preverify_ok = 1. Don't show it or the user
1046 * will think their "s" key is broken. */
1047 const bool c_ssl_verify_partial_chains = cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
1048 if (c_ssl_verify_partial_chains)
1049 {
1050 static int last_pos = 0;
1051 static X509 *last_cert = NULL;
1052 if (skip_mode && preverify_ok && (pos == last_pos) && last_cert)
1053 {
1054 unsigned char last_cert_md[EVP_MAX_MD_SIZE];
1055 unsigned int last_cert_mdlen;
1056 if (X509_digest(last_cert, EVP_sha256(), last_cert_md, &last_cert_mdlen) &&
1057 certificates_equal(cert, last_cert, last_cert_md, last_cert_mdlen))
1058 {
1059 mutt_debug(LL_DEBUG2, "ignoring duplicate skipped certificate\n");
1060 return true;
1061 }
1062 }
1063
1064 last_pos = pos;
1065 if (last_cert)
1066 X509_free(last_cert);
1067 last_cert = X509_dup(cert);
1068 }
1069#endif
1070
1071 /* check session cache first */
1072 if (check_certificate_cache(cert))
1073 {
1074 mutt_debug(LL_DEBUG2, "using cached certificate\n");
1075 SSL_set_ex_data(ssl, mod_data->skip_mode_ex_data_index, NULL);
1076 return true;
1077 }
1078
1079 /* check hostname only for the leaf certificate */
1080 buf[0] = '\0';
1081 const bool c_ssl_verify_host = cs_subset_bool(NeoMutt->sub, "ssl_verify_host");
1082 if ((pos == 0) && (c_ssl_verify_host != MUTT_NO))
1083 {
1084 if (check_host(cert, host, buf, sizeof(buf)) == 0)
1085 {
1086 mutt_error(_("Certificate host check failed: %s"), buf);
1087 /* we disallow (a)ccept always in the prompt, because it will have no effect
1088 * for hostname mismatches. */
1089 return interactive_check_cert(cert, pos, len, ssl, false);
1090 }
1091 mutt_debug(LL_DEBUG2, "hostname check passed\n");
1092 }
1093
1094 if (!preverify_ok || skip_mode)
1095 {
1096 /* automatic check from user's database */
1097 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
1098 if (c_certificate_file && check_certificate_by_digest(cert))
1099 {
1100 mutt_debug(LL_DEBUG2, "digest check passed\n");
1101 SSL_set_ex_data(ssl, mod_data->skip_mode_ex_data_index, NULL);
1102 return true;
1103 }
1104
1105 /* log verification error */
1106 int err = X509_STORE_CTX_get_error(ctx);
1107 snprintf(buf, sizeof(buf), "%s (%d)", X509_verify_cert_error_string(err), err);
1108 mutt_debug(LL_DEBUG2, "X509_verify_cert: %s\n", buf);
1109
1110 /* prompt user */
1111 return interactive_check_cert(cert, pos, len, ssl, true);
1112 }
1113
1114 return true;
1115}
1116
1127static int ssl_negotiate(struct Connection *conn, struct SslSockData *ssldata)
1128{
1129 int err;
1130 const char *errmsg = NULL;
1132
1133 if (mod_data->host_ex_data_index == -1)
1134 {
1135 mod_data->host_ex_data_index = SSL_get_ex_new_index(0, "host", NULL, NULL, NULL);
1136 if (mod_data->host_ex_data_index == -1)
1137 {
1138 mutt_debug(LL_DEBUG1, "#1 failed to get index for application specific data\n");
1139 return -1;
1140 }
1141 }
1142
1143 if (!SSL_set_ex_data(ssldata->ssl, mod_data->host_ex_data_index, conn->account.host))
1144 {
1145 mutt_debug(LL_DEBUG1, "#2 failed to save hostname in SSL structure\n");
1146 return -1;
1147 }
1148
1149 if (mod_data->skip_mode_ex_data_index == -1)
1150 {
1151 mod_data->skip_mode_ex_data_index = SSL_get_ex_new_index(0, "skip", NULL, NULL, NULL);
1152 if (mod_data->skip_mode_ex_data_index == -1)
1153 {
1154 mutt_debug(LL_DEBUG1, "#3 failed to get index for application specific data\n");
1155 return -1;
1156 }
1157 }
1158
1159 if (!SSL_set_ex_data(ssldata->ssl, mod_data->skip_mode_ex_data_index, NULL))
1160 {
1161 mutt_debug(LL_DEBUG1, "#4 failed to save skip mode in SSL structure\n");
1162 return -1;
1163 }
1164
1165 SSL_set_verify(ssldata->ssl, SSL_VERIFY_PEER, ssl_verify_callback);
1166 SSL_set_mode(ssldata->ssl, SSL_MODE_AUTO_RETRY);
1167
1168 if (!SSL_set_tlsext_host_name(ssldata->ssl, conn->account.host))
1169 {
1170 /* L10N: This is a warning when trying to set the host name for
1171 TLS Server Name Indication (SNI). This allows the server to present
1172 the correct certificate if it supports multiple hosts. */
1173 mutt_error(_("Warning: unable to set TLS SNI host name"));
1174 }
1175
1176 ERR_clear_error();
1177
1178retry:
1179 err = SSL_connect(ssldata->ssl);
1180 if (err != 1)
1181 {
1182 // Temporary failure, e.g. signal received
1183 if (BIO_should_retry(SSL_get_rbio(ssldata->ssl)))
1184 goto retry;
1185
1186 switch (SSL_get_error(ssldata->ssl, err))
1187 {
1188 case SSL_ERROR_SYSCALL:
1189 errmsg = _("I/O error");
1190 break;
1191 case SSL_ERROR_SSL:
1192 errmsg = ERR_error_string(ERR_get_error(), NULL);
1193 break;
1194 default:
1195 errmsg = _("unknown error");
1196 }
1197
1198 mutt_error(_("SSL failed: %s"), errmsg);
1199
1200 return -1;
1201 }
1202
1203 return 0;
1204}
1205
1211static inline struct SslSockData *sockdata(struct Connection *conn)
1212{
1213 return conn->sockdata;
1214}
1215
1222static int ssl_setup(struct Connection *conn)
1223{
1224 int maxbits = 0;
1225
1226 conn->sockdata = MUTT_MEM_CALLOC(1, struct SslSockData);
1227
1228 sockdata(conn)->sctx = SSL_CTX_new(SSLv23_client_method());
1229 if (!sockdata(conn)->sctx)
1230 {
1231 /* L10N: an SSL context is a data structure returned by the OpenSSL
1232 function SSL_CTX_new(). In this case it returned NULL: an
1233 error condition. */
1234 mutt_error(_("Unable to create SSL context"));
1236 goto free_ssldata;
1237 }
1238
1239 /* disable SSL protocols as needed */
1240#ifdef SSL_OP_NO_TLSv1_3
1241 const bool c_ssl_use_tlsv1_3 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_3");
1242 if (!c_ssl_use_tlsv1_3)
1243 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_3);
1244#endif
1245
1246#ifdef SSL_OP_NO_TLSv1_2
1247 const bool c_ssl_use_tlsv1_2 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_2");
1248 if (!c_ssl_use_tlsv1_2)
1249 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_2);
1250#endif
1251
1252 // Deprecated protocols
1253#ifdef SSL_OP_NO_TLSv1_1
1254 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_1);
1255#endif
1256#ifdef SSL_OP_NO_TLSv1
1257 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1);
1258#endif
1259 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_SSLv3);
1260 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_SSLv2);
1261
1262 const bool c_ssl_use_system_certs = cs_subset_bool(NeoMutt->sub, "ssl_use_system_certs");
1263 if (c_ssl_use_system_certs)
1264 {
1265 if (!SSL_CTX_set_default_verify_paths(sockdata(conn)->sctx))
1266 {
1267 mutt_debug(LL_DEBUG1, "Error setting default verify paths\n");
1268 goto free_ctx;
1269 }
1270 }
1271
1272 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
1273 if (c_certificate_file && !ssl_load_certificates(sockdata(conn)->sctx))
1274 mutt_debug(LL_DEBUG1, "Error loading trusted certificates\n");
1275
1276 ssl_get_client_cert(sockdata(conn), conn);
1277
1278 const char *const c_ssl_ciphers = cs_subset_string(NeoMutt->sub, "ssl_ciphers");
1279 if (c_ssl_ciphers)
1280 {
1281 SSL_CTX_set_cipher_list(sockdata(conn)->sctx, c_ssl_ciphers);
1282 }
1283
1284 if (!ssl_set_verify_partial(sockdata(conn)->sctx))
1285 {
1286 mutt_error(_("Warning: error enabling ssl_verify_partial_chains"));
1287 }
1288
1289 sockdata(conn)->ssl = SSL_new(sockdata(conn)->sctx);
1290 SSL_set_fd(sockdata(conn)->ssl, conn->fd);
1291
1292 if (ssl_negotiate(conn, sockdata(conn)))
1293 goto free_ssl;
1294
1295 sockdata(conn)->isopen = 1;
1296 conn->ssf = SSL_CIPHER_get_bits(SSL_get_current_cipher(sockdata(conn)->ssl), &maxbits);
1297
1298 return 0;
1299
1300free_ssl:
1301 SSL_free(sockdata(conn)->ssl);
1302 sockdata(conn)->ssl = NULL;
1303free_ctx:
1304 SSL_CTX_free(sockdata(conn)->sctx);
1305 sockdata(conn)->sctx = NULL;
1306free_ssldata:
1307 FREE(&conn->sockdata);
1308
1309 return -1;
1310}
1311
1315static int ssl_socket_poll(struct Connection *conn, time_t wait_secs)
1316{
1317 if (!conn)
1318 return -1;
1319
1320 if (SSL_has_pending(sockdata(conn)->ssl))
1321 return 1;
1322
1323 return raw_socket_poll(conn, wait_secs);
1324}
1325
1329static int ssl_socket_open(struct Connection *conn)
1330{
1331 if (raw_socket_open(conn) < 0)
1332 return -1;
1333
1334 int rc = ssl_setup(conn);
1335 if (rc)
1336 raw_socket_close(conn);
1337
1338 return rc;
1339}
1340
1344static int ssl_socket_read(struct Connection *conn, char *buf, size_t count)
1345{
1346 struct SslSockData *data = sockdata(conn);
1347 int rc;
1348
1349retry:
1350 rc = SSL_read(data->ssl, buf, count);
1351 if (rc > 0)
1352 return rc;
1353
1354 // User hit Ctrl-C
1355 if (SigInt && (errno == EINTR))
1356 {
1357 rc = -1;
1358 }
1359 else if (BIO_should_retry(SSL_get_rbio(data->ssl)))
1360 {
1361 // Temporary failure, e.g. signal received
1362 if (raw_socket_poll(conn, 0) >= 0)
1363 {
1364 goto retry;
1365 }
1366 }
1367
1368 data->isopen = 0;
1369 ssl_err(data, rc);
1370 return rc;
1371}
1372
1376static int ssl_socket_write(struct Connection *conn, const char *buf, size_t count)
1377{
1378 if (!conn || !conn->sockdata || !buf || (count == 0))
1379 return -1;
1380
1381 struct SslSockData *data = sockdata(conn);
1382 int rc;
1383
1384retry:
1385 rc = SSL_write(data->ssl, buf, count);
1386 if (rc > 0)
1387 return rc;
1388
1389 // User hit Ctrl-C
1390 if (SigInt && (errno == EINTR))
1391 {
1392 rc = -1;
1393 }
1394 else if (BIO_should_retry(SSL_get_wbio(data->ssl)))
1395 {
1396 // Temporary failure, e.g. signal received
1397 goto retry;
1398 }
1399
1400 ssl_err(data, rc);
1401 return rc;
1402}
1403
1407static int ssl_socket_close(struct Connection *conn)
1408{
1409 struct SslSockData *data = sockdata(conn);
1410
1411 if (data)
1412 {
1413 if (data->isopen && (raw_socket_poll(conn, 0) >= 0))
1414 SSL_shutdown(data->ssl);
1415
1416 SSL_free(data->ssl);
1417 data->ssl = NULL;
1418 SSL_CTX_free(data->sctx);
1419 data->sctx = NULL;
1420 FREE(&conn->sockdata);
1421 }
1422
1423 return raw_socket_close(conn);
1424}
1425
1433{
1434 if (ssl_init())
1435 return -1;
1436
1437 int rc = ssl_setup(conn);
1438
1439 /* hmm. watch out if we're starting TLS over any method other than raw. */
1440 conn->read = ssl_socket_read;
1441 conn->write = ssl_socket_write;
1443 conn->poll = ssl_socket_poll;
1444
1445 return rc;
1446}
1447
1455{
1456 if (ssl_init() < 0)
1457 {
1458 conn->open = ssl_socket_open_err;
1459 return -1;
1460 }
1461
1462 conn->open = ssl_socket_open;
1463 conn->read = ssl_socket_read;
1464 conn->write = ssl_socket_write;
1465 conn->poll = ssl_socket_poll;
1466 conn->close = ssl_socket_close;
1467
1468 return 0;
1469}
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.
Conn private Module data.
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:144
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
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:623
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:1407
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:358
static int ssl_socket_open(struct Connection *conn)
Open an SSL socket - Implements Connection::open() -.
Definition openssl.c:1329
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:1315
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:1344
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:1376
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
@ MODULE_ID_CONN
ModuleConn, Network connections
Definition module_api.h:60
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:808
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:731
void string_array_clear(struct StringArray *arr)
Free all memory of a StringArray.
Definition string.c:936
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
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:586
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
NeoMutt Logging.
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:663
static int check_host(X509 *x509cert, const char *hostname, char *err, size_t errlen)
Check the host on the certificate.
Definition openssl.c:714
static int add_entropy(const char *file)
Add a source of random numbers.
Definition openssl.c:206
static char * x509_get_part(X509_NAME *name, int nid)
Retrieve from X509 data.
Definition openssl.c:372
static char * asn1time_to_string(ASN1_UTCTIME *tm)
Convert a time to a string.
Definition openssl.c:416
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition openssl.c:1432
static STACK_OF(X509)
index for storing hostname as application specific data in SSL structure
Definition openssl.c:95
static void x509_fingerprint(struct Buffer *buf, X509 *cert, const EVP_MD *(*hashfunc)(void))
Generate a fingerprint for an X509 certificate.
Definition openssl.c:388
static int ssl_setup(struct Connection *conn)
Set up SSL on the Connection.
Definition openssl.c:1222
static bool check_certificate_cache(X509 *peercert)
Is the X509 Certificate in the cache?
Definition openssl.c:639
static int ssl_passwd_cb(char *buf, int buflen, int rwflag, void *userdata)
Callback to get a password.
Definition openssl.c:339
static int ssl_negotiate(struct Connection *conn, struct SslSockData *ssldata)
Attempt to negotiate SSL over the wire.
Definition openssl.c:1127
static bool certificates_equal(X509 *cert, X509 *peercert, unsigned char *peermd, unsigned int peermdlen)
Compare two X509 certificated.
Definition openssl.c:443
static bool ssl_load_certificates(SSL_CTX *ctx)
Load certificates and filter out the expired ones.
Definition openssl.c:121
static void ssl_dprint_err_stack(void)
Dump the SSL error stack.
Definition openssl.c:310
static int ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
Certificate verification callback.
Definition openssl.c:1010
static struct SslSockData * sockdata(struct Connection *conn)
Get a Connection's socket data.
Definition openssl.c:1211
static bool ssl_set_verify_partial(SSL_CTX *ctx)
Allow verification using partial chains (with no root)
Definition openssl.c:170
static int ssl_cache_trusted_cert(X509 *c)
Cache a trusted certificate.
Definition openssl.c:838
static int ssl_init(void)
Initialise the SSL library.
Definition openssl.c:554
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:853
static void ssl_err(struct SslSockData *data, int err)
Display an SSL error message.
Definition openssl.c:241
static void ssl_get_client_cert(struct SslSockData *ssldata, struct Connection *conn)
Get the client certificate for an SSL connection.
Definition openssl.c:607
static bool check_certificate_expiration(X509 *peercert, bool silent)
Check if a certificate has expired.
Definition openssl.c:473
static bool check_certificate_file(X509 *peercert)
Read and check a certificate file.
Definition openssl.c:668
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:897
static bool hostname_match(const char *hostname, const char *certname)
Does the hostname match the certificate.
Definition openssl.c:508
int mutt_ssl_socket_setup(struct Connection *conn)
Set up SSL socket mulitplexor.
Definition openssl.c:1454
static bool check_certificate_by_digest(X509 *peercert)
Validate a certificate by its digest.
Definition openssl.c:827
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:59
char user[128]
Username.
Definition connaccount.h:62
char pass[256]
Password.
Definition connaccount.h:63
char host[128]
Server to login to.
Definition connaccount.h:60
unsigned short port
Port to connect to.
Definition connaccount.h:64
Conn private Module data.
Definition module_data.h:38
int skip_mode_ex_data_index
OpenSSL skip mode extra data index.
Definition module_data.h:49
int host_ex_data_index
OpenSSL host extra data index.
Definition module_data.h:48
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:55
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49