NeoMutt  2025-12-11-435-g4ac674
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 "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)
 Keep a handle on accepted certificates in case we want to open up another connection to the same server in this session.
 
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.
 

Variables

static int HostExDataIndex = -1
 index for storing hostname as application specific data in SSL structure
 
static int SkipModeExDataIndex = -1
 Index for storing the "skip mode" state in SSL structure.
 

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

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 94 of file openssl.c.

102{
103 SSL_CTX *sctx;
104 SSL *ssl;
105 unsigned char isopen;
106};
+ 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 120 of file openssl.c.

121{
122 bool rc = true;
123
124 mutt_debug(LL_DEBUG2, "loading trusted certificates\n");
125 X509_STORE *store = SSL_CTX_get_cert_store(ctx);
126 if (!store)
127 {
128 store = X509_STORE_new();
129 SSL_CTX_set_cert_store(ctx, store);
130 }
131
132 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
133 FILE *fp = mutt_file_fopen(c_certificate_file, "r");
134 if (!fp)
135 return 0;
136
137 X509 *cert = NULL;
138 while (NULL != PEM_read_X509(fp, &cert, NULL, NULL))
139 {
140 if ((X509_cmp_current_time(X509_get0_notBefore(cert)) >= 0) ||
141 (X509_cmp_current_time(X509_get0_notAfter(cert)) <= 0))
142 {
143 char buf[256] = { 0 };
144 mutt_debug(LL_DEBUG2, "filtering expired cert: %s\n",
145 X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)));
146 }
147 else
148 {
149 X509_STORE_add_cert(store, cert);
150 }
151 }
152 /* PEM_read_X509 sets the error NO_START_LINE on eof */
153 if (ERR_GET_REASON(ERR_peek_last_error()) != PEM_R_NO_START_LINE)
154 rc = false;
155 ERR_clear_error();
156
157 X509_free(cert);
158 mutt_file_fclose(&fp);
159
160 return rc;
161}
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:139
#define mutt_file_fopen(PATH, MODE)
Definition file.h:138
#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 169 of file openssl.c.

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

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

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

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

339{
340 struct ConnAccount *cac = userdata;
341
342 if (mutt_account_getuser(cac) < 0)
343 return 0;
344
345 mutt_debug(LL_DEBUG2, "getting password for %s@%s:%u\n", cac->user, cac->host, cac->port);
346
347 if (mutt_account_getpass(cac) < 0)
348 return 0;
349
350 return snprintf(buf, buflen, "%s", cac->pass);
351}
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:53
char user[128]
Username.
Definition connaccount.h:56
char pass[256]
Password.
Definition connaccount.h:57
char host[128]
Server to login to.
Definition connaccount.h:54
unsigned short port
Port to connect to.
Definition connaccount.h:58
+ 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 371 of file openssl.c.

372{
373 static char data[128];
374
375 if (!name || (X509_NAME_get_text_by_NID(name, nid, data, sizeof(data)) < 0))
376 return NULL;
377
378 return data;
379}
+ 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 387 of file openssl.c.

388{
389 unsigned char md[EVP_MAX_MD_SIZE];
390 unsigned int n = 0;
391
392 if (X509_digest(cert, hashfunc(), md, &n) == 0) // Failure
393 {
394 buf_strcpy(buf, _("[unable to calculate]"));
395 return;
396 }
397
398 for (unsigned int i = 0; i < n; i++)
399 {
400 buf_add_printf(buf, "%02X", md[i]);
401
402 // Put a space after a pair of bytes (except for the last one)
403 if (((i % 2) == 1) && (i < (n - 1)))
404 buf_addch(buf, ' ');
405 }
406}
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 415 of file openssl.c.

