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