NeoMutt  2025-12-11-911-gd8d604
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
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 <unistd.h>
#include "private.h"
#include "mutt/lib.h"
#include "address/lib.h"
#include "config/lib.h"
#include "core/lib.h"
#include "connaccount.h"
#include "connection.h"
#include "globals.h"
#include "module_data.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)
 index for storing hostname as application specific data in SSL structure
 
static bool ssl_load_certificates (SSL_CTX *ctx)
 Load certificates and filter out the expired ones.
 
static bool ssl_set_verify_partial (SSL_CTX *ctx)
 Allow verification using partial chains (with no root)
 
static int add_entropy (const char *file)
 Add a source of random numbers.
 
static void ssl_err (struct SslSockData *data, int err)
 Display an SSL error message.
 
static void ssl_dprint_err_stack (void)
 Dump the SSL error stack.
 
static int ssl_passwd_cb (char *buf, int buflen, int rwflag, void *userdata)
 Callback to get a password.
 
static int ssl_socket_open_err (struct Connection *conn)
 Error callback for opening an SSL connection - Implements Connection::open() -.
 
static char * x509_get_part (X509_NAME *name, int nid)
 Retrieve from X509 data.
 
static void x509_fingerprint (struct Buffer *buf, X509 *cert, const EVP_MD *(*hashfunc)(void))
 Generate a fingerprint for an X509 certificate.
 
static char * asn1time_to_string (ASN1_UTCTIME *tm)
 Convert a time to a string.
 
static bool certificates_equal (X509 *cert, X509 *peercert, unsigned char *peermd, unsigned int peermdlen)
 Compare two X509 certificated.
 
static bool check_certificate_expiration (X509 *peercert, bool silent)
 Check if a certificate has expired.
 
static bool hostname_match (const char *hostname, const char *certname)
 Does the hostname match the certificate.
 
static int ssl_init (void)
 Initialise the SSL library.
 
static void ssl_get_client_cert (struct SslSockData *ssldata, struct Connection *conn)
 Get the client certificate for an SSL connection.
 
static int ssl_socket_close_and_restore (struct Connection *conn)
 Close an SSL Connection and restore Connection callbacks - Implements Connection::close() -.
 
static bool check_certificate_cache (X509 *peercert)
 Is the X509 Certificate in the cache?
 
static bool check_certificate_file (X509 *peercert)
 Read and check a certificate file.
 
static int check_host (X509 *x509cert, const char *hostname, char *err, size_t errlen)
 Check the host on the certificate.
 
static bool check_certificate_by_digest (X509 *peercert)
 Validate a certificate by its digest.
 
static int ssl_cache_trusted_cert (X509 *c)
 Cache a trusted certificate.
 
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 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 int ssl_verify_callback (int preverify_ok, X509_STORE_CTX *ctx)
 Certificate verification callback.
 
static int ssl_negotiate (struct Connection *conn, struct SslSockData *ssldata)
 Attempt to negotiate SSL over the wire.
 
static struct SslSockData * sockdata (struct Connection *conn)
 Get a Connection's socket data.
 
static int ssl_setup (struct Connection *conn)
 Set up SSL on the Connection.
 
static int ssl_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_open (struct Connection *conn)
 Open an SSL socket - Implements Connection::open() -.
 
static int ssl_socket_read (struct Connection *conn, char *buf, size_t count)
 Read data from an SSL 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() -.
 
static int ssl_socket_close (struct Connection *conn)
 Close an SSL connection - Implements Connection::close() -.
 
int mutt_ssl_starttls (struct Connection *conn)
 Negotiate TLS over an already opened connection.
 
int mutt_ssl_socket_setup (struct Connection *conn)
 Set up SSL socket mulitplexor.
 

Detailed Description

Handling of OpenSSL encryption.

Authors
  • Damien Riegel
  • Pietro Cerutti
  • Richard Russon
  • Ian Zimmerman
  • Alejandro Colomar

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

index for storing hostname as application specific data in SSL structure

Index for storing the "skip mode" state in SSL structure. When the user skips a certificate in the chain, the stored value will be non-null. Keep a handle on accepted certificates in case we want to open up another connection to the same server in this session SSL socket data -

< SSL context

< SSL socket

< Is the socket open?

Definition at line 95 of file openssl.c.

103{
104 SSL_CTX *sctx;
105 SSL *ssl;
106 unsigned char isopen;
107};
+ Here is the caller graph for this function:

◆ ssl_load_certificates()

static bool 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 121 of file openssl.c.

122{
123 bool rc = true;
124
125 mutt_debug(LL_DEBUG2, "loading trusted certificates\n");
126 X509_STORE *store = SSL_CTX_get_cert_store(ctx);
127 if (!store)
128 {
129 store = X509_STORE_new();
130 SSL_CTX_set_cert_store(ctx, store);
131 }
132
133 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
134 FILE *fp = mutt_file_fopen(c_certificate_file, "r");
135 if (!fp)
136 return 0;
137
138 X509 *cert = NULL;
139 while (NULL != PEM_read_X509(fp, &cert, NULL, NULL))
140 {
141 if ((X509_cmp_current_time(X509_get0_notBefore(cert)) >= 0) ||
142 (X509_cmp_current_time(X509_get0_notAfter(cert)) <= 0))
143 {
144 char buf[256] = { 0 };
145 mutt_debug(LL_DEBUG2, "filtering expired cert: %s\n",
146 X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)));
147 }
148 else
149 {
150 X509_STORE_add_cert(store, cert);
151 }
152 }
153 /* PEM_read_X509 sets the error NO_START_LINE on eof */
154 if (ERR_GET_REASON(ERR_peek_last_error()) != PEM_R_NO_START_LINE)
155 rc = false;
156 ERR_clear_error();
157
158 X509_free(cert);
159 mutt_file_fclose(&fp);
160
161 return rc;
162}
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition helpers.c:168
#define mutt_file_fclose(FP)
Definition file.h:144
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ssl_set_verify_partial()