416{
417 static char buf[64];
418 BIO *bio = NULL;
419
420 mutt_str_copy(buf, _("[invalid date]"), sizeof(buf));
421
422 bio = BIO_new(BIO_s_mem());
423 if (bio)
424 {
425 if (ASN1_TIME_print(bio, tm))
426 (void) BIO_read(bio, buf, sizeof(buf));
427 BIO_free(bio);
428 }
429
430 return buf;
431}
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:583
+ 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 442 of file openssl.c.

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

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

508{
509 const char *cmp1 = NULL, *cmp2 = NULL;
510
511 if (mutt_strn_equal(certname, "*.", 2))
512 {
513 cmp1 = certname + 2;
514 cmp2 = strchr(hostname, '.');
515 if (!cmp2)
516 return false;
517
518 cmp2++;
519 }
520 else
521 {
522 cmp1 = certname;
523 cmp2 = hostname;
524 }
525
526 if ((*cmp1 == '\0') || (*cmp2 == '\0'))
527 {
528 return false;
529 }
530
531 if (strcasecmp(cmp1, cmp2) != 0)
532 {
533 return false;
534 }
535
536 return true;
537}
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 553 of file openssl.c.

554{
555 static bool init_complete = false;
556
557 if (init_complete)
558 return 0;
559
560 if (RAND_status() != 1)
561 {
562 /* load entropy from files */
563 struct Buffer *path = buf_pool_get();
564 const char *const c_entropy_file = 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 buf_printf(path, "%s/.entropy", NONULL(NeoMutt->home_dir));
572 add_entropy(buf_string(path));
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));
578 buf_pool_release(&path);
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}
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:728
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:205
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:56
+ 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 = cs_subset_path(NeoMutt->sub, "ssl_client_cert");
609 if (!c_ssl_client_cert)
610 return;
611
612 mutt_debug(LL_DEBUG2, "Using client certificate %s\n", c_ssl_client_cert);
613 SSL_CTX_set_default_passwd_cb_userdata(ssldata->sctx, &conn->account);
614 SSL_CTX_set_default_passwd_cb(ssldata->sctx, ssl_passwd_cb);
615 SSL_CTX_use_certificate_file(ssldata->sctx, c_ssl_client_cert, SSL_FILETYPE_PEM);
616 SSL_CTX_use_PrivateKey_file(ssldata->sctx, c_ssl_client_cert, SSL_FILETYPE_PEM);
617}
static int ssl_passwd_cb(char *buf, int buflen, int rwflag, void *userdata)
Callback to get a password.
Definition openssl.c:338
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 638 of file openssl.c.

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

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

