NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
openssl.c File Reference

Handling of OpenSSL encryption. More...

#include "config.h"
#include <errno.h>
#include <openssl/asn1.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/obj_mac.h>
#include <openssl/opensslv.h>
#include <openssl/ossl_typ.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/safestack.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "private.h"
#include "mutt/lib.h"
#include "address/lib.h"
#include "config/lib.h"
#include "core/lib.h"
#include "lib.h"
#include "mutt_logging.h"
#include "ssl.h"
+ Include dependency graph for openssl.c:

Go to the source code of this file.

Functions

static STACK_OF (X509)
 SSL socket data -. More...
 
static int ssl_load_certificates (SSL_CTX *ctx)
 Load certificates and filter out the expired ones. More...
 
static int ssl_set_verify_partial (SSL_CTX *ctx)
 Allow verification using partial chains (with no root) More...
 
static int add_entropy (const char *file)
 Add a source of random numbers. More...
 
static void ssl_err (struct SslSockData *data, int err)
 Display an SSL error message. More...
 
static void ssl_dprint_err_stack (void)
 Dump the SSL error stack. More...
 
static int ssl_passwd_cb (char *buf, int buflen, int rwflag, void *userdata)
 Callback to get a password. More...
 
static int ssl_socket_open_err (struct Connection *conn)
 Error callback for opening an SSL connection - Implements Connection::open() -. More...
 
static char * x509_get_part (X509_NAME *name, int nid)
 Retrieve from X509 data. More...
 
static void x509_fingerprint (char *s, int l, X509 *cert, const EVP_MD *(*hashfunc)(void))
 Generate a fingerprint for an X509 certificate. More...
 
static char * asn1time_to_string (ASN1_UTCTIME *tm)
 Convert a time to a string. More...
 
static bool compare_certificates (X509 *cert, X509 *peercert, unsigned char *peermd, unsigned int peermdlen)
 Compare two X509 certificated. More...
 
static bool check_certificate_expiration (X509 *peercert, bool silent)
 Check if a certificate has expired. More...
 
static bool hostname_match (const char *hostname, const char *certname)
 Does the hostname match the certificate. More...
 
static int ssl_init (void)
 Initialise the SSL library. More...
 
static void ssl_get_client_cert (struct SslSockData *ssldata, struct Connection *conn)
 Get the client certificate for an SSL connection. More...
 
static int ssl_socket_close_and_restore (struct Connection *conn)
 Close an SSL Connection and restore Connection callbacks - Implements Connection::close() -. More...
 
static bool check_certificate_cache (X509 *peercert)
 Is the X509 Certificate in the cache? More...
 
static bool check_certificate_file (X509 *peercert)
 Read and check a certificate file. More...
 
static int check_host (X509 *x509cert, const char *hostname, char *err, size_t errlen)
 Check the host on the certificate. More...
 
static bool check_certificate_by_digest (X509 *peercert)
 Validate a certificate by its digest. More...
 
static int ssl_cache_trusted_cert (X509 *c)
 Cache a trusted certificate. More...
 
static void add_cert (const char *title, X509 *cert, bool issuer, struct ListHead *list)
 Look up certificate info and save it to a list. More...
 
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. More...
 
static int ssl_verify_callback (int preverify_ok, X509_STORE_CTX *ctx)
 Certificate verification callback. More...
 
static int ssl_negotiate (struct Connection *conn, struct SslSockData *ssldata)
 Attempt to negotiate SSL over the wire. More...
 
static struct SslSockData * sockdata (struct Connection *conn)
 Get a Connection's socket data. More...
 
static int ssl_setup (struct Connection *conn)
 Set up SSL on the Connection. More...
 
static int ssl_socket_poll (struct Connection *conn, time_t wait_secs)
 Check whether a socket read would block - Implements Connection::poll() -. More...
 
static int ssl_socket_open (struct Connection *conn)
 Open an SSL socket - Implements Connection::open() -. More...
 
static int ssl_socket_read (struct Connection *conn, char *buf, size_t count)
 Read data from an SSL socket - Implements Connection::read() -. More...
 
static int ssl_socket_write (struct Connection *conn, const char *buf, size_t count)
 Write data to an SSL socket - Implements Connection::write() -. More...
 
static int ssl_socket_close (struct Connection *conn)
 Close an SSL connection - Implements Connection::close() -. More...
 
int mutt_ssl_starttls (struct Connection *conn)
 Negotiate TLS over an already opened connection. More...
 
int mutt_ssl_socket_setup (struct Connection *conn)
 Set up SSL socket mulitplexor. More...
 

Variables

static int HostExDataIndex = -1
 
static int SkipModeExDataIndex = -1
 

Detailed Description

Handling of OpenSSL encryption.

Authors
  • Tommi Komulainen

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file openssl.c.

Function Documentation

◆ STACK_OF()

static STACK_OF ( X509  )
static

SSL socket data -.

Definition at line 90 of file openssl.c.

98 {
99  SSL_CTX *sctx;
100  SSL *ssl;
101  unsigned char isopen;
102 };
+ Here is the caller graph for this function:

◆ ssl_load_certificates()

static int ssl_load_certificates ( SSL_CTX *  ctx)
static

Load certificates and filter out the expired ones.

Parameters
ctxSSL context
Return values
1Success
0Error

ssl certificate verification can behave strangely if there are expired certs loaded into the trusted store. This function filters out expired certs.

Previously the code used this form: SSL_CTX_load_verify_locations (ssldata->ctx, $certificate_file, NULL);

Definition at line 116 of file openssl.c.

117 {
118  int rc = 1;
119 
120  mutt_debug(LL_DEBUG2, "loading trusted certificates\n");
121  X509_STORE *store = SSL_CTX_get_cert_store(ctx);
122  if (!store)
123  {
124  store = X509_STORE_new();
125  SSL_CTX_set_cert_store(ctx, store);
126  }
127 
128  const char *const c_certificate_file =
129  cs_subset_path(NeoMutt->sub, "certificate_file");
130  FILE *fp = mutt_file_fopen(c_certificate_file, "rt");
131  if (!fp)
132  return 0;
133 
134  X509 *cert = NULL;
135  while (NULL != PEM_read_X509(fp, &cert, NULL, NULL))
136  {
137  if ((X509_cmp_current_time(X509_get0_notBefore(cert)) >= 0) ||
138  (X509_cmp_current_time(X509_get0_notAfter(cert)) <= 0))
139  {
140  char buf[256];
141  mutt_debug(LL_DEBUG2, "filtering expired cert: %s\n",
142  X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)));
143  }
144  else
145  {
146  X509_STORE_add_cert(store, cert);
147  }
148  }
149  /* PEM_read_X509 sets the error NO_START_LINE on eof */
150  if (ERR_GET_REASON(ERR_peek_last_error()) != PEM_R_NO_START_LINE)
151  rc = 0;
152  ERR_clear_error();
153 
154  X509_free(cert);
155  mutt_file_fclose(&fp);
156 
157  return rc;
158 }
Container for Accounts, Notifications.
Definition: neomutt.h:36
Log at debug level 2.
Definition: logging.h:41
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ssl_set_verify_partial()