static bool ssl_set_verify_partial ( SSL_CTX * ctx)
static

Allow verification using partial chains (with no root)

Parameters
ctxSSL context
Return values
trueSuccess
falseError

Definition at line 170 of file openssl.c.

171{
172 bool rc = true;
173#ifdef HAVE_SSL_PARTIAL_CHAIN
174 X509_VERIFY_PARAM *param = NULL;
175
176 const bool c_ssl_verify_partial_chains = cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
177 if (c_ssl_verify_partial_chains)
178 {
179 param = X509_VERIFY_PARAM_new();
180 if (param)
181 {
182 X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_PARTIAL_CHAIN);
183 if (SSL_CTX_set1_param(ctx, param) == 0)
184 {
185 mutt_debug(LL_DEBUG2, "SSL_CTX_set1_param() failed\n");
186 rc = false;
187 }
188 X509_VERIFY_PARAM_free(param);
189 }
190 else
191 {
192 mutt_debug(LL_DEBUG2, "X509_VERIFY_PARAM_new() failed\n");
193 rc = false;
194 }
195 }
196#endif
197 return rc;
198}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
+ 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 206 of file openssl.c.

207{
208 if (!file)
209 return 0;
210
211 struct stat st = { 0 };
212 int n = -1;
213
214 if (stat(file, &st) == -1)
215 return (errno == ENOENT) ? 0 : -1;
216
217 mutt_message(_("Filling entropy pool: %s..."), file);
218
219 /* check that the file permissions are secure */
220 if ((st.st_uid != getuid()) || ((st.st_mode & (S_IWGRP | S_IRGRP)) != 0) ||
221 ((st.st_mode & (S_IWOTH | S_IROTH)) != 0))
222 {
223 mutt_error(_("%s has insecure permissions"), file);
224 return -1;
225 }
226
227#ifdef HAVE_RAND_EGD
228 n = RAND_egd(file);
229#endif
230 if (n <= 0)
231 n = RAND_load_file(file, -1);
232
233 return n;
234}
#define mutt_error(...)
Definition logging2.h:94
#define mutt_message(...)
Definition logging2.h:93
#define _(a)
Definition message.h:28
+ 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 241 of file openssl.c.

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

311{
312 BIO *bio = BIO_new(BIO_s_mem());
313 if (!bio)
314 return;
315 ERR_print_errors(bio);
316
317 char *buf = NULL;
318 long buflen = BIO_get_mem_data(bio, &buf);
319 if (buflen > 0)
320 {
321 char *output = MUTT_MEM_MALLOC(buflen + 1, char);
322 memcpy(output, buf, buflen);
323 output[buflen] = '\0';
324 mutt_debug(LL_DEBUG1, "SSL error stack: %s\n", output);
325 FREE(&output);
326 }
327 BIO_free(bio);
328}
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:53
+ 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 339 of file openssl.c.

340{
341 struct ConnAccount *cac = userdata;
342
343 if (mutt_account_getuser(cac) < 0)
344 return 0;
345
346 mutt_debug(LL_DEBUG2, "getting password for %s@%s:%u\n", cac->user, cac->host, cac->port);
347
348 if (mutt_account_getpass(cac) < 0)
349 return 0;
350
351 return snprintf(buf, buflen, "%s", cac->pass);
352}
int mutt_account_getpass(struct ConnAccount *cac)
Fetch password into ConnAccount, if necessary.
int mutt_account_getuser(struct ConnAccount *cac)
Retrieve username into ConnAccount, if necessary.
Definition connaccount.c:51
Login details for a remote server.
Definition connaccount.h:59
char user[128]
Username.
Definition connaccount.h:62
char pass[256]
Password.
Definition connaccount.h:63
char host[128]
Server to login to.
Definition connaccount.h:60
unsigned short port
Port to connect to.
Definition connaccount.h:64
+ 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 372 of file openssl.c.

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

◆ x509_fingerprint()

static void x509_fingerprint ( struct Buffer * buf,
X509 * cert,
const EVP_MD *(* hashfunc )(void) )
static

Generate a fingerprint for an X509 certificate.

Parameters
bufBuffer for fingerprint
certCertificate
hashfuncHashing function

Definition at line 388 of file openssl.c.

389{
390 unsigned char md[EVP_MAX_MD_SIZE];
391 unsigned int n = 0;
392
393 if (X509_digest(cert, hashfunc(), md, &n) == 0) // Failure
394 {
395 buf_strcpy(buf, _("[unable to calculate]"));
396 return;
397 }
398
399 for (unsigned int i = 0; i < n; i++)
400 {
401 buf_add_printf(buf, "%02X", md[i]);
402
403 // Put a space after a pair of bytes (except for the last one)
404 if (((i % 2) == 1) && (i < (n - 1)))
405 buf_addch(buf, ' ');
406 }
407}
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition buffer.c:204
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:241
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
+ 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 416 of file openssl.c.

417{
418 static char buf[64];
419 BIO *bio = NULL;
420
421 mutt_str_copy(buf, _("[invalid date]"), sizeof(buf));
422
423 bio = BIO_new(BIO_s_mem());
424 if (bio)
425 {
426 if (ASN1_TIME_print(bio, tm))
427 (void) BIO_read(bio, buf, sizeof(buf));
428 BIO_free(bio);
429 }
430
431 return buf;
432}
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition string.c:586
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ certificates_equal()

