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>
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
82#if (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x2070000fL))
83#define SSL_has_pending SSL_pending
106 unsigned char isopen;
126 X509_STORE *store = SSL_CTX_get_cert_store(ctx);
129 store = X509_STORE_new();
130 SSL_CTX_set_cert_store(ctx, store);
139 while (NULL != PEM_read_X509(fp, &cert, NULL, NULL))
141 if ((X509_cmp_current_time(X509_get0_notBefore(cert)) >= 0) ||
142 (X509_cmp_current_time(X509_get0_notAfter(cert)) <= 0))
144 char buf[256] = { 0 };
146 X509_NAME_oneline(X509_get_subject_name(cert), buf,
sizeof(buf)));
150 X509_STORE_add_cert(store, cert);
154 if (ERR_GET_REASON(ERR_peek_last_error()) != PEM_R_NO_START_LINE)
173#ifdef HAVE_SSL_PARTIAL_CHAIN
174 X509_VERIFY_PARAM *param = NULL;
177 if (c_ssl_verify_partial_chains)
179 param = X509_VERIFY_PARAM_new();
182 X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_PARTIAL_CHAIN);
183 if (SSL_CTX_set1_param(ctx, param) == 0)
188 X509_VERIFY_PARAM_free(param);
211 struct stat st = { 0 };
214 if (stat(file, &st) == -1)
215 return (errno == ENOENT) ? 0 : -1;
220 if ((st.st_uid != getuid()) || ((st.st_mode & (S_IWGRP | S_IRGRP)) != 0) ||
221 ((st.st_mode & (S_IWOTH | S_IROTH)) != 0))
223 mutt_error(
_(
"%s has insecure permissions"), file);
231 n = RAND_load_file(file, -1);
241static void ssl_err(
struct SslSockData *data,
int err)
243 int e = SSL_get_error(data->ssl, err);
248 case SSL_ERROR_ZERO_RETURN:
251 case SSL_ERROR_SYSCALL:
256 const char *errmsg = NULL;
257 unsigned long sslerr;
261 case SSL_ERROR_SYSCALL:
262 errmsg =
"I/O error";
264 case SSL_ERROR_WANT_ACCEPT:
265 errmsg =
"retry accept";
267 case SSL_ERROR_WANT_CONNECT:
268 errmsg =
"retry connect";
270 case SSL_ERROR_WANT_READ:
271 errmsg =
"retry read";
273 case SSL_ERROR_WANT_WRITE:
274 errmsg =
"retry write";
276 case SSL_ERROR_WANT_X509_LOOKUP:
277 errmsg =
"retry x509 lookup";
279 case SSL_ERROR_ZERO_RETURN:
280 errmsg =
"SSL connection closed";
283 sslerr = ERR_get_error();
293 errmsg = strerror(errno);
297 errmsg = ERR_error_string(sslerr, NULL);
301 errmsg =
"unknown error";
312 BIO *bio = BIO_new(BIO_s_mem());
315 ERR_print_errors(bio);
318 long buflen = BIO_get_mem_data(bio, &buf);
322 memcpy(output, buf, buflen);
323 output[buflen] =
'\0';
351 return snprintf(buf, buflen,
"%s", cac->
pass);
360 mutt_error(
_(
"SSL disabled due to the lack of entropy"));
374 static char data[128];
376 if (!name || (X509_NAME_get_text_by_NID(name, nid, data,
sizeof(data)) < 0))
390 unsigned char md[EVP_MAX_MD_SIZE];
393 if (X509_digest(cert, hashfunc(), md, &n) == 0)
399 for (
unsigned int i = 0; i < n; i++)
404 if (((i % 2) == 1) && (i < (n - 1)))
423 bio = BIO_new(BIO_s_mem());
426 if (ASN1_TIME_print(bio, tm))
427 (void) BIO_read(bio, buf,
sizeof(buf));
444 unsigned char *peermd,
unsigned int peermdlen)
446 unsigned char md[EVP_MAX_MD_SIZE];
451 if ((X509_subject_name_cmp(cert, peercert) != 0) ||
452 (X509_issuer_name_cmp(cert, peercert) != 0))
457 if (!X509_digest(cert, EVP_sha256(), md, &mdlen) || (peermdlen != mdlen))
460 if (memcmp(peermd, md, mdlen) != 0)
476 if (c_ssl_verify_dates ==
MUTT_NO)
479 if (X509_cmp_current_time(X509_get0_notBefore(peercert)) >= 0)
484 mutt_error(
_(
"Server certificate is not yet valid"));
489 if (X509_cmp_current_time(X509_get0_notAfter(peercert)) <= 0)
510 const char *cmp1 = NULL, *cmp2 = NULL;
515 cmp2 = strchr(hostname,
'.');
527 if ((*cmp1 ==
'\0') || (*cmp2 ==
'\0'))
532 if (strcasecmp(cmp1, cmp2) != 0)
556 static bool init_complete =
false;
561 if (RAND_status() != 1)
578 RAND_write_file(RAND_file_name(path->
data, path->
dsize));
582 if (RAND_status() != 1)
584 mutt_error(
_(
"Failed to find enough entropy on your system"));
591#if (defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x10100000L)) || \
592 (defined(LIBRESSL_VERSION_NUMBER))
595 SSL_load_error_strings();
598 init_complete =
true;
610 if (!c_ssl_client_cert)
614 SSL_CTX_set_default_passwd_cb_userdata(ssldata->sctx, &conn->
account);
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);
641 unsigned char peermd[EVP_MAX_MD_SIZE];
642 unsigned int peermdlen;
645 if (!X509_digest(peercert, EVP_sha256(), peermd, &peermdlen) || !SslSessionCerts)
650 for (
int i = sk_X509_num(SslSessionCerts) - 1; i >= 0; i--)
652 cert = sk_X509_value(SslSessionCerts, i);
670 unsigned char peermd[EVP_MAX_MD_SIZE];
671 unsigned int peermdlen;
681 if (!X509_digest(peercert, EVP_sha256(), peermd, &peermdlen))
687 while (PEM_read_X509(fp, &cert, NULL, NULL))
714static int check_host(X509 *x509cert,
const char *hostname,
char *err,
size_t errlen)
718 char *hostname_ascii = NULL;
720 X509_NAME *x509_subject = NULL;
724 STACK_OF(GENERAL_NAME) * subj_alt_names;
725 int subj_alt_names_count;
726 GENERAL_NAME *subj_alt_name = NULL;
730 bool has_san_dns =
false;
746 subj_alt_names = X509_get_ext_d2i(x509cert, NID_subject_alt_name, NULL, NULL);
749 subj_alt_names_count = sk_GENERAL_NAME_num(subj_alt_names);
750 for (
int i = 0; i < subj_alt_names_count; i++)
752 subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
753 if (subj_alt_name->type == GEN_DNS)
756 if ((subj_alt_name->d.ia5->length >= 0) &&
758 (
size_t) subj_alt_name->d.ia5->length) &&
760 (
char *) (subj_alt_name->d.ia5->data))))
766 GENERAL_NAMES_free(subj_alt_names);
769 if (!match_found && !has_san_dns)
773 x509_subject = X509_get_subject_name(x509cert);
782 bufsize = X509_NAME_get_text_by_NID(x509_subject, NID_commonName, NULL, 0);
786 mutt_str_copy(err,
_(
"can't get certificate common name"), errlen);
791 if (X509_NAME_get_text_by_NID(x509_subject, NID_commonName, buf, bufsize) == -1)
794 mutt_str_copy(err,
_(
"can't get certificate common name"), errlen);
808 snprintf(err, errlen,
_(
"certificate owner does not match hostname %s"), hostname);
816 FREE(&hostname_ascii);
841 if (!SslSessionCerts)
842 SslSessionCerts = sk_X509_new_null();
843 return sk_X509_push(SslSessionCerts, X509_dup(c));
853static void add_cert(
const char *title, X509 *cert,
bool issuer,
struct StringArray *carr)
855 static const int part[] = {
857 NID_pkcs9_emailAddress,
858 NID_organizationName,
859 NID_organizationalUnitName,
861 NID_stateOrProvinceName,
865 X509_NAME *x509 = NULL;
867 x509 = X509_get_issuer_name(cert);
869 x509 = X509_get_subject_name(cert);
876 for (
size_t i = 0; i <
countof(part); i++)
910 add_cert(
_(
"This certificate belongs to:"), cert,
false, &carr);
912 add_cert(
_(
"This certificate was issued by:"), cert,
true, &carr);
929 buf->
data[39] =
'\0';
936 bool allow_skip =
false;
938#ifdef HAVE_SSL_PARTIAL_CHAIN
940 if ((idx != 0) && c_ssl_verify_partial_chains)
944 char title[256] = { 0 };
945 snprintf(title,
sizeof(title),
946 _(
"SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
953 allow_always = allow_always && c_certificate_file &&
957 if ((rc == 3) && !allow_always)
974 if (PEM_write_X509(fp, cert))
982 mutt_error(
_(
"Warning: Couldn't save certificate"));
1012 char buf[256] = { 0 };
1014 SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
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));
1040 X509_NAME_oneline(X509_get_subject_name(cert), buf,
sizeof(buf)),
1041 preverify_ok, skip_mode);
1043#ifdef HAVE_SSL_PARTIAL_CHAIN
1048 if (c_ssl_verify_partial_chains)
1050 static int last_pos = 0;
1051 static X509 *last_cert = NULL;
1052 if (skip_mode && preverify_ok && (pos == last_pos) && last_cert)
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) &&
1066 X509_free(last_cert);
1067 last_cert = X509_dup(cert);
1082 if ((pos == 0) && (c_ssl_verify_host !=
MUTT_NO))
1084 if (
check_host(cert, host, buf,
sizeof(buf)) == 0)
1086 mutt_error(
_(
"Certificate host check failed: %s"), buf);
1094 if (!preverify_ok || skip_mode)
1106 int err = X509_STORE_CTX_get_error(ctx);
1107 snprintf(buf,
sizeof(buf),
"%s (%d)", X509_verify_cert_error_string(err), err);
1130 const char *errmsg = NULL;
1166 SSL_set_mode(ssldata->ssl, SSL_MODE_AUTO_RETRY);
1168 if (!SSL_set_tlsext_host_name(ssldata->ssl, conn->
account.
host))
1173 mutt_error(
_(
"Warning: unable to set TLS SNI host name"));
1179 err = SSL_connect(ssldata->ssl);
1183 if (BIO_should_retry(SSL_get_rbio(ssldata->ssl)))
1186 switch (SSL_get_error(ssldata->ssl, err))
1188 case SSL_ERROR_SYSCALL:
1189 errmsg =
_(
"I/O error");
1192 errmsg = ERR_error_string(ERR_get_error(), NULL);
1195 errmsg =
_(
"unknown error");
1228 sockdata(conn)->sctx = SSL_CTX_new(SSLv23_client_method());
1240#ifdef SSL_OP_NO_TLSv1_3
1242 if (!c_ssl_use_tlsv1_3)
1243 SSL_CTX_set_options(
sockdata(conn)->sctx, SSL_OP_NO_TLSv1_3);
1246#ifdef SSL_OP_NO_TLSv1_2
1248 if (!c_ssl_use_tlsv1_2)
1249 SSL_CTX_set_options(
sockdata(conn)->sctx, SSL_OP_NO_TLSv1_2);
1253#ifdef SSL_OP_NO_TLSv1_1
1254 SSL_CTX_set_options(
sockdata(conn)->sctx, SSL_OP_NO_TLSv1_1);
1256#ifdef SSL_OP_NO_TLSv1
1257 SSL_CTX_set_options(
sockdata(conn)->sctx, SSL_OP_NO_TLSv1);
1259 SSL_CTX_set_options(
sockdata(conn)->sctx, SSL_OP_NO_SSLv3);
1260 SSL_CTX_set_options(
sockdata(conn)->sctx, SSL_OP_NO_SSLv2);
1263 if (c_ssl_use_system_certs)
1265 if (!SSL_CTX_set_default_verify_paths(
sockdata(conn)->sctx))
1281 SSL_CTX_set_cipher_list(
sockdata(conn)->sctx, c_ssl_ciphers);
1286 mutt_error(
_(
"Warning: error enabling ssl_verify_partial_chains"));
1296 conn->
ssf = SSL_CIPHER_get_bits(SSL_get_current_cipher(
sockdata(conn)->ssl), &maxbits);
1304 SSL_CTX_free(
sockdata(conn)->sctx);
1320 if (SSL_has_pending(
sockdata(conn)->ssl))
1346 struct SslSockData *data =
sockdata(conn);
1350 rc = SSL_read(data->ssl, buf, count);
1355 if (
SigInt && (errno == EINTR))
1359 else if (BIO_should_retry(SSL_get_rbio(data->ssl)))
1378 if (!conn || !conn->
sockdata || !buf || (count == 0))
1381 struct SslSockData *data =
sockdata(conn);
1385 rc = SSL_write(data->ssl, buf, count);
1390 if (
SigInt && (errno == EINTR))
1394 else if (BIO_should_retry(SSL_get_wbio(data->ssl)))
1409 struct SslSockData *data =
sockdata(conn);
1414 SSL_shutdown(data->ssl);
1416 SSL_free(data->ssl);
1418 SSL_CTX_free(data->sctx);
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
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.
An open network connection (socket)
Convenience wrapper for the core headers.
#define mutt_file_fclose(FP)
#define mutt_file_fopen(PATH, MODE)
bool OptGui
(pseudo) when the gui (and curses) are started
static int ssl_socket_close_and_restore(struct Connection *conn)
Close an SSL Connection and restore Connection callbacks - Implements Connection::close() -.
int raw_socket_close(struct Connection *conn)
Close a socket - Implements Connection::close() -.
static int ssl_socket_close(struct Connection *conn)
Close an SSL connection - Implements Connection::close() -.
int raw_socket_open(struct Connection *conn)
Open a socket - Implements Connection::open() -.
static int ssl_socket_open_err(struct Connection *conn)
Error callback for opening an SSL connection - Implements Connection::open() -.
static int ssl_socket_open(struct Connection *conn)
Open an SSL socket - Implements Connection::open() -.
static int ssl_socket_poll(struct Connection *conn, time_t wait_secs)
Check if any data is waiting on a socket - Implements Connection::poll() -.
int raw_socket_poll(struct Connection *conn, time_t wait_secs)
Check if any data is waiting on a socket - Implements Connection::poll() -.
static int ssl_socket_read(struct Connection *conn, char *buf, size_t count)
Read data from an SSL socket - Implements Connection::read() -.
int raw_socket_read(struct Connection *conn, char *buf, size_t len)
Read data from a socket - Implements Connection::read() -.
static int ssl_socket_write(struct Connection *conn, const char *buf, size_t count)
Write data to an SSL socket - Implements Connection::write() -.
int raw_socket_write(struct Connection *conn, const char *buf, size_t count)
Write data to a socket - Implements Connection::write() -.
int dlg_certificate(const char *title, struct StringArray *carr, bool allow_always, bool allow_skip)
Ask the user to validate the certificate -.
#define mutt_message(...)
#define mutt_debug(LEVEL,...)
int mutt_idna_to_ascii_lz(const char *input, char **output, uint8_t flags)
Convert a domain to Punycode.
@ LL_DEBUG2
Log at debug level 2.
@ LL_DEBUG1
Log at debug level 1.
#define FREE(x)
Free memory and set the pointer to NULL.
#define MUTT_MEM_CALLOC(n, type)
#define MUTT_MEM_MALLOC(n, type)
@ MODULE_ID_CONN
ModuleConn, Network connections
Convenience wrapper for the library headers.
char * mutt_str_dup(const char *str)
Copy a string, safely.
int mutt_str_asprintf(char **strp, const char *fmt,...)
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
const char * mutt_str_getenv(const char *name)
Get an environment variable.
void string_array_clear(struct StringArray *arr)
Free all memory of a StringArray.
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
static int check_host(X509 *x509cert, const char *hostname, char *err, size_t errlen)
Check the host on the certificate.
static int add_entropy(const char *file)
Add a source of random numbers.
static char * x509_get_part(X509_NAME *name, int nid)
Retrieve from X509 data.
static char * asn1time_to_string(ASN1_UTCTIME *tm)
Convert a time to a string.
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
static STACK_OF(X509)
index for storing hostname as application specific data in SSL structure
static void x509_fingerprint(struct Buffer *buf, X509 *cert, const EVP_MD *(*hashfunc)(void))
Generate a fingerprint for an X509 certificate.
static int ssl_setup(struct Connection *conn)
Set up SSL on the Connection.
static bool check_certificate_cache(X509 *peercert)
Is the X509 Certificate in the cache?
static int ssl_passwd_cb(char *buf, int buflen, int rwflag, void *userdata)
Callback to get a password.
static int ssl_negotiate(struct Connection *conn, struct SslSockData *ssldata)
Attempt to negotiate SSL over the wire.
static bool certificates_equal(X509 *cert, X509 *peercert, unsigned char *peermd, unsigned int peermdlen)
Compare two X509 certificated.
static bool ssl_load_certificates(SSL_CTX *ctx)
Load certificates and filter out the expired ones.
static void ssl_dprint_err_stack(void)
Dump the SSL error stack.
static int ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
Certificate verification callback.
static struct SslSockData * sockdata(struct Connection *conn)
Get a Connection's socket data.
static bool ssl_set_verify_partial(SSL_CTX *ctx)
Allow verification using partial chains (with no root)
static int ssl_cache_trusted_cert(X509 *c)
Cache a trusted certificate.
static int ssl_init(void)
Initialise the SSL library.
static void add_cert(const char *title, X509 *cert, bool issuer, struct StringArray *carr)
Look up certificate info and save it to a list.
static void ssl_err(struct SslSockData *data, int err)
Display an SSL error message.
static void ssl_get_client_cert(struct SslSockData *ssldata, struct Connection *conn)
Get the client certificate for an SSL connection.
static bool check_certificate_expiration(X509 *peercert, bool silent)
Check if a certificate has expired.
static bool check_certificate_file(X509 *peercert)
Read and check a certificate file.
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.
static bool hostname_match(const char *hostname, const char *certname)
Does the hostname match the certificate.
int mutt_ssl_socket_setup(struct Connection *conn)
Set up SSL socket mulitplexor.
static bool check_certificate_by_digest(X509 *peercert)
Validate a certificate by its digest.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
@ MUTT_NO
User answered 'No', or assume 'No'.
volatile sig_atomic_t SigInt
true after SIGINT is received
Handling of SSL encryption.
String manipulation buffer.
size_t dsize
Length of data.
char * data
Pointer to data.
Login details for a remote server.
char host[128]
Server to login to.
unsigned short port
Port to connect to.
Conn private Module data.
int skip_mode_ex_data_index
OpenSSL skip mode extra data index.
int host_ex_data_index
OpenSSL host extra data index.
void * sockdata
Backend-specific socket data.
int(* poll)(struct Connection *conn, time_t wait_secs)
int(* write)(struct Connection *conn, const char *buf, size_t count)
unsigned int ssf
Security strength factor, in bits (see notes)
int(* close)(struct Connection *conn)
struct ConnAccount account
Account details: username, password, etc.
int(* open)(struct Connection *conn)
int fd
Socket file descriptor.
int(* read)(struct Connection *conn, char *buf, size_t count)
Container for Accounts, Notifications.
char * home_dir
User's home directory.
struct ConfigSubset * sub
Inherited config items.