static int ssl_set_verify_partial ( SSL_CTX *  ctx)
static

Allow verification using partial chains (with no root)

Parameters
ctxSSL context
Return values
0Success
-1Error

Definition at line 166 of file openssl.c.

167 {
168  int rc = 0;
169 #ifdef HAVE_SSL_PARTIAL_CHAIN
170  X509_VERIFY_PARAM *param = NULL;
171 
172  const bool c_ssl_verify_partial_chains =
173  cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
174  if (c_ssl_verify_partial_chains)
175  {
176  param = X509_VERIFY_PARAM_new();
177  if (param)
178  {
179  X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_PARTIAL_CHAIN);
180  if (SSL_CTX_set1_param(ctx, param) == 0)
181  {
182  mutt_debug(LL_DEBUG2, "SSL_CTX_set1_param() failed\n");
183  rc = -1;
184  }
185  X509_VERIFY_PARAM_free(param);
186  }
187  else
188  {
189  mutt_debug(LL_DEBUG2, "X509_VERIFY_PARAM_new() failed\n");
190  rc = -1;
191  }
192  }
193 #endif
194  return rc;
195 }
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
Container for Accounts, Notifications.
Definition: neomutt.h:36
Log at debug level 2.
Definition: logging.h:41
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ add_entropy()

static int add_entropy ( const char *  file)
static

Add a source of random numbers.

Parameters
fileRandom device
Return values
>0Success, number of bytes read from the source
-1Error

Definition at line 203 of file openssl.c.

204 {
205  if (!file)
206  return 0;
207 
208  struct stat st;
209  int n = -1;
210 
211  if (stat(file, &st) == -1)
212  return (errno == ENOENT) ? 0 : -1;
213 
214  mutt_message(_("Filling entropy pool: %s..."), file);
215 
216  /* check that the file permissions are secure */
217  if ((st.st_uid != getuid()) || ((st.st_mode & (S_IWGRP | S_IRGRP)) != 0) ||
218  ((st.st_mode & (S_IWOTH | S_IROTH)) != 0))
219  {
220  mutt_error(_("%s has insecure permissions"), file);
221  return -1;
222  }
223 
224 #ifdef HAVE_RAND_EGD
225  n = RAND_egd(file);
226 #endif
227  if (n <= 0)
228  n = RAND_load_file(file, -1);
229 
230  return n;
231 }
#define mutt_error(...)
Definition: logging.h:88
#define _(a)
Definition: message.h:28
#define mutt_message(...)
Definition: logging.h:87
+ Here is the caller graph for this function:

◆ ssl_err()

static void ssl_err ( struct SslSockData *  data,
int  err 
)
static

Display an SSL error message.

Parameters
dataSSL socket data
errSSL error code

Definition at line 238 of file openssl.c.

239 {
240  int e = SSL_get_error(data->ssl, err);
241  switch (e)
242  {
243  case SSL_ERROR_NONE:
244  return;
245  case SSL_ERROR_ZERO_RETURN:
246  data->isopen = 0;
247  break;
248  case SSL_ERROR_SYSCALL:
249  data->isopen = 0;
250  break;
251  }
252 
253  const char *errmsg = NULL;
254  unsigned long sslerr;
255 
256  switch (e)
257  {
258  case SSL_ERROR_SYSCALL:
259  errmsg = "I/O error";
260  break;
261  case SSL_ERROR_WANT_ACCEPT:
262  errmsg = "retry accept";
263  break;
264  case SSL_ERROR_WANT_CONNECT:
265  errmsg = "retry connect";
266  break;
267  case SSL_ERROR_WANT_READ:
268  errmsg = "retry read";
269  break;
270  case SSL_ERROR_WANT_WRITE:
271  errmsg = "retry write";
272  break;
273  case SSL_ERROR_WANT_X509_LOOKUP:
274  errmsg = "retry x509 lookup";
275  break;
276  case SSL_ERROR_ZERO_RETURN:
277  errmsg = "SSL connection closed";
278  break;
279  case SSL_ERROR_SSL:
280  sslerr = ERR_get_error();
281  switch (sslerr)
282  {
283  case 0:
284  switch (err)
285  {
286  case 0:
287  errmsg = "EOF";
288  break;
289  default:
290  errmsg = strerror(errno);
291  }
292  break;
293  default:
294  errmsg = ERR_error_string(sslerr, NULL);
295  }
296  break;
297  default:
298  errmsg = "unknown error";
299  }
300 
301  mutt_debug(LL_DEBUG1, "SSL error: %s\n", errmsg);
302 }
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Log at debug level 1.
Definition: logging.h:40
+ Here is the caller graph for this function:

◆ ssl_dprint_err_stack()

static void ssl_dprint_err_stack ( void  )
static

Dump the SSL error stack.

Definition at line 307 of file openssl.c.

308 {
309  BIO *bio = BIO_new(BIO_s_mem());
310  if (!bio)
311  return;
312  ERR_print_errors(bio);
313 
314  char *buf = NULL;
315  long buflen = BIO_get_mem_data(bio, &buf);
316  if (buflen > 0)
317  {
318  char *output = mutt_mem_malloc(buflen + 1);
319  memcpy(output, buf, buflen);
320  output[buflen] = '\0';
321  mutt_debug(LL_DEBUG1, "SSL error stack: %s\n", output);
322  FREE(&output);
323  }
324  BIO_free(bio);
325 }
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Log at debug level 1.
Definition: logging.h:40
#define FREE(x)
Definition: memory.h:40
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ssl_passwd_cb()

static int ssl_passwd_cb ( char *  buf,
int  buflen,
int  rwflag,
void *  userdata 
)
static

Callback to get a password.

Parameters
bufBuffer for the password
buflenLength of the buffer
rwflag0 if writing, 1 if reading (UNUSED)
userdataConnAccount whose password is requested
Return values
>0Success, number of chars written to buf
0Error

Definition at line 336 of file openssl.c.