static bool certificates_equal ( 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 443 of file openssl.c.

445{
446 unsigned char md[EVP_MAX_MD_SIZE];
447 unsigned int mdlen;
448
449 /* Avoid CPU-intensive digest calculation if the certificates are
450 * not even remotely equal. */
451 if ((X509_subject_name_cmp(cert, peercert) != 0) ||
452 (X509_issuer_name_cmp(cert, peercert) != 0))
453 {
454 return false;
455 }
456
457 if (!X509_digest(cert, EVP_sha256(), md, &mdlen) || (peermdlen != mdlen))
458 return false;
459
460 if (memcmp(peermd, md, mdlen) != 0)
461 return false;
462
463 return true;
464}
+ 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 473 of file openssl.c.

474{
475 const bool c_ssl_verify_dates = cs_subset_bool(NeoMutt->sub, "ssl_verify_dates");
476 if (c_ssl_verify_dates == MUTT_NO)
477 return true;
478
479 if (X509_cmp_current_time(X509_get0_notBefore(peercert)) >= 0)
480 {
481 if (!silent)
482 {
483 mutt_debug(LL_DEBUG2, "Server certificate is not yet valid\n");
484 mutt_error(_("Server certificate is not yet valid"));
485 }
486 return false;
487 }
488
489 if (X509_cmp_current_time(X509_get0_notAfter(peercert)) <= 0)
490 {
491 if (!silent)
492 {
493 mutt_debug(LL_DEBUG2, "Server certificate has expired\n");
494 mutt_error(_("Server certificate has expired"));
495 }
496 return false;
497 }
498
499 return true;
500}
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition quad.h:38
+ 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 508 of file openssl.c.

509{
510 const char *cmp1 = NULL, *cmp2 = NULL;
511
512 if (mutt_strn_equal(certname, "*.", 2))
513 {
514 cmp1 = certname + 2;
515 cmp2 = strchr(hostname, '.');
516 if (!cmp2)
517 return false;
518
519 cmp2++;
520 }
521 else
522 {
523 cmp1 = certname;
524 cmp2 = hostname;
525 }
526
527 if ((*cmp1 == '\0') || (*cmp2 == '\0'))
528 {
529 return false;
530 }
531
532 if (strcasecmp(cmp1, cmp2) != 0)
533 {
534 return false;
535 }
536
537 return true;
538}
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition string.c:429
+ 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 554 of file openssl.c.

555{
556 static bool init_complete = false;
557
558 if (init_complete)
559 return 0;
560
561 if (RAND_status() != 1)
562 {
563 /* load entropy from files */
564 struct Buffer *path = buf_pool_get();
565 const char *const c_entropy_file = cs_subset_path(NeoMutt->sub, "entropy_file");
566 add_entropy(c_entropy_file);
567 add_entropy(RAND_file_name(path->data, path->dsize));
568
569/* load entropy from egd sockets */
570#ifdef HAVE_RAND_EGD
571 add_entropy(mutt_str_getenv("EGDSOCKET"));
572 buf_printf(path, "%s/.entropy", NONULL(NeoMutt->home_dir));
573 add_entropy(buf_string(path));
574 add_entropy(TMPDIR "/entropy");
575#endif
576
577 /* shuffle $RANDFILE (or ~/.rnd if unset) */
578 RAND_write_file(RAND_file_name(path->data, path->dsize));
579 buf_pool_release(&path);
580
582 if (RAND_status() != 1)
583 {
584 mutt_error(_("Failed to find enough entropy on your system"));
585 return -1;
586 }
587 }
588
589/* OpenSSL performs automatic initialization as of 1.1.
590 * However LibreSSL does not (as of 2.8.3). */
591#if (defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x10100000L)) || \
592 (defined(LIBRESSL_VERSION_NUMBER))
593 /* I don't think you can do this just before reading the error. The call
594 * itself might clobber the last SSL error. */
595 SSL_load_error_strings();
596 SSL_library_init();
597#endif
598 init_complete = true;
599 return 0;
600}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition string.c:731
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
static int add_entropy(const char *file)
Add a source of random numbers.
Definition openssl.c:206
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
#define NONULL(x)
Definition string2.h:44
String manipulation buffer.
Definition buffer.h:36
size_t dsize
Length of data.
Definition buffer.h:39
char * data
Pointer to data.
Definition buffer.h:37
char * home_dir
User's home directory.
Definition neomutt.h:55
+ 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 607 of file openssl.c.

608{
609 const char *const c_ssl_client_cert = cs_subset_path(NeoMutt->sub, "ssl_client_cert");
610 if (!c_ssl_client_cert)
611 return;
612
613 mutt_debug(LL_DEBUG2, "Using client certificate %s\n", c_ssl_client_cert);
614 SSL_CTX_set_default_passwd_cb_userdata(ssldata->sctx, &conn->account);
615 SSL_CTX_set_default_passwd_cb(ssldata->sctx, ssl_passwd_cb);
616 SSL_CTX_use_certificate_file(ssldata->sctx, c_ssl_client_cert, SSL_FILETYPE_PEM);
617 SSL_CTX_use_PrivateKey_file(ssldata->sctx, c_ssl_client_cert, SSL_FILETYPE_PEM);
618}
static int ssl_passwd_cb(char *buf, int buflen, int rwflag, void *userdata)
Callback to get a password.
Definition openssl.c:339
struct ConnAccount account
Account details: username, password, etc.
Definition connection.h:49
+ 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 639 of file openssl.c.

640{
641 unsigned char peermd[EVP_MAX_MD_SIZE];
642 unsigned int peermdlen;
643 X509 *cert = NULL;
644
645 if (!X509_digest(peercert, EVP_sha256(), peermd, &peermdlen) || !SslSessionCerts)
646 {
647 return false;
648 }
649
650 for (int i = sk_X509_num(SslSessionCerts) - 1; i >= 0; i--)
651 {
652 cert = sk_X509_value(SslSessionCerts, i);
653 if (certificates_equal(cert, peercert, peermd, peermdlen))
654 {
655 return true;
656 }
657 }
658
659 return false;
660}
static bool certificates_equal(X509 *cert, X509 *peercert, unsigned char *peermd, unsigned int peermdlen)
Compare two X509 certificated.
Definition openssl.c:443
+ 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 668 of file openssl.c.

669{
670 unsigned char peermd[EVP_MAX_MD_SIZE];
671 unsigned int peermdlen;
672 X509 *cert = NULL;
673 int pass = false;
674 FILE *fp = NULL;
675
676 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
677 fp = mutt_file_fopen(c_certificate_file, "r");
678 if (!fp)
679 return false;
680
681 if (!X509_digest(peercert, EVP_sha256(), peermd, &peermdlen))
682 {
683 mutt_file_fclose(&fp);
684 return false;
685 }
686
687 while (PEM_read_X509(fp, &cert, NULL, NULL))
688 {
689 if (certificates_equal(cert, peercert, peermd, peermdlen) &&
691 {
692 pass = true;
693 break;
694 }
695 }
696 /* PEM_read_X509 sets an error on eof */
697 if (!pass)
698 ERR_clear_error();
699 X509_free(cert);
700 mutt_file_fclose(&fp);
701
702 return pass;
703}
static bool check_certificate_expiration(X509 *peercert, bool silent)
Check if a certificate has expired.
Definition openssl.c:473
+ 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 714 of file openssl.c.

715{
716 int rc = 0;
717 /* hostname in ASCII format: */
718 char *hostname_ascii = NULL;
719 /* needed to get the common name: */
720 X509_NAME *x509_subject = NULL;
721 char *buf = NULL;
722 int bufsize;
723 /* needed to get the DNS subjectAltNames: */
724 STACK_OF(GENERAL_NAME) * subj_alt_names;
725 int subj_alt_names_count;
726 GENERAL_NAME *subj_alt_name = NULL;
727 /* did we find a name matching hostname? */
728 bool match_found;
729 /* did the SAN extension contain any dNSName entries? (RFC6125 §6.4.4) */
730 bool has_san_dns = false;
731
732 /* Check if 'hostname' matches the one of the subjectAltName extensions of
733 * type DNS or the Common Name (CN). */
734
735#ifdef HAVE_LIBIDN
736 if (mutt_idna_to_ascii_lz(hostname, &hostname_ascii, 0) != 0)
737 {
738 hostname_ascii = mutt_str_dup(hostname);
739 }
740#else
741 hostname_ascii = mutt_str_dup(hostname);
742#endif
743
744 /* Try the DNS subjectAltNames. */
745 match_found = false;
746 subj_alt_names = X509_get_ext_d2i(x509cert, NID_subject_alt_name, NULL, NULL);
747 if (subj_alt_names)
748 {
749 subj_alt_names_count = sk_GENERAL_NAME_num(subj_alt_names);
750 for (int i = 0; i < subj_alt_names_count; i++)
751 {
752 subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
753 if (subj_alt_name->type == GEN_DNS)
754 {
755 has_san_dns = true;
756 if ((subj_alt_name->d.ia5->length >= 0) &&
757 (mutt_str_len((char *) subj_alt_name->d.ia5->data) ==
758 (size_t) subj_alt_name->d.ia5->length) &&
759 (match_found = hostname_match(hostname_ascii,
760 (char *) (subj_alt_name->d.ia5->data))))
761 {
762 break;
763 }
764 }
765 }
766 GENERAL_NAMES_free(subj_alt_names);
767 }
768
769 if (!match_found && !has_san_dns)
770 {
771 /* Only fall back to CN when the SAN extension has no dNSName entries.
772 * RFC6125 §6.4.4: if SANs of type dNSName are present, the CN must not be checked. */
773 x509_subject = X509_get_subject_name(x509cert);
774 if (!x509_subject)
775 {
776 if (err && errlen)
777 mutt_str_copy(err, _("can't get certificate subject"), errlen);
778 goto out;
779 }
780
781 /* first get the space requirements */
782 bufsize = X509_NAME_get_text_by_NID(x509_subject, NID_commonName, NULL, 0);
783 if (bufsize == -1)
784 {
785 if (err && errlen)
786 mutt_str_copy(err, _("can't get certificate common name"), errlen);
787 goto out;
788 }
789 bufsize++; /* space for the terminal nul char */
790 buf = MUTT_MEM_MALLOC(bufsize, char);
791 if (X509_NAME_get_text_by_NID(x509_subject, NID_commonName, buf, bufsize) == -1)
792 {
793 if (err && errlen)
794 mutt_str_copy(err, _("can't get certificate common name"), errlen);
795 goto out;
796 }
797 /* cast is safe since bufsize is incremented above, so bufsize-1 is always
798 * zero or greater. */
799 if (mutt_str_len(buf) == (size_t) bufsize - 1)
800 {
801 match_found = hostname_match(hostname_ascii, buf);
802 }
803 }
804
805 if (!match_found)
806 {
807 if (err && errlen)
808 snprintf(err, errlen, _("certificate owner does not match hostname %s"), hostname);
809 goto out;
810 }
811
812 rc = 1;
813
814out:
815 FREE(&buf);
816 FREE(&hostname_ascii);
817
818 return rc;
819}
int mutt_idna_to_ascii_lz(const char *input, char **output, uint8_t flags)
Convert a domain to Punycode.
Definition idna.c:90
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
static STACK_OF(X509)
index for storing hostname as application specific data in SSL structure
Definition openssl.c:95
static bool hostname_match(const char *hostname, const char *certname)
Does the hostname match the certificate.
Definition openssl.c:508
+ 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 827 of file openssl.c.

828{
829 return check_certificate_expiration(peercert, false) && check_certificate_file(peercert);
830}
static bool check_certificate_file(X509 *peercert)
Read and check a certificate file.
Definition openssl.c:668
+ 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 838 of file openssl.c.

839{
840 mutt_debug(LL_DEBUG1, "trusted\n");
841 if (!SslSessionCerts)
842 SslSessionCerts = sk_X509_new_null();
843 return sk_X509_push(SslSessionCerts, X509_dup(c));
844}
+ Here is the caller graph for this function:

◆ add_cert()

static void add_cert ( const char * title,
X509 * cert,
bool issuer,
struct StringArray * carr )
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
carrArray to save info to

Definition at line 853 of file openssl.c.

854{
855 static const int part[] = {
856 NID_commonName, // CN
857 NID_pkcs9_emailAddress, // Email
858 NID_organizationName, // O
859 NID_organizationalUnitName, // OU
860 NID_localityName, // L
861 NID_stateOrProvinceName, // ST
862 NID_countryName, // C
863 };
864
865 X509_NAME *x509 = NULL;
866 if (issuer)
867 x509 = X509_get_issuer_name(cert);
868 else
869 x509 = X509_get_subject_name(cert);
870
871 // Allocate formatted strings and let the array take ownership
872 ARRAY_ADD(carr, mutt_str_dup(title));
873
874 char *line = NULL;
875 char *text = NULL;
876 for (size_t i = 0; i < countof(part); i++)
877 {
878 text = x509_get_part(x509, part[i]);
879 if (text)
880 {
881 mutt_str_asprintf(&line, " %s", text);
882 ARRAY_ADD(carr, line);
883 }
884 }
885}
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:157
#define countof(x)
Definition memory.h:49
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition string.c:808
static char * x509_get_part(X509_NAME *name, int nid)
Retrieve from X509 data.
Definition openssl.c:372
+ 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 897 of file openssl.c.

898{
900 if (!OptGui)
901 {
902 mutt_debug(LL_DEBUG1, "unable to prompt for certificate in batch mode\n");
903 mutt_error(_("Untrusted server certificate"));
904 return 0;
905 }
906
907 struct StringArray carr = ARRAY_HEAD_INITIALIZER;
908 struct Buffer *buf = buf_pool_get();
909
910 add_cert(_("This certificate belongs to:"), cert, false, &carr);
911 ARRAY_ADD(&carr, NULL);
912 add_cert(_("This certificate was issued by:"), cert, true, &carr);
913
914 char *line = NULL;
915 ARRAY_ADD(&carr, NULL);
916 ARRAY_ADD(&carr, mutt_str_dup(_("This certificate is valid")));
917 mutt_str_asprintf(&line, _(" from %s"), asn1time_to_string(X509_getm_notBefore(cert)));
918 ARRAY_ADD(&carr, line);
919 mutt_str_asprintf(&line, _(" to %s"), asn1time_to_string(X509_getm_notAfter(cert)));
920 ARRAY_ADD(&carr, line);
921
922 ARRAY_ADD(&carr, NULL);
923 x509_fingerprint(buf, cert, EVP_sha1);
924 mutt_str_asprintf(&line, _("SHA1 Fingerprint: %s"), buf_string(buf));
925 ARRAY_ADD(&carr, line);
926
927 buf_reset(buf);
928 x509_fingerprint(buf, cert, EVP_sha256);
929 buf->data[39] = '\0'; /* Divide into two lines of output */
930 mutt_str_asprintf(&line, "%s%s", _("SHA256 Fingerprint: "), buf_string(buf));
931 ARRAY_ADD(&carr, line);
932 mutt_str_asprintf(&line, "%*s%s", (int) mutt_str_len(_("SHA256 Fingerprint: ")),
933 "", buf->data + 40);
934 ARRAY_ADD(&carr, line);
935
936 bool allow_skip = false;
937/* The leaf/host certificate can't be skipped. */
938#ifdef HAVE_SSL_PARTIAL_CHAIN
939 const bool c_ssl_verify_partial_chains = cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
940 if ((idx != 0) && c_ssl_verify_partial_chains)
941 allow_skip = true;
942#endif
943
944 char title[256] = { 0 };
945 snprintf(title, sizeof(title),
946 _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
947
948 /* Inside ssl_verify_callback(), this function is guarded by a call to
949 * check_certificate_by_digest(). This means if check_certificate_expiration() is
950 * true, then check_certificate_file() must be false. Therefore we don't need
951 * to also scan the certificate file here. */
952 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
953 allow_always = allow_always && c_certificate_file &&
955
956 int rc = dlg_certificate(title, &carr, allow_always, allow_skip);
957 if ((rc == 3) && !allow_always)
958 rc = 4;
959
960 switch (rc)
961 {
962 case 1: // Reject
963 break;
964 case 2: // Once
965 SSL_set_ex_data(ssl, mod_data->skip_mode_ex_data_index, NULL);
967 break;
968 case 3: // Always
969 {
970 bool saved = false;
971 FILE *fp = mutt_file_fopen(c_certificate_file, "a");
972 if (fp)
973 {
974 if (PEM_write_X509(fp, cert))
975 saved = true;
976 mutt_file_fclose(&fp);
977 }
978
979 if (saved)
980 mutt_message(_("Certificate saved"));
981 else
982 mutt_error(_("Warning: Couldn't save certificate"));
983
984 SSL_set_ex_data(ssl, mod_data->skip_mode_ex_data_index, NULL);
986 break;
987 }
988 case 4: // Skip
989 SSL_set_ex_data(ssl, mod_data->skip_mode_ex_data_index, &mod_data->skip_mode_ex_data_index);
990 break;
991 }
992
993 string_array_clear(&carr);
994 buf_pool_release(&buf);
995
996 return (rc > 1);
997}
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition array.h:58
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
bool OptGui
(pseudo) when the gui (and curses) are started
Definition globals.c:48
int dlg_certificate(const char *title, struct StringArray *carr, bool allow_always, bool allow_skip)
Ask the user to validate the certificate -.
@ MODULE_ID_CONN
ModuleConn, Network connections
Definition module_api.h:60
void string_array_clear(struct StringArray *arr)
Free all memory of a StringArray.
Definition string.c:936
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:663
static char * asn1time_to_string(ASN1_UTCTIME *tm)
Convert a time to a string.
Definition openssl.c:416
static void x509_fingerprint(struct Buffer *buf, X509 *cert, const EVP_MD *(*hashfunc)(void))
Generate a fingerprint for an X509 certificate.
Definition openssl.c:388
static int ssl_cache_trusted_cert(X509 *c)
Cache a trusted certificate.
Definition openssl.c:838
static void add_cert(const char *title, X509 *cert, bool issuer, struct StringArray *carr)
Look up certificate info and save it to a list.
Definition openssl.c:853
Conn private Module data.
Definition module_data.h:38
int skip_mode_ex_data_index
OpenSSL skip mode extra data index.
Definition module_data.h:49
+ 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 1010 of file openssl.c.

1011{
1012 char buf[256] = { 0 };
1013
1014 SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
1015 if (!ssl)
1016 {
1017 mutt_debug(LL_DEBUG1, "failed to retrieve SSL structure from X509_STORE_CTX\n");
1018 return false;
1019 }
1021 const char *host = SSL_get_ex_data(ssl, mod_data->host_ex_data_index);
1022 if (!host)
1023 {
1024 mutt_debug(LL_DEBUG1, "failed to retrieve hostname from SSL structure\n");
1025 return false;
1026 }
1027
1028 /* This is true when a previous entry in the certificate chain did
1029 * not verify and the user manually chose to skip it via the
1030 * $ssl_verify_partial_chains option.
1031 * In this case, all following certificates need to be treated as non-verified
1032 * until one is actually verified. */
1033 bool skip_mode = (SSL_get_ex_data(ssl, mod_data->skip_mode_ex_data_index));
1034
1035 X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
1036 int pos = X509_STORE_CTX_get_error_depth(ctx);
1037 size_t len = sk_X509_num(X509_STORE_CTX_get0_chain(ctx));
1038
1039 mutt_debug(LL_DEBUG1, "checking cert chain entry %s (preverify: %d skipmode: %d)\n",
1040 X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)),
1041 preverify_ok, skip_mode);
1042
1043#ifdef HAVE_SSL_PARTIAL_CHAIN
1044 /* Sometimes, when a certificate is (s)kipped, OpenSSL will pass it
1045 * a second time with preverify_ok = 1. Don't show it or the user
1046 * will think their "s" key is broken. */
1047 const bool c_ssl_verify_partial_chains = cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
1048 if (c_ssl_verify_partial_chains)
1049 {
1050 static int last_pos = 0;
1051 static X509 *last_cert = NULL;
1052 if (skip_mode && preverify_ok && (pos == last_pos) && last_cert)
1053 {
1054 unsigned char last_cert_md[EVP_MAX_MD_SIZE];
1055 unsigned int last_cert_mdlen;
1056 if (X509_digest(last_cert, EVP_sha256(), last_cert_md, &last_cert_mdlen) &&
1057 certificates_equal(cert, last_cert, last_cert_md, last_cert_mdlen))
1058 {
1059 mutt_debug(LL_DEBUG2, "ignoring duplicate skipped certificate\n");
1060 return true;
1061 }
1062 }
1063
1064 last_pos = pos;
1065 if (last_cert)
1066 X509_free(last_cert);
1067 last_cert = X509_dup(cert);
1068 }
1069#endif
1070
1071 /* check session cache first */
1072 if (check_certificate_cache(cert))
1073 {
1074 mutt_debug(LL_DEBUG2, "using cached certificate\n");
1075 SSL_set_ex_data(ssl, mod_data->skip_mode_ex_data_index, NULL);
1076 return true;
1077 }
1078
1079 /* check hostname only for the leaf certificate */
1080 buf[0] = '\0';
1081 const bool c_ssl_verify_host = cs_subset_bool(NeoMutt->sub, "ssl_verify_host");
1082 if ((pos == 0) && (c_ssl_verify_host != MUTT_NO))
1083 {
1084 if (check_host(cert, host, buf, sizeof(buf)) == 0)
1085 {
1086 mutt_error(_("Certificate host check failed: %s"), buf);
1087 /* we disallow (a)ccept always in the prompt, because it will have no effect
1088 * for hostname mismatches. */
1089 return interactive_check_cert(cert, pos, len, ssl, false);
1090 }
1091 mutt_debug(LL_DEBUG2, "hostname check passed\n");
1092 }
1093
1094 if (!preverify_ok || skip_mode)
1095 {
1096 /* automatic check from user's database */
1097 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
1098 if (c_certificate_file && check_certificate_by_digest(cert))
1099 {
1100 mutt_debug(LL_DEBUG2, "digest check passed\n");
1101 SSL_set_ex_data(ssl, mod_data->skip_mode_ex_data_index, NULL);
1102 return true;
1103 }
1104
1105 /* log verification error */
1106 int err = X509_STORE_CTX_get_error(ctx);
1107 snprintf(buf, sizeof(buf), "%s (%d)", X509_verify_cert_error_string(err), err);
1108 mutt_debug(LL_DEBUG2, "X509_verify_cert: %s\n", buf);
1109
1110 /* prompt user */
1111 return interactive_check_cert(cert, pos, len, ssl, true);
1112 }
1113
1114 return true;
1115}
static int check_host(X509 *x509cert, const char *hostname, char *err, size_t errlen)
Check the host on the certificate.
Definition openssl.c:714
static bool check_certificate_cache(X509 *peercert)
Is the X509 Certificate in the cache?
Definition openssl.c:639
static bool interactive_check_cert(X509 *cert, int idx, size_t len, SSL *ssl, bool allow_always)
Ask the user if a certificate is valid.
Definition openssl.c:897
static bool check_certificate_by_digest(X509 *peercert)
Validate a certificate by its digest.
Definition openssl.c:827
int host_ex_data_index
OpenSSL host extra data index.
Definition module_data.h:48
+ 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 1127 of file openssl.c.

1128{
1129 int err;
1130 const char *errmsg = NULL;
1132
1133 if (mod_data->host_ex_data_index == -1)
1134 {
1135 mod_data->host_ex_data_index = SSL_get_ex_new_index(0, "host", NULL, NULL, NULL);
1136 if (mod_data->host_ex_data_index == -1)
1137 {
1138 mutt_debug(LL_DEBUG1, "#1 failed to get index for application specific data\n");
1139 return -1;
1140 }
1141 }
1142
1143 if (!SSL_set_ex_data(ssldata->ssl, mod_data->host_ex_data_index, conn->account.host))
1144 {
1145 mutt_debug(LL_DEBUG1, "#2 failed to save hostname in SSL structure\n");
1146 return -1;
1147 }
1148
1149 if (mod_data->skip_mode_ex_data_index == -1)
1150 {
1151 mod_data->skip_mode_ex_data_index = SSL_get_ex_new_index(0, "skip", NULL, NULL, NULL);
1152 if (mod_data->skip_mode_ex_data_index == -1)
1153 {
1154 mutt_debug(LL_DEBUG1, "#3 failed to get index for application specific data\n");
1155 return -1;
1156 }
1157 }
1158
1159 if (!SSL_set_ex_data(ssldata->ssl, mod_data->skip_mode_ex_data_index, NULL))
1160 {
1161 mutt_debug(LL_DEBUG1, "#4 failed to save skip mode in SSL structure\n");
1162 return -1;
1163 }
1164
1165 SSL_set_verify(ssldata->ssl, SSL_VERIFY_PEER, ssl_verify_callback);
1166 SSL_set_mode(ssldata->ssl, SSL_MODE_AUTO_RETRY);
1167
1168 if (!SSL_set_tlsext_host_name(ssldata->ssl, conn->account.host))
1169 {
1170 /* L10N: This is a warning when trying to set the host name for
1171 TLS Server Name Indication (SNI). This allows the server to present
1172 the correct certificate if it supports multiple hosts. */
1173 mutt_error(_("Warning: unable to set TLS SNI host name"));
1174 }
1175
1176 ERR_clear_error();
1177
1178retry:
1179 err = SSL_connect(ssldata->ssl);
1180 if (err != 1)
1181 {
1182 // Temporary failure, e.g. signal received
1183 if (BIO_should_retry(SSL_get_rbio(ssldata->ssl)))
1184 goto retry;
1185
1186 switch (SSL_get_error(ssldata->ssl, err))
1187 {
1188 case SSL_ERROR_SYSCALL:
1189 errmsg = _("I/O error");
1190 break;
1191 case SSL_ERROR_SSL:
1192 errmsg = ERR_error_string(ERR_get_error(), NULL);
1193 break;
1194 default:
1195 errmsg = _("unknown error");
1196 }
1197
1198 mutt_error(_("SSL failed: %s"), errmsg);
1199
1200 return -1;
1201 }
1202
1203 return 0;
1204}
static int ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
Certificate verification callback.
Definition openssl.c:1010
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ sockdata()

static struct SslSockData * sockdata ( struct Connection * conn)
inlinestatic

Get a Connection's socket data.

Parameters
connConnection
Return values
ptrSocket data

Definition at line 1211 of file openssl.c.

1212{
1213 return conn->sockdata;
1214}
void * sockdata
Backend-specific socket data.
Definition connection.h:55
+ 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 1222 of file openssl.c.

1223{
1224 int maxbits = 0;
1225
1226 conn->sockdata = MUTT_MEM_CALLOC(1, struct SslSockData);
1227
1228 sockdata(conn)->sctx = SSL_CTX_new(SSLv23_client_method());
1229 if (!sockdata(conn)->sctx)
1230 {
1231 /* L10N: an SSL context is a data structure returned by the OpenSSL
1232 function SSL_CTX_new(). In this case it returned NULL: an
1233 error condition. */
1234 mutt_error(_("Unable to create SSL context"));
1236 goto free_ssldata;
1237 }
1238
1239 /* disable SSL protocols as needed */
1240#ifdef SSL_OP_NO_TLSv1_3
1241 const bool c_ssl_use_tlsv1_3 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_3");
1242 if (!c_ssl_use_tlsv1_3)
1243 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_3);
1244#endif
1245
1246#ifdef SSL_OP_NO_TLSv1_2
1247 const bool c_ssl_use_tlsv1_2 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_2");
1248 if (!c_ssl_use_tlsv1_2)
1249 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_2);
1250#endif
1251
1252 // Deprecated protocols
1253#ifdef SSL_OP_NO_TLSv1_1
1254 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_1);
1255#endif
1256#ifdef SSL_OP_NO_TLSv1
1257 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1);
1258#endif
1259 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_SSLv3);
1260 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_SSLv2);
1261
1262 const bool c_ssl_use_system_certs = cs_subset_bool(NeoMutt->sub, "ssl_use_system_certs");
1263 if (c_ssl_use_system_certs)
1264 {
1265 if (!SSL_CTX_set_default_verify_paths(sockdata(conn)->sctx))
1266 {
1267 mutt_debug(LL_DEBUG1, "Error setting default verify paths\n");
1268 goto free_ctx;
1269 }
1270 }
1271
1272 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
1273 if (c_certificate_file && !ssl_load_certificates(sockdata(conn)->sctx))
1274 mutt_debug(LL_DEBUG1, "Error loading trusted certificates\n");
1275
1276 ssl_get_client_cert(sockdata(conn), conn);
1277
1278 const char *const c_ssl_ciphers = cs_subset_string(NeoMutt->sub, "ssl_ciphers");
1279 if (c_ssl_ciphers)
1280 {
1281 SSL_CTX_set_cipher_list(sockdata(conn)->sctx, c_ssl_ciphers);
1282 }
1283
1284 if (!ssl_set_verify_partial(sockdata(conn)->sctx))
1285 {
1286 mutt_error(_("Warning: error enabling ssl_verify_partial_chains"));
1287 }
1288
1289 sockdata(conn)->ssl = SSL_new(sockdata(conn)->sctx);
1290 SSL_set_fd(sockdata(conn)->ssl, conn->fd);
1291
1292 if (ssl_negotiate(conn, sockdata(conn)))
1293 goto free_ssl;
1294
1295 sockdata(conn)->isopen = 1;
1296 conn->ssf = SSL_CIPHER_get_bits(SSL_get_current_cipher(sockdata(conn)->ssl), &maxbits);
1297
1298 return 0;
1299
1300free_ssl:
1301 SSL_free(sockdata(conn)->ssl);
1302 sockdata(conn)->ssl = NULL;
1303free_ctx:
1304 SSL_CTX_free(sockdata(conn)->sctx);
1305 sockdata(conn)->sctx = NULL;
1306free_ssldata:
1307 FREE(&conn->sockdata);
1308
1309 return -1;
1310}
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
static int ssl_negotiate(struct Connection *conn, struct SslSockData *ssldata)
Attempt to negotiate SSL over the wire.
Definition openssl.c:1127
static bool ssl_load_certificates(SSL_CTX *ctx)
Load certificates and filter out the expired ones.
Definition openssl.c:121
static void ssl_dprint_err_stack(void)
Dump the SSL error stack.
Definition openssl.c:310
static struct SslSockData * sockdata(struct Connection *conn)
Get a Connection's socket data.
Definition openssl.c:1211
static bool ssl_set_verify_partial(SSL_CTX *ctx)
Allow verification using partial chains (with no root)
Definition openssl.c:170
static void ssl_get_client_cert(struct SslSockData *ssldata, struct Connection *conn)
Get the client certificate for an SSL connection.
Definition openssl.c:607
unsigned int ssf
Security strength factor, in bits (see notes)
Definition connection.h:50
int fd
Socket file descriptor.
Definition connection.h:53
+ 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 1432 of file openssl.c.