714{
715 int rc = 0;
716 /* hostname in ASCII format: */
717 char *hostname_ascii = NULL;
718 /* needed to get the common name: */
719 X509_NAME *x509_subject = NULL;
720 char *buf = NULL;
721 int bufsize;
722 /* needed to get the DNS subjectAltNames: */
723 STACK_OF(GENERAL_NAME) * subj_alt_names;
724 int subj_alt_names_count;
725 GENERAL_NAME *subj_alt_name = NULL;
726 /* did we find a name matching hostname? */
727 bool match_found;
728
729 /* Check if 'hostname' matches the one of the subjectAltName extensions of
730 * type DNS or the Common Name (CN). */
731
732#ifdef HAVE_LIBIDN
733 if (mutt_idna_to_ascii_lz(hostname, &hostname_ascii, 0) != 0)
734 {
735 hostname_ascii = mutt_str_dup(hostname);
736 }
737#else
738 hostname_ascii = mutt_str_dup(hostname);
739#endif
740
741 /* Try the DNS subjectAltNames. */
742 match_found = false;
743 subj_alt_names = X509_get_ext_d2i(x509cert, NID_subject_alt_name, NULL, NULL);
744 if (subj_alt_names)
745 {
746 subj_alt_names_count = sk_GENERAL_NAME_num(subj_alt_names);
747 for (int i = 0; i < subj_alt_names_count; i++)
748 {
749 subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
750 if (subj_alt_name->type == GEN_DNS)
751 {
752 if ((subj_alt_name->d.ia5->length >= 0) &&
753 (mutt_str_len((char *) subj_alt_name->d.ia5->data) ==
754 (size_t) subj_alt_name->d.ia5->length) &&
755 (match_found = hostname_match(hostname_ascii,
756 (char *) (subj_alt_name->d.ia5->data))))
757 {
758 break;
759 }
760 }
761 }
762 GENERAL_NAMES_free(subj_alt_names);
763 }
764
765 if (!match_found)
766 {
767 /* Try the common name */
768 x509_subject = X509_get_subject_name(x509cert);
769 if (!x509_subject)
770 {
771 if (err && errlen)
772 mutt_str_copy(err, _("can't get certificate subject"), errlen);
773 goto out;
774 }
775
776 /* first get the space requirements */
777 bufsize = X509_NAME_get_text_by_NID(x509_subject, NID_commonName, NULL, 0);
778 if (bufsize == -1)
779 {
780 if (err && errlen)
781 mutt_str_copy(err, _("can't get certificate common name"), errlen);
782 goto out;
783 }
784 bufsize++; /* space for the terminal nul char */
785 buf = MUTT_MEM_MALLOC(bufsize, char);
786 if (X509_NAME_get_text_by_NID(x509_subject, NID_commonName, buf, bufsize) == -1)
787 {
788 if (err && errlen)
789 mutt_str_copy(err, _("can't get certificate common name"), errlen);
790 goto out;
791 }
792 /* cast is safe since bufsize is incremented above, so bufsize-1 is always
793 * zero or greater. */
794 if (mutt_str_len(buf) == (size_t) bufsize - 1)
795 {
796 match_found = hostname_match(hostname_ascii, buf);
797 }
798 }
799
800 if (!match_found)
801 {
802 if (err && errlen)
803 snprintf(err, errlen, _("certificate owner does not match hostname %s"), hostname);
804 goto out;
805 }
806
807 rc = 1;
808
809out:
810 FREE(&buf);
811 FREE(&hostname_ascii);
812
813 return rc;
814}
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:500
static STACK_OF(X509)
Keep a handle on accepted certificates in case we want to open up another connection to the same serv...
Definition openssl.c:94
static bool hostname_match(const char *hostname, const char *certname)
Does the hostname match the certificate.
Definition openssl.c:507
+ 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 822 of file openssl.c.

823{
824 return check_certificate_expiration(peercert, false) && check_certificate_file(peercert);
825}
static bool check_certificate_file(X509 *peercert)
Read and check a certificate file.
Definition openssl.c:667
+ 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 833 of file openssl.c.

834{
835 mutt_debug(LL_DEBUG1, "trusted\n");
836 if (!SslSessionCerts)
837 SslSessionCerts = sk_X509_new_null();
838 return sk_X509_push(SslSessionCerts, X509_dup(c));
839}
+ 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 848 of file openssl.c.

849{
850 static const int part[] = {
851 NID_commonName, // CN
852 NID_pkcs9_emailAddress, // Email
853 NID_organizationName, // O
854 NID_organizationalUnitName, // OU
855 NID_localityName, // L
856 NID_stateOrProvinceName, // ST
857 NID_countryName, // C
858 };
859
860 X509_NAME *x509 = NULL;
861 if (issuer)
862 x509 = X509_get_issuer_name(cert);
863 else
864 x509 = X509_get_subject_name(cert);
865
866 // Allocate formatted strings and let the array take ownership
867 ARRAY_ADD(carr, mutt_str_dup(title));
868
869 char *line = NULL;
870 char *text = NULL;
871 for (size_t i = 0; i < countof(part); i++)
872 {
873 text = x509_get_part(x509, part[i]);
874 if (text)
875 {
876 mutt_str_asprintf(&line, " %s", text);
877 ARRAY_ADD(carr, line);
878 }
879 }
880}
#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:805
static char * x509_get_part(X509_NAME *name, int nid)
Retrieve from X509 data.
Definition openssl.c:371
+ 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 892 of file openssl.c.

893{
894 if (!OptGui)
895 {
896 mutt_debug(LL_DEBUG1, "unable to prompt for certificate in batch mode\n");
897 mutt_error(_("Untrusted server certificate"));
898 return 0;
899 }
900
901 struct StringArray carr = ARRAY_HEAD_INITIALIZER;
902 struct Buffer *buf = buf_pool_get();
903
904 add_cert(_("This certificate belongs to:"), cert, false, &carr);
905 ARRAY_ADD(&carr, NULL);
906 add_cert(_("This certificate was issued by:"), cert, true, &carr);
907
908 char *line = NULL;
909 ARRAY_ADD(&carr, NULL);
910 ARRAY_ADD(&carr, mutt_str_dup(_("This certificate is valid")));
911 mutt_str_asprintf(&line, _(" from %s"), asn1time_to_string(X509_getm_notBefore(cert)));
912 ARRAY_ADD(&carr, line);
913 mutt_str_asprintf(&line, _(" to %s"), asn1time_to_string(X509_getm_notAfter(cert)));
914 ARRAY_ADD(&carr, line);
915
916 ARRAY_ADD(&carr, NULL);
917 x509_fingerprint(buf, cert, EVP_sha1);
918 mutt_str_asprintf(&line, _("SHA1 Fingerprint: %s"), buf_string(buf));
919 ARRAY_ADD(&carr, line);
920
921 buf_reset(buf);
922 x509_fingerprint(buf, cert, EVP_sha256);
923 buf->data[39] = '\0'; /* Divide into two lines of output */
924 mutt_str_asprintf(&line, "%s%s", _("SHA256 Fingerprint: "), buf_string(buf));
925 ARRAY_ADD(&carr, line);
926 mutt_str_asprintf(&line, "%*s%s", (int) mutt_str_len(_("SHA256 Fingerprint: ")),
927 "", buf->data + 40);
928 ARRAY_ADD(&carr, 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 = cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
934 if ((idx != 0) && c_ssl_verify_partial_chains)
935 allow_skip = true;
936#endif
937
938 char title[256] = { 0 };
939 snprintf(title, sizeof(title),
940 _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
941
942 /* Inside ssl_verify_callback(), this function is guarded by a call to
943 * check_certificate_by_digest(). This means if check_certificate_expiration() is
944 * true, then check_certificate_file() must be false. Therefore we don't need
945 * to also scan the certificate file here. */
946 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
947 allow_always = allow_always && c_certificate_file &&
949
950 int rc = dlg_certificate(title, &carr, allow_always, allow_skip);
951 if ((rc == 3) && !allow_always)
952 rc = 4;
953
954 switch (rc)
955 {
956 case 1: // Reject
957 break;
958 case 2: // Once
959 SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
961 break;
962 case 3: // Always
963 {
964 bool saved = false;
965 FILE *fp = mutt_file_fopen(c_certificate_file, "a");
966 if (fp)
967 {
968 if (PEM_write_X509(fp, cert))
969 saved = true;
970 mutt_file_fclose(&fp);
971 }
972
973 if (saved)
974 mutt_message(_("Certificate saved"));
975 else
976 mutt_error(_("Warning: Couldn't save certificate"));
977
978 SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
980 break;
981 }
982 case 4: // Skip
983 SSL_set_ex_data(ssl, SkipModeExDataIndex, &SkipModeExDataIndex);
984 break;
985 }
986
987 string_array_clear(&carr);
988 buf_pool_release(&buf);
989
990 return (rc > 1);
991}
#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 -.
void string_array_clear(struct StringArray *arr)
Free all memory of a StringArray.
Definition string.c:933
static char * asn1time_to_string(ASN1_UTCTIME *tm)
Convert a time to a string.
Definition openssl.c:415
static void x509_fingerprint(struct Buffer *buf, X509 *cert, const EVP_MD *(*hashfunc)(void))
Generate a fingerprint for an X509 certificate.
Definition openssl.c:387
static int ssl_cache_trusted_cert(X509 *c)
Cache a trusted certificate.
Definition openssl.c:833
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:848
static int SkipModeExDataIndex
Index for storing the "skip mode" state in SSL structure.
Definition openssl.c:90
+ 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] = { 0 };
1007
1008 SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
1009 if (!ssl)
1010 {
1011 mutt_debug(LL_DEBUG1, "failed to retrieve SSL structure from X509_STORE_CTX\n");
1012 return false;
1013 }
1014 const char *host = SSL_get_ex_data(ssl, HostExDataIndex);
1015 if (!host)
1016 {
1017 mutt_debug(LL_DEBUG1, "failed to retrieve hostname from SSL structure\n");
1018 return false;
1019 }
1020
1021 /* This is true when a previous entry in the certificate chain did
1022 * not verify and the user manually chose to skip it via the
1023 * $ssl_verify_partial_chains option.
1024 * In this case, all following certificates need to be treated as non-verified
1025 * until one is actually verified. */
1026 bool skip_mode = (SSL_get_ex_data(ssl, SkipModeExDataIndex));
1027
1028 X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
1029 int pos = X509_STORE_CTX_get_error_depth(ctx);
1030 size_t len = sk_X509_num(X509_STORE_CTX_get0_chain(ctx));
1031
1032 mutt_debug(LL_DEBUG1, "checking cert chain entry %s (preverify: %d skipmode: %d)\n",
1033 X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)),
1034 preverify_ok, skip_mode);
1035
1036#ifdef HAVE_SSL_PARTIAL_CHAIN
1037 /* Sometimes, when a certificate is (s)kipped, OpenSSL will pass it
1038 * a second time with preverify_ok = 1. Don't show it or the user
1039 * will think their "s" key is broken. */
1040 const bool c_ssl_verify_partial_chains = cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
1041 if (c_ssl_verify_partial_chains)
1042 {
1043 static int last_pos = 0;
1044 static X509 *last_cert = NULL;
1045 if (skip_mode && preverify_ok && (pos == last_pos) && last_cert)
1046 {
1047 unsigned char last_cert_md[EVP_MAX_MD_SIZE];
1048 unsigned int last_cert_mdlen;
1049 if (X509_digest(last_cert, EVP_sha256(), last_cert_md, &last_cert_mdlen) &&
1050 certificates_equal(cert, last_cert, last_cert_md, last_cert_mdlen))
1051 {
1052 mutt_debug(LL_DEBUG2, "ignoring duplicate skipped certificate\n");
1053 return true;
1054 }
1055 }
1056
1057 last_pos = pos;
1058 if (last_cert)
1059 X509_free(last_cert);
1060 last_cert = X509_dup(cert);
1061 }
1062#endif
1063
1064 /* check session cache first */
1065 if (check_certificate_cache(cert))
1066 {
1067 mutt_debug(LL_DEBUG2, "using cached certificate\n");
1068 SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
1069 return true;
1070 }
1071
1072 /* check hostname only for the leaf certificate */
1073 buf[0] = '\0';
1074 const bool c_ssl_verify_host = cs_subset_bool(NeoMutt->sub, "ssl_verify_host");
1075 if ((pos == 0) && (c_ssl_verify_host != MUTT_NO))
1076 {
1077 if (check_host(cert, host, buf, sizeof(buf)) == 0)
1078 {
1079 mutt_error(_("Certificate host check failed: %s"), buf);
1080 /* we disallow (a)ccept always in the prompt, because it will have no effect
1081 * for hostname mismatches. */
1082 return interactive_check_cert(cert, pos, len, ssl, false);
1083 }
1084 mutt_debug(LL_DEBUG2, "hostname check passed\n");
1085 }
1086
1087 if (!preverify_ok || skip_mode)
1088 {
1089 /* automatic check from user's database */
1090 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
1091 if (c_certificate_file && check_certificate_by_digest(cert))
1092 {
1093 mutt_debug(LL_DEBUG2, "digest check passed\n");
1094 SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
1095 return true;
1096 }
1097
1098 /* log verification error */
1099 int err = X509_STORE_CTX_get_error(ctx);
1100 snprintf(buf, sizeof(buf), "%s (%d)", X509_verify_cert_error_string(err), err);
1101 mutt_debug(LL_DEBUG2, "X509_verify_cert: %s\n", buf);
1102
1103 /* prompt user */
1104 return interactive_check_cert(cert, pos, len, ssl, true);
1105 }
1106
1107 return true;
1108}
static int check_host(X509 *x509cert, const char *hostname, char *err, size_t errlen)
Check the host on the certificate.
Definition openssl.c:713
static bool check_certificate_cache(X509 *peercert)
Is the X509 Certificate in the cache?
Definition openssl.c:638
static int HostExDataIndex
index for storing hostname as application specific data in SSL structure
Definition openssl.c:86
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:892
static bool check_certificate_by_digest(X509 *peercert)
Validate a certificate by its digest.
Definition openssl.c:822
+ 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 1120 of file openssl.c.

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