337 {
338  struct ConnAccount *cac = userdata;
339 
340  if (mutt_account_getuser(cac) < 0)
341  return 0;
342 
343  mutt_debug(LL_DEBUG2, "getting password for %s@%s:%u\n", cac->user, cac->host, cac->port);
344 
345  if (mutt_account_getpass(cac) < 0)
346  return 0;
347 
348  return snprintf(buf, buflen, "%s", cac->pass);
349 }
int mutt_account_getuser(struct ConnAccount *cac)
Retrieve username into ConnAccount, if necessary.
Definition: connaccount.c:47
char user[128]
Username.
Definition: connaccount.h:55
char host[128]
Server to login to.
Definition: connaccount.h:53
Log at debug level 2.
Definition: logging.h:41
char pass[256]
Password.
Definition: connaccount.h:56
unsigned short port
Port to connect to.
Definition: connaccount.h:57
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Login details for a remote server.
Definition: connaccount.h:51
int mutt_account_getpass(struct ConnAccount *cac)
Fetch password into ConnAccount, if necessary.
Definition: connaccount.c:110
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ x509_get_part()

static char* x509_get_part ( X509_NAME *  name,
int  nid 
)
static

Retrieve from X509 data.

Parameters
nameName of data to retrieve
nidID of the item to retrieve
Return values
ptrRetrieved data

The returned pointer is to a static buffer, so it must not be free()'d.

Definition at line 369 of file openssl.c.

370 {
371  static char data[128];
372 
373  if (!name || (X509_NAME_get_text_by_NID(name, nid, data, sizeof(data)) < 0))
374  return NULL;
375 
376  return data;
377 }
+ Here is the caller graph for this function:

◆ x509_fingerprint()

static void x509_fingerprint ( char *  s,
int  l,
X509 *  cert,
const EVP_MD *(*)(void)  hashfunc 
)
static

Generate a fingerprint for an X509 certificate.

Parameters
sBuffer for fingerprint
lLength of buffer
certCertificate
hashfuncHashing function

Definition at line 386 of file openssl.c.

387 {
388  unsigned char md[EVP_MAX_MD_SIZE];
389  unsigned int n;
390 
391  if (X509_digest(cert, hashfunc(), md, &n) == 0) // Failure
392  {
393  snprintf(s, l, "%s", _("[unable to calculate]"));
394  }
395  else
396  {
397  for (unsigned int i = 0; i < n; i++)
398  {
399  char ch[8];
400  snprintf(ch, sizeof(ch), "%02X%s", md[i], ((i % 2) ? " " : ""));
401  mutt_str_cat(s, l, ch);
402  }
403  }
404 }
#define _(a)
Definition: message.h:28
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:385
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ asn1time_to_string()

static char* asn1time_to_string ( ASN1_UTCTIME *  tm)
static

Convert a time to a string.

Parameters
tmTime to convert
Return values
ptrTime string

The returned pointer is to a static buffer, so it must not be free()'d.

Definition at line 413 of file openssl.c.

414 {
415  static char buf[64];
416  BIO *bio = NULL;
417 
418  mutt_str_copy(buf, _("[invalid date]"), sizeof(buf));
419 
420  bio = BIO_new(BIO_s_mem());
421  if (bio)
422  {
423  if (ASN1_TIME_print(bio, tm))
424  (void) BIO_read(bio, buf, sizeof(buf));
425  BIO_free(bio);
426  }
427 
428  return buf;
429 }
#define _(a)
Definition: message.h:28
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:749
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ compare_certificates()

static bool compare_certificates ( X509 *  cert,
X509 *  peercert,
unsigned char *  peermd,
unsigned int  peermdlen 
)
static

Compare two X509 certificated.

Parameters
certCertificate
peercertPeer certificate
peermdPeer certificate message digest
peermdlenLength of peer certificate message digest
Return values
trueCertificates match
falseCertificates differ

Definition at line 440 of file openssl.c.

442 {
443  unsigned char md[EVP_MAX_MD_SIZE];
444  unsigned int mdlen;
445 
446  /* Avoid CPU-intensive digest calculation if the certificates are
447  * not even remotely equal. */
448  if ((X509_subject_name_cmp(cert, peercert) != 0) ||
449  (X509_issuer_name_cmp(cert, peercert) != 0))
450  {
451  return false;
452  }
453 
454  if (!X509_digest(cert, EVP_sha256(), md, &mdlen) || (peermdlen != mdlen))
455  return false;
456 
457  if (memcmp(peermd, md, mdlen) != 0)
458  return false;
459 
460  return true;
461 }
+ Here is the caller graph for this function:

◆ check_certificate_expiration()

static bool check_certificate_expiration ( X509 *  peercert,
bool  silent 
)
static

Check if a certificate has expired.

Parameters
peercertCertificate to check
silentIf true, don't notify the user if the certificate has expired
Return values
trueCertificate is valid
falseCertificate has expired (or hasn't yet become valid)

Definition at line 470 of file openssl.c.

471 {
472  const bool c_ssl_verify_dates =
473  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 }
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
#define mutt_error(...)
Definition: logging.h:88
#define _(a)
Definition: message.h:28
Container for Accounts, Notifications.
Definition: neomutt.h:36
Log at debug level 2.
Definition: logging.h:41
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:38
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ hostname_match()

static bool hostname_match ( const char *  hostname,
const char *  certname 
)
static

Does the hostname match the certificate.

Parameters
hostnameHostname
certnameCertificate
Return values
trueHostname matches the certificate

Definition at line 506 of file openssl.c.

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 }
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:593
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ssl_init()

static int ssl_init ( void  )
static

Initialise the SSL library.

Return values
0Success
-1Error

OpenSSL library needs to be fed with sufficient entropy. On systems with /dev/urandom, this is done transparently by the library itself, on other systems we need to fill the entropy pool ourselves.

Even though only OpenSSL 0.9.5 and later will complain about the lack of entropy, we try to our best and fill the pool with older versions also. (That's the reason for the ugly ifdefs and macros, otherwise I could have simply ifdef'd the whole ssl_init function)