1433{
1434 if (ssl_init())
1435 return -1;
1436
1437 int rc = ssl_setup(conn);
1438
1439 /* hmm. watch out if we're starting TLS over any method other than raw. */
1440 conn->read = ssl_socket_read;
1441 conn->write = ssl_socket_write;
1443 conn->poll = ssl_socket_poll;
1444
1445 return rc;
1446}
static int ssl_socket_close_and_restore(struct Connection *conn)
Close an SSL Connection and restore Connection callbacks - Implements Connection::close() -.
Definition openssl.c:623
static int ssl_socket_poll(struct Connection *conn, time_t wait_secs)
Check if any data is waiting on a socket - Implements Connection::poll() -.
Definition openssl.c:1315
static int ssl_socket_read(struct Connection *conn, char *buf, size_t count)
Read data from an SSL socket - Implements Connection::read() -.
Definition openssl.c:1344
static int ssl_socket_write(struct Connection *conn, const char *buf, size_t count)
Write data to an SSL socket - Implements Connection::write() -.
Definition openssl.c:1376
static int ssl_setup(struct Connection *conn)
Set up SSL on the Connection.
Definition openssl.c:1222
static int ssl_init(void)
Initialise the SSL library.
Definition openssl.c:554
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
int(* close)(struct Connection *conn)
Definition connection.h:116
int(* read)(struct Connection *conn, char *buf, size_t count)
Definition connection.h:79
+ 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 1454 of file openssl.c.

1455{
1456 if (ssl_init() < 0)
1457 {
1458 conn->open = ssl_socket_open_err;
1459 return -1;
1460 }
1461
1462 conn->open = ssl_socket_open;
1463 conn->read = ssl_socket_read;
1464 conn->write = ssl_socket_write;
1465 conn->poll = ssl_socket_poll;
1466 conn->close = ssl_socket_close;
1467
1468 return 0;
1469}
static int ssl_socket_close(struct Connection *conn)
Close an SSL connection - Implements Connection::close() -.
Definition openssl.c:1407
static int ssl_socket_open_err(struct Connection *conn)
Error callback for opening an SSL connection - Implements Connection::open() -.
Definition openssl.c:358
static int ssl_socket_open(struct Connection *conn)
Open an SSL socket - Implements Connection::open() -.
Definition openssl.c:1329
int(* open)(struct Connection *conn)
Definition connection.h:66
+ Here is the call graph for this function: