NeoMutt  2025-01-09-41-g086358
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 CertArray *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

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 -

Definition at line 93 of file openssl.c.

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

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

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

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

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

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

338{
339 struct ConnAccount *cac = userdata;
340
341 if (mutt_account_getuser(cac) < 0)
342 return 0;
343
344 mutt_debug(LL_DEBUG2, "getting password for %s@%s:%u\n", cac->user, cac->host, cac->port);
345
346 if (mutt_account_getpass(cac) < 0)
347 return 0;
348
349 return snprintf(buf, buflen, "%s", cac->pass);
350}
int mutt_account_getpass(struct ConnAccount *cac)
Fetch password into ConnAccount, if necessary.
Definition: connaccount.c:130
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 370 of file openssl.c.

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

◆ x509_fingerprint()

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

Generate a fingerprint for an X509 certificate.

Parameters
bufBuffer for fingerprint
certCertificate
hashfuncHashing function

Definition at line 386 of file openssl.c.

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

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

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

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

507{
508 const char *cmp1 = NULL, *cmp2 = NULL;
509
510 if (mutt_strn_equal(certname, "*.", 2))
511 {
512 cmp1 = certname + 2;
513 cmp2 = strchr(hostname, '.');
514 if (!cmp2)
515 return false;
516
517 cmp2++;
518 }
519 else
520 {
521 cmp1 = certname;
522 cmp2 = hostname;
523 }
524
525 if ((*cmp1 == '\0') || (*cmp2 == '\0'))
526 {
527 return false;
528 }
529
530 if (strcasecmp(cmp1, cmp2) != 0)
531 {
532 return false;
533 }
534
535 return true;
536}
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:425
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ssl_init()

static int ssl_init ( void  )
static

Initialise the SSL library.

Return values
0Success
-1Error

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

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

Definition at line 552 of file openssl.c.

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

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

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

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

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

◆ check_certificate_by_digest()

static bool check_certificate_by_digest ( X509 *  peercert)
static

Validate a certificate by its digest.

Parameters
peercertCertificate
Return values
trueCertificate is valid
falseError

Definition at line 821 of file openssl.c.

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

833{
834 mutt_debug(LL_DEBUG1, "trusted\n");
835 if (!SslSessionCerts)
836 SslSessionCerts = sk_X509_new_null();
837 return sk_X509_push(SslSessionCerts, X509_dup(c));
838}
+ Here is the caller graph for this function:

◆ add_cert()