Definition at line 552 of file openssl.c.

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 = mutt_buffer_pool_get();
563  const char *const c_entropy_file =
564  cs_subset_path(NeoMutt->sub, "entropy_file");
565  add_entropy(c_entropy_file);
566  add_entropy(RAND_file_name(path->data, path->dsize));
567 
568 /* load entropy from egd sockets */
569 #ifdef HAVE_RAND_EGD
570  add_entropy(mutt_str_getenv("EGDSOCKET"));
571  mutt_buffer_printf(path, "%s/.entropy", NONULL(HomeDir));
573  add_entropy(TMPDIR "/entropy");
574 #endif
575 
576  /* shuffle $RANDFILE (or ~/.rnd if unset) */
577  RAND_write_file(RAND_file_name(path->data, path->dsize));
579 
581  if (RAND_status() != 1)
582  {
583  mutt_error(_("Failed to find enough entropy on your system"));
584  return -1;
585  }
586  }
587 
588 /* OpenSSL performs automatic initialization as of 1.1.
589  * However LibreSSL does not (as of 2.8.3). */
590 #if (defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x10100000L)) || \
591  (defined(LIBRESSL_VERSION_NUMBER))
592  /* I don't think you can do this just before reading the error. The call
593  * itself might clobber the last SSL error. */
594  SSL_load_error_strings();
595  SSL_library_init();
596 #endif
597  init_complete = true;
598  return 0;
599 }
#define NONULL(x)
Definition: string2.h:37
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define mutt_error(...)
Definition: logging.h:88
static int add_entropy(const char *file)
Add a source of random numbers.
Definition: openssl.c:203
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
String manipulation buffer.
Definition: buffer.h:33
#define _(a)
Definition: message.h:28
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
Container for Accounts, Notifications.
Definition: neomutt.h:36
char * HomeDir
User&#39;s home directory.
Definition: mutt_globals.h:45
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:1024
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
size_t dsize
Length of data.
Definition: buffer.h:37
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:112
char * data
Pointer to data.
Definition: buffer.h:35
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ssl_get_client_cert()

static void ssl_get_client_cert ( struct SslSockData *  ssldata,
struct Connection conn 
)
static

Get the client certificate for an SSL connection.

Parameters
ssldataSSL socket data
connConnection to a server

Definition at line 606 of file openssl.c.

607 {
608  const char *const c_ssl_client_cert =
609  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  /* if we are using a client cert, SASL may expect an external auth name */
620  if (mutt_account_getuser(&conn->account) < 0)
621  mutt_debug(LL_DEBUG1, "Couldn't get user info\n");
622 }
int mutt_account_getuser(struct ConnAccount *cac)
Retrieve username into ConnAccount, if necessary.
Definition: connaccount.c:47
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:40
Container for Accounts, Notifications.
Definition: neomutt.h:36
Log at debug level 2.
Definition: logging.h:41
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Log at debug level 1.
Definition: logging.h:40
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
static int ssl_passwd_cb(char *buf, int buflen, int rwflag, void *userdata)
Callback to get a password.
Definition: openssl.c:336
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ check_certificate_cache()

static bool check_certificate_cache ( X509 *  peercert)
static

Is the X509 Certificate in the cache?

Parameters
peercertCertificate
Return values
trueCertificate is in the cache

Definition at line 643 of file openssl.c.

644 {
645  unsigned char peermd[EVP_MAX_MD_SIZE];
646  unsigned int peermdlen;
647  X509 *cert = NULL;
648 
649  if (!X509_digest(peercert, EVP_sha256(), peermd, &peermdlen) || !SslSessionCerts)
650  {
651  return false;
652  }
653 
654  for (int i = sk_X509_num(SslSessionCerts); i-- > 0;)
655  {
656  cert = sk_X509_value(SslSessionCerts, i);
657  if (compare_certificates(cert, peercert, peermd, peermdlen))
658  {
659  return true;
660  }
661  }
662 
663  return false;
664 }
static bool compare_certificates(X509 *cert, X509 *peercert, unsigned char *peermd, unsigned int peermdlen)
Compare two X509 certificated.
Definition: openssl.c:440
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ check_certificate_file()

static bool check_certificate_file ( X509 *  peercert)
static

Read and check a certificate file.

Parameters
peercertCertificate
Return values
trueCertificate is valid
falseError, or certificate is invalid

Definition at line 672 of file openssl.c.

673 {
674  unsigned char peermd[EVP_MAX_MD_SIZE];
675  unsigned int peermdlen;
676  X509 *cert = NULL;
677  int pass = false;
678  FILE *fp = NULL;
679 
680  const char *const c_certificate_file =
681  cs_subset_path(NeoMutt->sub, "certificate_file");
682  fp = mutt_file_fopen(c_certificate_file, "rt");
683  if (!fp)
684  return false;
685 
686  if (!X509_digest(peercert, EVP_sha256(), peermd, &peermdlen))
687  {
688  mutt_file_fclose(&fp);
689  return false;
690  }
691 
692  while (PEM_read_X509(fp, &cert, NULL, NULL))
693  {
694  if (compare_certificates(cert, peercert, peermd, peermdlen) &&
695  check_certificate_expiration(cert, true))
696  {
697  pass = true;
698  break;
699  }
700  }
701  /* PEM_read_X509 sets an error on eof */
702  if (!pass)
703  ERR_clear_error();
704  X509_free(cert);
705  mutt_file_fclose(&fp);
706 
707  return pass;
708 }
static bool compare_certificates(X509 *cert, X509 *peercert, unsigned char *peermd, unsigned int peermdlen)
Compare two X509 certificated.
Definition: openssl.c:440
Container for Accounts, Notifications.
Definition: neomutt.h:36
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
static bool check_certificate_expiration(X509 *peercert, bool silent)
Check if a certificate has expired.
Definition: openssl.c:470
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ check_host()

static int check_host ( X509 *  x509cert,
const char *  hostname,
char *  err,
size_t  errlen 
)
static

Check the host on the certificate.

Parameters
x509certCertificate
hostnameHostname
errBuffer for error message
errlenLength of buffer
Return values
1Hostname matches the certificate
0Error

Definition at line 719 of file openssl.c.