1198{
1199 return conn->sockdata;
1200}
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 1208 of file openssl.c.

1209{
1210 int maxbits = 0;
1211
1212 conn->sockdata = MUTT_MEM_CALLOC(1, struct SslSockData);
1213
1214 sockdata(conn)->sctx = SSL_CTX_new(SSLv23_client_method());
1215 if (!sockdata(conn)->sctx)
1216 {
1217 /* L10N: an SSL context is a data structure returned by the OpenSSL
1218 function SSL_CTX_new(). In this case it returned NULL: an
1219 error condition. */
1220 mutt_error(_("Unable to create SSL context"));
1222 goto free_ssldata;
1223 }
1224
1225 /* disable SSL protocols as needed */
1226#ifdef SSL_OP_NO_TLSv1_3
1227 const bool c_ssl_use_tlsv1_3 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_3");
1228 if (!c_ssl_use_tlsv1_3)
1229 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_3);
1230#endif
1231
1232#ifdef SSL_OP_NO_TLSv1_2
1233 const bool c_ssl_use_tlsv1_2 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_2");
1234 if (!c_ssl_use_tlsv1_2)
1235 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_2);
1236#endif
1237
1238 // Deprecated protocols
1239#ifdef SSL_OP_NO_TLSv1_1
1240 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_1);
1241#endif
1242#ifdef SSL_OP_NO_TLSv1
1243 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1);
1244#endif
1245 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_SSLv3);
1246 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_SSLv2);
1247
1248 const bool c_ssl_use_system_certs = cs_subset_bool(NeoMutt->sub, "ssl_use_system_certs");
1249 if (c_ssl_use_system_certs)
1250 {
1251 if (!SSL_CTX_set_default_verify_paths(sockdata(conn)->sctx))
1252 {
1253 mutt_debug(LL_DEBUG1, "Error setting default verify paths\n");
1254 goto free_ctx;
1255 }
1256 }
1257
1258 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
1259 if (c_certificate_file && !ssl_load_certificates(sockdata(conn)->sctx))
1260 mutt_debug(LL_DEBUG1, "Error loading trusted certificates\n");
1261
1262 ssl_get_client_cert(sockdata(conn), conn);
1263
1264 const char *const c_ssl_ciphers = cs_subset_string(NeoMutt->sub, "ssl_ciphers");
1265 if (c_ssl_ciphers)
1266 {
1267 SSL_CTX_set_cipher_list(sockdata(conn)->sctx, c_ssl_ciphers);
1268 }
1269
1270 if (!ssl_set_verify_partial(sockdata(conn)->sctx))
1271 {
1272 mutt_error(_("Warning: error enabling ssl_verify_partial_chains"));
1273 }
1274
1275 sockdata(conn)->ssl = SSL_new(sockdata(conn)->sctx);
1276 SSL_set_fd(sockdata(conn)->ssl, conn->fd);
1277
1278 if (ssl_negotiate(conn, sockdata(conn)))
1279 goto free_ssl;
1280
1281 sockdata(conn)->isopen = 1;
1282 conn->ssf = SSL_CIPHER_get_bits(SSL_get_current_cipher(sockdata(conn)->ssl), &maxbits);
1283
1284 return 0;
1285
1286free_ssl:
1287 SSL_free(sockdata(conn)->ssl);
1288 sockdata(conn)->ssl = NULL;
1289free_ctx:
1290 SSL_CTX_free(sockdata(conn)->sctx);
1291 sockdata(conn)->sctx = NULL;
1292free_ssldata:
1293 FREE(&conn->sockdata);
1294
1295 return -1;
1296}
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:1120
static bool ssl_load_certificates(SSL_CTX *ctx)
Load certificates and filter out the expired ones.
Definition openssl.c:120
static void ssl_dprint_err_stack(void)
Dump the SSL error stack.
Definition openssl.c:309
static struct SslSockData * sockdata(struct Connection *conn)
Get a Connection's socket data.
Definition openssl.c:1197
static bool ssl_set_verify_partial(SSL_CTX *ctx)
Allow verification using partial chains (with no root)
Definition openssl.c:169
static void ssl_get_client_cert(struct SslSockData *ssldata, struct Connection *conn)
Get the client certificate for an SSL connection.
Definition openssl.c:606
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 1418 of file openssl.c.