static void add_cert ( const char *  title,
X509 *  cert,
bool  issuer,
struct CertArray *  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 847 of file openssl.c.

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

892{
893 if (OptNoCurses)
894 {
895 mutt_debug(LL_DEBUG1, "unable to prompt for certificate in batch mode\n");
896 mutt_error(_("Untrusted server certificate"));
897 return 0;
898 }
899
900 struct CertArray carr = ARRAY_HEAD_INITIALIZER;
901 struct Buffer *buf = buf_pool_get();
902
903 add_cert(_("This certificate belongs to:"), cert, false, &carr);
904 ARRAY_ADD(&carr, NULL);
905 add_cert(_("This certificate was issued by:"), cert, true, &carr);
906
907 char *line = NULL;
908 ARRAY_ADD(&carr, NULL);
909 ARRAY_ADD(&carr, mutt_str_dup(_("This certificate is valid")));
910 mutt_str_asprintf(&line, _(" from %s"), asn1time_to_string(X509_getm_notBefore(cert)));
911 ARRAY_ADD(&carr, line);
912 mutt_str_asprintf(&line, _(" to %s"), asn1time_to_string(X509_getm_notAfter(cert)));
913 ARRAY_ADD(&carr, line);
914
915 ARRAY_ADD(&carr, NULL);
916 x509_fingerprint(buf, cert, EVP_sha1);
917 mutt_str_asprintf(&line, _("SHA1 Fingerprint: %s"), buf_string(buf));
918 ARRAY_ADD(&carr, line);
919
920 buf_reset(buf);
921 x509_fingerprint(buf, cert, EVP_sha256);
922 buf->data[39] = '\0'; /* Divide into two lines of output */
923 mutt_str_asprintf(&line, "%s%s", _("SHA256 Fingerprint: "), buf_string(buf));
924 ARRAY_ADD(&carr, line);
925 mutt_str_asprintf(&line, "%*s%s", (int) mutt_str_len(_("SHA256 Fingerprint: ")),
926 "", buf->data + 40);
927 ARRAY_ADD(&carr, line);
928
929 bool allow_skip = false;
930/* The leaf/host certificate can't be skipped. */
931#ifdef HAVE_SSL_PARTIAL_CHAIN
932 const bool c_ssl_verify_partial_chains = cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
933 if ((idx != 0) && c_ssl_verify_partial_chains)
934 allow_skip = true;
935#endif
936
937 char title[256] = { 0 };
938 snprintf(title, sizeof(title),
939 _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
940
941 /* Inside ssl_verify_callback(), this function is guarded by a call to
942 * check_certificate_by_digest(). This means if check_certificate_expiration() is
943 * true, then check_certificate_file() must be false. Therefore we don't need
944 * to also scan the certificate file here. */
945 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
946 allow_always = allow_always && c_certificate_file &&
948
949 int rc = dlg_certificate(title, &carr, allow_always, allow_skip);
950 if ((rc == 3) && !allow_always)
951 rc = 4;
952
953 switch (rc)
954 {
955 case 1: // Reject
956 break;
957 case 2: // Once
958 SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
960 break;
961 case 3: // Always
962 {
963 bool saved = false;
964 FILE *fp = mutt_file_fopen(c_certificate_file, "a");
965 if (fp)
966 {
967 if (PEM_write_X509(fp, cert))
968 saved = true;
969 mutt_file_fclose(&fp);
970 }
971
972 if (saved)
973 mutt_message(_("Certificate saved"));
974 else
975 mutt_error(_("Warning: Couldn't save certificate"));
976
977 SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
979 break;
980 }
981 case 4: // Skip
982 SSL_set_ex_data(ssl, SkipModeExDataIndex, &SkipModeExDataIndex);
983 break;
984 }
985
986 cert_array_clear(&carr);
987 buf_pool_release(&buf);
988
989 return (rc > 1);
990}
#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
void cert_array_clear(struct CertArray *carr)
Free all memory of a CertArray.
bool OptNoCurses
(pseudo) when sending in batch mode
Definition: globals.c:69
int dlg_certificate(const char *title, struct CertArray *carr, bool allow_always, bool allow_skip)
Ask the user to validate the certificate -.
static char * asn1time_to_string(ASN1_UTCTIME *tm)
Convert a time to a string.
Definition: openssl.c:414
static void x509_fingerprint(struct Buffer *buf, X509 *cert, const EVP_MD *(*hashfunc)(void))
Generate a fingerprint for an X509 certificate.
Definition: openssl.c:386
static int ssl_cache_trusted_cert(X509 *c)
Cache a trusted certificate.
Definition: openssl.c:832
static void add_cert(const char *title, X509 *cert, bool issuer, struct CertArray *carr)
Look up certificate info and save it to a list.
Definition: openssl.c:847
static int SkipModeExDataIndex
Index for storing the "skip mode" state in SSL structure.
Definition: openssl.c:89
+ 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 1003 of file openssl.c.

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

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

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

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

1425{
1426 if (ssl_init())
1427 return -1;
1428
1429 int rc = ssl_setup(conn);
1430
1431 /* hmm. watch out if we're starting TLS over any method other than raw. */
1432 conn->read = ssl_socket_read;
1433 conn->write = ssl_socket_write;
1435 conn->poll = ssl_socket_poll;
1436
1437 return rc;
1438}
static int ssl_socket_close_and_restore(struct Connection *conn)
Close an SSL Connection and restore Connection callbacks - Implements Connection::close() -.
Definition: openssl.c:621
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:1310
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:1339
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:1368
static int ssl_setup(struct Connection *conn)
Set up SSL on the Connection.
Definition: openssl.c:1207
static int ssl_init(void)
Initialise the SSL library.
Definition: openssl.c:552
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 1446 of file openssl.c.

1447{
1448 if (ssl_init() < 0)
1449 {
1450 conn->open = ssl_socket_open_err;
1451 return -1;
1452 }
1453
1454 conn->open = ssl_socket_open;
1455 conn->read = ssl_socket_read;
1456 conn->write = ssl_socket_write;
1457 conn->poll = ssl_socket_poll;
1458 conn->close = ssl_socket_close;
1459
1460 return 0;
1461}
static int ssl_socket_close(struct Connection *conn)
Close an SSL connection - Implements Connection::close() -.
Definition: openssl.c:1399
static int ssl_socket_open_err(struct Connection *conn)
Error callback for opening an SSL connection - Implements Connection::open() -.
Definition: openssl.c:356
static int ssl_socket_open(struct Connection *conn)
Open an SSL socket - Implements Connection::open() -.
Definition: openssl.c:1324
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 85 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 89 of file openssl.c.