720 {
721  int rc = 0;
722  /* hostname in ASCII format: */
723  char *hostname_ascii = NULL;
724  /* needed to get the common name: */
725  X509_NAME *x509_subject = NULL;
726  char *buf = NULL;
727  int bufsize;
728  /* needed to get the DNS subjectAltNames: */
729  STACK_OF(GENERAL_NAME) * subj_alt_names;
730  int subj_alt_names_count;
731  GENERAL_NAME *subj_alt_name = NULL;
732  /* did we find a name matching hostname? */
733  bool match_found;
734 
735  /* Check if 'hostname' matches the one of the subjectAltName extensions of
736  * type DNS or the Common Name (CN). */
737 
738 #ifdef HAVE_LIBIDN
739  if (mutt_idna_to_ascii_lz(hostname, &hostname_ascii, 0) != 0)
740  {
741  hostname_ascii = mutt_str_dup(hostname);
742  }
743 #else
744  hostname_ascii = mutt_str_dup(hostname);
745 #endif
746 
747  /* Try the DNS subjectAltNames. */
748  match_found = false;
749  subj_alt_names = X509_get_ext_d2i(x509cert, NID_subject_alt_name, NULL, NULL);
750  if (subj_alt_names)
751  {
752  subj_alt_names_count = sk_GENERAL_NAME_num(subj_alt_names);
753  for (int i = 0; i < subj_alt_names_count; i++)
754  {
755  subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
756  if (subj_alt_name->type == GEN_DNS)
757  {
758  if ((subj_alt_name->d.ia5->length >= 0) &&
759  (mutt_str_len((char *) subj_alt_name->d.ia5->data) ==
760  (size_t) subj_alt_name->d.ia5->length) &&
761  (match_found = hostname_match(hostname_ascii,
762  (char *) (subj_alt_name->d.ia5->data))))
763  {
764  break;
765  }
766  }
767  }
768  GENERAL_NAMES_free(subj_alt_names);
769  }
770 
771  if (!match_found)
772  {
773  /* Try the common name */
774  x509_subject = X509_get_subject_name(x509cert);
775  if (!x509_subject)
776  {
777  if (err && errlen)
778  mutt_str_copy(err, _("can't get certificate subject"), errlen);
779  goto out;
780  }
781 
782  /* first get the space requirements */
783  bufsize = X509_NAME_get_text_by_NID(x509_subject, NID_commonName, NULL, 0);
784  if (bufsize == -1)
785  {
786  if (err && errlen)
787  mutt_str_copy(err, _("can't get certificate common name"), errlen);
788  goto out;
789  }
790  bufsize++; /* space for the terminal nul char */
791  buf = mutt_mem_malloc((size_t) bufsize);
792  if (X509_NAME_get_text_by_NID(x509_subject, NID_commonName, buf, bufsize) == -1)
793  {
794  if (err && errlen)
795  mutt_str_copy(err, _("can't get certificate common name"), errlen);
796  goto out;
797  }
798  /* cast is safe since bufsize is incremented above, so bufsize-1 is always
799  * zero or greater. */
800  if (mutt_str_len(buf) == (size_t) bufsize - 1)
801  {
802  match_found = hostname_match(hostname_ascii, buf);
803  }
804  }
805 
806  if (!match_found)
807  {
808  if (err && errlen)
809  snprintf(err, errlen, _("certificate owner does not match hostname %s"), hostname);
810  goto out;
811  }
812 
813  rc = 1;
814 
815 out:
816  FREE(&buf);
817  FREE(&hostname_ascii);
818 
819  return rc;
820 }
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
#define _(a)
Definition: message.h:28
int mutt_idna_to_ascii_lz(const char *input, char **output, uint8_t flags)
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
static STACK_OF(X509)
SSL socket data -.
Definition: openssl.c:90
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
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:749
#define FREE(x)
Definition: memory.h:40
static bool hostname_match(const char *hostname, const char *certname)
Does the hostname match the certificate.
Definition: openssl.c:506
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ check_certificate_by_digest()

static bool check_certificate_by_digest ( X509 *  peercert)
static

Validate a certificate by its digest.

Parameters
peercertCertificate
Return values
trueCertificate is valid
falseError

Definition at line 828 of file openssl.c.

829 {
830  return check_certificate_expiration(peercert, false) && check_certificate_file(peercert);
831 }
static bool check_certificate_file(X509 *peercert)
Read and check a certificate file.
Definition: openssl.c:672
static bool check_certificate_expiration(X509 *peercert, bool silent)
Check if a certificate has expired.
Definition: openssl.c:470
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ssl_cache_trusted_cert()

static int ssl_cache_trusted_cert ( X509 *  c)
static

Cache a trusted certificate.

Parameters
cCertificate
Return values
>0Number of elements in the cache
0Error

Definition at line 839 of file openssl.c.

840 {
841  mutt_debug(LL_DEBUG1, "trusted\n");
842  if (!SslSessionCerts)
843  SslSessionCerts = sk_X509_new_null();
844  return sk_X509_push(SslSessionCerts, X509_dup(c));
845 }
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Log at debug level 1.
Definition: logging.h:40
+ Here is the caller graph for this function:

◆ add_cert()

static void add_cert ( const char *  title,
X509 *  cert,
bool  issuer,
struct ListHead *  list 
)
static

Look up certificate info and save it to a list.

Parameters
titleTitle for this block of certificate info
certCertificate
issuerIf true, look up the issuer rather than owner details
listList to save info to

Definition at line 854 of file openssl.c.

855 {
856  static const int part[] = {
857  NID_commonName, // CN
858  NID_pkcs9_emailAddress, // Email
859  NID_organizationName, // O
860  NID_organizationalUnitName, // OU
861  NID_localityName, // L
862  NID_stateOrProvinceName, // ST
863  NID_countryName, // C
864  };
865 
866  X509_NAME *x509 = NULL;
867  if (issuer)
868  x509 = X509_get_issuer_name(cert);
869  else
870  x509 = X509_get_subject_name(cert);
871 
872  // Allocate formatted strings and let the ListHead take ownership
873  mutt_list_insert_tail(list, mutt_str_dup(title));
874 
875  char *line = NULL;
876  char *text = NULL;
877  for (size_t i = 0; i < mutt_array_size(part); i++)
878  {
879  text = x509_get_part(x509, part[i]);
880  if (text)
881  {
882  mutt_str_asprintf(&line, " %s", text);
883  mutt_list_insert_tail(list, line);
884  }
885  }
886 }
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
#define mutt_array_size(x)
Definition: memory.h:33
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
static char * x509_get_part(X509_NAME *name, int nid)
Retrieve from X509 data.
Definition: openssl.c:369
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1128
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ interactive_check_cert()

static bool interactive_check_cert ( X509 *  cert,
int  idx,
size_t  len,
SSL *  ssl,
bool  allow_always 
)
static

Ask the user if a certificate is valid.

Parameters
certCertificate
idxPlace of certificate in the chain
lenLength of the certificate chain
sslSSL state
allow_alwaysIf certificate may be always allowed
Return values
trueUser selected 'skip'
falseOtherwise

Definition at line 898 of file openssl.c.