1419{
1420 if (ssl_init())
1421 return -1;
1422
1423 int rc = ssl_setup(conn);
1424
1425 /* hmm. watch out if we're starting TLS over any method other than raw. */
1426 conn->read = ssl_socket_read;
1427 conn->write = ssl_socket_write;
1429 conn->poll = ssl_socket_poll;
1430
1431 return rc;
1432}
static int ssl_socket_close_and_restore(struct Connection *conn)
Close an SSL Connection and restore Connection callbacks - Implements Connection::close() -.
Definition openssl.c:622
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:1301
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:1330
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:1362
static int ssl_setup(struct Connection *conn)
Set up SSL on the Connection.
Definition openssl.c:1208
static int ssl_init(void)
Initialise the SSL library.
Definition openssl.c:553
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 1440 of file openssl.c.

1441{
1442 if (ssl_init() < 0)
1443 {
1444 conn->open = ssl_socket_open_err;
1445 return -1;
1446 }
1447
1448 conn->open = ssl_socket_open;
1449 conn->read = ssl_socket_read;
1450 conn->write = ssl_socket_write;
1451 conn->poll = ssl_socket_poll;
1452 conn->close = ssl_socket_close;
1453
1454 return 0;
1455}
static int ssl_socket_close(struct Connection *conn)
Close an SSL connection - Implements Connection::close() -.
Definition openssl.c:1393
static int ssl_socket_open_err(struct Connection *conn)
Error callback for opening an SSL connection - Implements Connection::open() -.
Definition openssl.c:357
static int ssl_socket_open(struct Connection *conn)
Open an SSL socket - Implements Connection::open() -.
Definition openssl.c:1315
int(* open)(struct Connection *conn)
Definition connection.h:66
+ Here is the call graph for this function:

Variable Documentation

◆ HostExDataIndex

int HostExDataIndex = -1
static

index for storing hostname as application specific data in SSL structure

Definition at line 86 of file openssl.c.

◆ SkipModeExDataIndex

int SkipModeExDataIndex = -1
static

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.

Definition at line 90 of file openssl.c.