899 {
900  char buf[256];
901  struct ListHead list = STAILQ_HEAD_INITIALIZER(list);
902 
903  add_cert(_("This certificate belongs to:"), cert, false, &list);
904  mutt_list_insert_tail(&list, NULL);
905  add_cert(_("This certificate was issued by:"), cert, true, &list);
906 
907  char *line = NULL;
908  mutt_list_insert_tail(&list, NULL);
909  mutt_list_insert_tail(&list, mutt_str_dup(_("This certificate is valid")));
910  mutt_str_asprintf(&line, _(" from %s"), asn1time_to_string(X509_getm_notBefore(cert)));
911  mutt_list_insert_tail(&list, line);
912  mutt_str_asprintf(&line, _(" to %s"), asn1time_to_string(X509_getm_notAfter(cert)));
913  mutt_list_insert_tail(&list, line);
914 
915  mutt_list_insert_tail(&list, NULL);
916  buf[0] = '\0';
917  x509_fingerprint(buf, sizeof(buf), cert, EVP_sha1);
918  mutt_str_asprintf(&line, _("SHA1 Fingerprint: %s"), buf);
919  mutt_list_insert_tail(&list, line);
920  buf[0] = '\0';
921  buf[40] = '\0'; /* Ensure the second printed line is null terminated */
922  x509_fingerprint(buf, sizeof(buf), cert, EVP_sha256);
923  buf[39] = '\0'; /* Divide into two lines of output */
924  mutt_str_asprintf(&line, "%s%s", _("SHA256 Fingerprint: "), buf);
925  mutt_list_insert_tail(&list, line);
926  mutt_str_asprintf(&line, "%*s%s",
927  (int) mutt_str_len(_("SHA256 Fingerprint: ")), "", buf + 40);
928  mutt_list_insert_tail(&list, line);
929 
930  bool allow_skip = false;
931 /* The leaf/host certificate can't be skipped. */
932 #ifdef HAVE_SSL_PARTIAL_CHAIN
933  const bool c_ssl_verify_partial_chains =
934  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];
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 =
948  cs_subset_path(NeoMutt->sub, "certificate_file");
949  allow_always = allow_always && c_certificate_file &&
950  check_certificate_expiration(cert, true);
951 
952  int rc = dlg_verify_certificate(title, &list, allow_always, allow_skip);
953  if ((rc == 3) && !allow_always)
954  rc = 4;
955 
956  switch (rc)
957  {
958  case 1: // Reject
959  break;
960  case 2: // Once
961  SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
963  break;
964  case 3: // Always
965  {
966  bool saved = false;
967  FILE *fp = mutt_file_fopen(c_certificate_file, "a");
968  if (fp)
969  {
970  if (PEM_write_X509(fp, cert))
971  saved = true;
972  mutt_file_fclose(&fp);
973  }
974 
975  if (saved)
976  mutt_message(_("Certificate saved"));
977  else
978  mutt_error(_("Warning: Couldn't save certificate"));
979 
980  SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
982  break;
983  }
984  case 4: // Skip
985  SSL_set_ex_data(ssl, SkipModeExDataIndex, &SkipModeExDataIndex);
986  break;
987  }
988 
989  mutt_list_free(&list);
990  return (rc > 1);
991 }
static char * asn1time_to_string(ASN1_UTCTIME *tm)
Convert a time to a string.
Definition: openssl.c:413
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
static void add_cert(const char *title, X509 *cert, bool issuer, struct ListHead *list)
Look up certificate info and save it to a list.
Definition: openssl.c:854
#define mutt_error(...)
Definition: logging.h:88
size_t idx
Definition: mailbox.c:257
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
#define _(a)
Definition: message.h:28
Container for Accounts, Notifications.
Definition: neomutt.h:36
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
static int ssl_cache_trusted_cert(X509 *c)
Cache a trusted certificate.
Definition: openssl.c:839
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
int dlg_verify_certificate(const char *title, struct ListHead *list, bool allow_always, bool allow_skip)
Ask the user to validate the certificate.
Definition: dlgverifycert.c:92
static void x509_fingerprint(char *s, int l, X509 *cert, const EVP_MD *(*hashfunc)(void))
Generate a fingerprint for an X509 certificate.
Definition: openssl.c:386
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
static int SkipModeExDataIndex
Definition: openssl.c:86
#define mutt_message(...)
Definition: logging.h:87
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
static bool check_certificate_expiration(X509 *peercert, bool silent)
Check if a certificate has expired.
Definition: openssl.c:470
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1128
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ssl_verify_callback()

static int ssl_verify_callback ( int  preverify_ok,
X509_STORE_CTX *  ctx 
)
static

Certificate verification callback.

Parameters
preverify_okIf true, don't question the user if they skipped verification
ctxX509 store context
Return values
trueCertificate is valid
falseError, or Certificate is invalid

Called for each certificate in the chain sent by the peer, starting from the root; returning true means that the given certificate is trusted, returning false immediately aborts the SSL connection

Definition at line 1004 of file openssl.c.

1005 {
1006  char buf[256];
1007 
1008  SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
1009  if (!ssl)
1010  {
1012  "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 =
1042  cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
1043  if (c_ssl_verify_partial_chains)
1044  {
1045  static int last_pos = 0;
1046  static X509 *last_cert = NULL;
1047  if (skip_mode && preverify_ok && (pos == last_pos) && last_cert)
1048  {
1049  unsigned char last_cert_md[EVP_MAX_MD_SIZE];
1050  unsigned int last_cert_mdlen;
1051  if (X509_digest(last_cert, EVP_sha256(), last_cert_md, &last_cert_mdlen) &&
1052  compare_certificates(cert, last_cert, last_cert_md, last_cert_mdlen))
1053  {
1054  mutt_debug(LL_DEBUG2, "ignoring duplicate skipped certificate\n");
1055  return true;
1056  }
1057  }
1058 
1059  last_pos = pos;
1060  if (last_cert)
1061  X509_free(last_cert);
1062  last_cert = X509_dup(cert);
1063  }
1064 #endif
1065 
1066  /* check session cache first */
1067  if (check_certificate_cache(cert))
1068  {
1069  mutt_debug(LL_DEBUG2, "using cached certificate\n");
1070  SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
1071  return true;
1072  }
1073 
1074  /* check hostname only for the leaf certificate */
1075  buf[0] = '\0';
1076  const bool c_ssl_verify_host =
1077  cs_subset_bool(NeoMutt->sub, "ssl_verify_host");
1078  if ((pos == 0) && (c_ssl_verify_host != MUTT_NO))
1079  {
1080  if (check_host(cert, host, buf, sizeof(buf)) == 0)
1081  {
1082  mutt_error(_("Certificate host check failed: %s"), buf);
1083  /* we disallow (a)ccept always in the prompt, because it will have no effect
1084  * for hostname mismatches. */
1085  return interactive_check_cert(cert, pos, len, ssl, false);
1086  }
1087  mutt_debug(LL_DEBUG2, "hostname check passed\n");
1088  }
1089 
1090  if (!preverify_ok || skip_mode)
1091  {
1092  /* automatic check from user's database */
1093  const char *const c_certificate_file =
1094  cs_subset_path(NeoMutt->sub, "certificate_file");
1095  if (c_certificate_file && check_certificate_by_digest(cert))
1096  {
1097  mutt_debug(LL_DEBUG2, "digest check passed\n");
1098  SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
1099  return true;
1100  }
1101 
1102  /* log verification error */
1103  int err = X509_STORE_CTX_get_error(ctx);
1104  snprintf(buf, sizeof(buf), "%s (%d)", X509_verify_cert_error_string(err), err);
1105  mutt_debug(LL_DEBUG2, "X509_verify_cert: %s\n", buf);
1106 
1107  /* prompt user */
1108  return interactive_check_cert(cert, pos, len, ssl, true);
1109  }
1110 
1111  return true;
1112 }
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
#define mutt_error(...)
Definition: logging.h:88
#define _(a)
Definition: message.h:28
static bool compare_certificates(X509 *cert, X509 *peercert, unsigned char *peermd, unsigned int peermdlen)
Compare two X509 certificated.
Definition: openssl.c:440
Container for Accounts, Notifications.
Definition: neomutt.h:36
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:898
static bool check_certificate_cache(X509 *peercert)
Is the X509 Certificate in the cache?
Definition: openssl.c:643
static int check_host(X509 *x509cert, const char *hostname, char *err, size_t errlen)
Check the host on the certificate.
Definition: openssl.c:719
Log at debug level 2.
Definition: logging.h:41
static bool check_certificate_by_digest(X509 *peercert)
Validate a certificate by its digest.
Definition: openssl.c:828
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Log at debug level 1.
Definition: logging.h:40
static int SkipModeExDataIndex
Definition: openssl.c:86
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:38
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
static int HostExDataIndex
Definition: openssl.c:81
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ssl_negotiate()

static int ssl_negotiate ( struct Connection conn,
struct SslSockData *  ssldata 
)
static

Attempt to negotiate SSL over the wire.

Parameters
connConnection to a server
ssldataSSL socket data
Return values
0Success
-1Error

After SSL state has been initialized, attempt to negotiate SSL over the wire, including certificate checks.

Definition at line 1124 of file openssl.c.

1125 {
1126  int err;
1127  const char *errmsg = NULL;
1128 
1129  HostExDataIndex = SSL_get_ex_new_index(0, "host", NULL, NULL, NULL);
1130  if (HostExDataIndex == -1)
1131  {
1133  "#1 failed to get index for application specific data\n");
1134  return -1;
1135  }
1136 
1137  if (!SSL_set_ex_data(ssldata->ssl, HostExDataIndex, conn->account.host))
1138  {
1139  mutt_debug(LL_DEBUG1, "#2 failed to save hostname in SSL structure\n");
1140  return -1;
1141  }
1142 
1143  SkipModeExDataIndex = SSL_get_ex_new_index(0, "skip", NULL, NULL, NULL);
1144  if (SkipModeExDataIndex == -1)
1145  {
1147  "#3 failed to get index for application specific data\n");
1148  return -1;
1149  }
1150 
1151  if (!SSL_set_ex_data(ssldata->ssl, SkipModeExDataIndex, NULL))
1152  {
1153  mutt_debug(LL_DEBUG1, "#4 failed to save skip mode in SSL structure\n");
1154  return -1;
1155  }
1156 
1157  SSL_set_verify(ssldata->ssl, SSL_VERIFY_PEER, ssl_verify_callback);
1158  SSL_set_mode(ssldata->ssl, SSL_MODE_AUTO_RETRY);
1159 
1160  if (!SSL_set_tlsext_host_name(ssldata->ssl, conn->account.host))
1161  {
1162  /* L10N: This is a warning when trying to set the host name for
1163  TLS Server Name Indication (SNI). This allows the server to present
1164  the correct certificate if it supports multiple hosts. */
1165  mutt_error(_("Warning: unable to set TLS SNI host name"));
1166  }
1167 
1168  ERR_clear_error();
1169 
1170  err = SSL_connect(ssldata->ssl);
1171  if (err != 1)
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 }
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:40
#define mutt_error(...)
Definition: logging.h:88
#define _(a)
Definition: message.h:28
static int ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
Certificate verification callback.
Definition: openssl.c:1004
char host[128]
Server to login to.
Definition: connaccount.h:53
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Log at debug level 1.
Definition: logging.h:40
static int SkipModeExDataIndex
Definition: openssl.c:86
static int HostExDataIndex
Definition: openssl.c:81
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ sockdata()

static struct SslSockData* sockdata ( struct Connection conn)
static

Get a Connection's socket data.

Parameters
connConnection
Return values
ptrSocket data

Definition at line 1198 of file openssl.c.

1199 {
1200  return conn->sockdata;
1201 }
void * sockdata
Backend-specific socket data.
Definition: connection.h:46
+ Here is the caller graph for this function:

◆ ssl_setup()

static int ssl_setup ( struct Connection conn)
static

Set up SSL on the Connection.

Parameters
connConnection
Return values
0Success
-1Failure

Definition at line 1209 of file openssl.c.

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 =
1229  cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_3");
1230  if (!c_ssl_use_tlsv1_3)
1231  SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_3);
1232 #endif
1233 
1234 #ifdef SSL_OP_NO_TLSv1_2
1235  const bool c_ssl_use_tlsv1_2 =
1236  cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_2");
1237  if (!c_ssl_use_tlsv1_2)
1238  SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_2);
1239 #endif
1240 
1241 #ifdef SSL_OP_NO_TLSv1_1
1242  const bool c_ssl_use_tlsv1_1 =
1243  cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_1");
1244  if (!c_ssl_use_tlsv1_1)
1245  SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_1);
1246 #endif
1247 
1248 #ifdef SSL_OP_NO_TLSv1
1249  const bool c_ssl_use_tlsv1 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1");
1250  if (!c_ssl_use_tlsv1)
1251  SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1);
1252 #endif
1253 
1254  const bool c_ssl_use_sslv3 = cs_subset_bool(NeoMutt->sub, "ssl_use_sslv3");
1255  if (!c_ssl_use_sslv3)
1256  SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_SSLv3);
1257 
1258  const bool c_ssl_use_sslv2 = cs_subset_bool(NeoMutt->sub, "ssl_use_sslv2");
1259  if (!c_ssl_use_sslv2)
1260  SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_SSLv2);
1261 
1262  const bool c_ssl_use_system_certs =
1263  cs_subset_bool(NeoMutt->sub, "ssl_use_system_certs");
1264  if (c_ssl_use_system_certs)
1265  {
1266  if (!SSL_CTX_set_default_verify_paths(sockdata(conn)->sctx))
1267  {
1268  mutt_debug(LL_DEBUG1, "Error setting default verify paths\n");
1269  goto free_ctx;
1270  }
1271  }
1272 
1273  const char *const c_certificate_file =
1274  cs_subset_path(NeoMutt->sub, "certificate_file");
1275  if (c_certificate_file && !ssl_load_certificates(sockdata(conn)->sctx))
1276  mutt_debug(LL_DEBUG1, "Error loading trusted certificates\n");
1277 
1278  ssl_get_client_cert(sockdata(conn), conn);
1279 
1280  const char *const c_ssl_ciphers =
1281  cs_subset_string(NeoMutt->sub, "ssl_ciphers");
1282  if (c_ssl_ciphers)
1283  {
1284  SSL_CTX_set_cipher_list(sockdata(conn)->sctx, c_ssl_ciphers);
1285  }
1286 
1287  if (ssl_set_verify_partial(sockdata(conn)->sctx))
1288  {
1289  mutt_error(_("Warning: error enabling ssl_verify_partial_chains"));
1290  }
1291 
1292  sockdata(conn)->ssl = SSL_new(sockdata(conn)->sctx);
1293  SSL_set_fd(sockdata(conn)->ssl, conn->fd);
1294 
1295  if (ssl_negotiate(conn, sockdata(conn)))
1296  goto free_ssl;
1297 
1298  sockdata(conn)->isopen = 1;
1299  conn->ssf = SSL_CIPHER_get_bits(SSL_get_current_cipher(sockdata(conn)->ssl), &maxbits);
1300 
1301  return 0;
1302 
1303 free_ssl:
1304  SSL_free(sockdata(conn)->ssl);
1305  sockdata(conn)->ssl = NULL;
1306 free_ctx:
1307  SSL_CTX_free(sockdata(conn)->sctx);
1308  sockdata(conn)->sctx = NULL;
1309 free_ssldata:
1310  FREE(&conn->sockdata);
1311 
1312  return -1;
1313 }
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
static int ssl_negotiate(struct Connection *conn, struct SslSockData *ssldata)
Attempt to negotiate SSL over the wire.
Definition: openssl.c:1124
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
unsigned int ssf
Security strength factor, in bits (see below)
Definition: connection.h:41
#define mutt_error(...)
Definition: logging.h:88
static void ssl_get_client_cert(struct SslSockData *ssldata, struct Connection *conn)
Get the client certificate for an SSL connection.
Definition: openssl.c:606
void * sockdata
Backend-specific socket data.
Definition: connection.h:46
static int ssl_load_certificates(SSL_CTX *ctx)
Load certificates and filter out the expired ones.
Definition: openssl.c:116
#define _(a)
Definition: message.h:28
static void ssl_dprint_err_stack(void)
Dump the SSL error stack.
Definition: openssl.c:307
Container for Accounts, Notifications.
Definition: neomutt.h:36
int fd
Socket file descriptor.
Definition: connection.h:44
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
static struct SslSockData * sockdata(struct Connection *conn)
Get a Connection&#39;s socket data.
Definition: openssl.c:1198
Log at debug level 1.
Definition: logging.h:40
static int ssl_set_verify_partial(SSL_CTX *ctx)
Allow verification using partial chains (with no root)
Definition: openssl.c:166
#define FREE(x)
Definition: memory.h:40
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ssl_starttls()

int mutt_ssl_starttls ( struct Connection conn)

Negotiate TLS over an already opened connection.

Parameters
connConnection to a server
Return values
0Success
-1Error

Definition at line 1415 of file openssl.c.

1416 {
1417  if (ssl_init())
1418  return -1;
1419 
1420  int rc = ssl_setup(conn);
1421 
1422  /* hmm. watch out if we're starting TLS over any method other than raw. */
1423  conn->read = ssl_socket_read;
1424  conn->write = ssl_socket_write;
1426  conn->poll = ssl_socket_poll;
1427 
1428  return rc;
1429 }
int(* read)(struct Connection *conn, char *buf, size_t count)
Definition: connection.h:82
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:1347
static int ssl_socket_poll(struct Connection *conn, time_t wait_secs)
Check whether a socket read would block - Implements Connection::poll() -.
Definition: openssl.c:1318
static int ssl_socket_close_and_restore(struct Connection *conn)
Close an SSL Connection and restore Connection callbacks - Implements Connection::close() -...
Definition: openssl.c:627
static int ssl_setup(struct Connection *conn)
Set up SSL on the Connection.
Definition: openssl.c:1209
int(* close)(struct Connection *conn)
Definition: connection.h:119
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:1369
int(* poll)(struct Connection *conn, time_t wait_secs)
Definition: connection.h:108
static int ssl_init(void)
Initialise the SSL library.
Definition: openssl.c:552
int(* write)(struct Connection *conn, const char *buf, size_t count)
Definition: connection.h:95
+ Here is the call graph for this function:

◆ mutt_ssl_socket_setup()

int mutt_ssl_socket_setup ( struct Connection conn)

Set up SSL socket mulitplexor.

Parameters
connConnection to a server
Return values
0Success
-1Error

Definition at line 1437 of file openssl.c.

1438 {
1439  if (ssl_init() < 0)
1440  {
1441  conn->open = ssl_socket_open_err;
1442  return -1;
1443  }
1444 
1445  conn->open = ssl_socket_open;
1446  conn->read = ssl_socket_read;
1447  conn->write = ssl_socket_write;
1448  conn->poll = ssl_socket_poll;
1449  conn->close = ssl_socket_close;
1450 
1451  return 0;
1452 }
static int ssl_socket_open_err(struct Connection *conn)
Error callback for opening an SSL connection - Implements Connection::open() -.
Definition: openssl.c:355
int(* read)(struct Connection *conn, char *buf, size_t count)
Definition: connection.h:82
static int ssl_socket_open(struct Connection *conn)
Open an SSL socket - Implements Connection::open() -.
Definition: openssl.c:1332
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:1347
int(* open)(struct Connection *conn)
Definition: connection.h:69
static int ssl_socket_poll(struct Connection *conn, time_t wait_secs)
Check whether a socket read would block - Implements Connection::poll() -.
Definition: openssl.c:1318
static int ssl_socket_close(struct Connection *conn)
Close an SSL connection - Implements Connection::close() -.
Definition: openssl.c:1390
int(* close)(struct Connection *conn)
Definition: connection.h:119
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:1369
int(* poll)(struct Connection *conn, time_t wait_secs)
Definition: connection.h:108
static int ssl_init(void)
Initialise the SSL library.
Definition: openssl.c:552
int(* write)(struct Connection *conn, const char *buf, size_t count)
Definition: connection.h:95
+ Here is the call graph for this function:

Variable Documentation

◆ HostExDataIndex

int HostExDataIndex = -1
static

Definition at line 81 of file openssl.c.

◆ SkipModeExDataIndex

int SkipModeExDataIndex = -1
static

Definition at line 86 of file openssl.c.