NeoMutt  2024-04-25-102-g19653a
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, "rt");
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:149
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:148
#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);
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}
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:91
#define FREE(x)
Definition: memory.h:45
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ssl_passwd_cb()

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

Callback to get a password.

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

Definition at line 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:38
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:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
#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); i-- > 0;)
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, "rt");
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((size_t) bufsize);
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 x509_fingerprint(buf, cert, EVP_sha256);
920 buf->data[39] = '\0'; /* Divide into two lines of output */
921 mutt_str_asprintf(&line, "%s%s", _("SHA256 Fingerprint: "), buf_string(buf));
922 ARRAY_ADD(&carr, line);
923 mutt_str_asprintf(&line, "%*s%s", (int) mutt_str_len(_("SHA256 Fingerprint: ")),
924 "", buf->data + 40);
925 ARRAY_ADD(&carr, line);
926
927 bool allow_skip = false;
928/* The leaf/host certificate can't be skipped. */
929#ifdef HAVE_SSL_PARTIAL_CHAIN
930 const bool c_ssl_verify_partial_chains = cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
931 if ((idx != 0) && c_ssl_verify_partial_chains)
932 allow_skip = true;
933#endif
934
935 char title[256] = { 0 };
936 snprintf(title, sizeof(title),
937 _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
938
939 /* Inside ssl_verify_callback(), this function is guarded by a call to
940 * check_certificate_by_digest(). This means if check_certificate_expiration() is
941 * true, then check_certificate_file() must be false. Therefore we don't need
942 * to also scan the certificate file here. */
943 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
944 allow_always = allow_always && c_certificate_file &&
946
947 int rc = dlg_certificate(title, &carr, allow_always, allow_skip);
948 if ((rc == 3) && !allow_always)
949 rc = 4;
950
951 switch (rc)
952 {
953 case 1: // Reject
954 break;
955 case 2: // Once
956 SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
958 break;
959 case 3: // Always
960 {
961 bool saved = false;
962 FILE *fp = mutt_file_fopen(c_certificate_file, "a");
963 if (fp)
964 {
965 if (PEM_write_X509(fp, cert))
966 saved = true;
967 mutt_file_fclose(&fp);
968 }
969
970 if (saved)
971 mutt_message(_("Certificate saved"));
972 else
973 mutt_error(_("Warning: Couldn't save certificate"));
974
975 SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
977 break;
978 }
979 case 4: // Skip
980 SSL_set_ex_data(ssl, SkipModeExDataIndex, &SkipModeExDataIndex);
981 break;
982 }
983
984 cert_array_clear(&carr);
985 buf_pool_release(&buf);
986
987 return (rc > 1);
988}
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:58
void cert_array_clear(struct CertArray *carr)
Free all memory of a CertArray.
bool OptNoCurses
(pseudo) when sending in batch mode
Definition: globals.c:70
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 1001 of file openssl.c.

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

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

1195{
1196 return conn->sockdata;
1197}
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 1205 of file openssl.c.

1206{
1207 int maxbits = 0;
1208
1209 conn->sockdata = mutt_mem_calloc(1, sizeof(struct SslSockData));
1210
1211 sockdata(conn)->sctx = SSL_CTX_new(SSLv23_client_method());
1212 if (!sockdata(conn)->sctx)
1213 {
1214 /* L10N: an SSL context is a data structure returned by the OpenSSL
1215 function SSL_CTX_new(). In this case it returned NULL: an
1216 error condition. */
1217 mutt_error(_("Unable to create SSL context"));
1219 goto free_ssldata;
1220 }
1221
1222 /* disable SSL protocols as needed */
1223#ifdef SSL_OP_NO_TLSv1_3
1224 const bool c_ssl_use_tlsv1_3 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_3");
1225 if (!c_ssl_use_tlsv1_3)
1226 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_3);
1227#endif
1228
1229#ifdef SSL_OP_NO_TLSv1_2
1230 const bool c_ssl_use_tlsv1_2 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_2");
1231 if (!c_ssl_use_tlsv1_2)
1232 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_2);
1233#endif
1234
1235#ifdef SSL_OP_NO_TLSv1_1
1236 const bool c_ssl_use_tlsv1_1 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_1");
1237 if (!c_ssl_use_tlsv1_1)
1238 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_1);
1239#endif
1240
1241#ifdef SSL_OP_NO_TLSv1
1242 const bool c_ssl_use_tlsv1 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1");
1243 if (!c_ssl_use_tlsv1)
1244 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1);
1245#endif
1246
1247 const bool c_ssl_use_sslv3 = cs_subset_bool(NeoMutt->sub, "ssl_use_sslv3");
1248 if (!c_ssl_use_sslv3)
1249 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_SSLv3);
1250
1251 const bool c_ssl_use_sslv2 = cs_subset_bool(NeoMutt->sub, "ssl_use_sslv2");
1252 if (!c_ssl_use_sslv2)
1253 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_SSLv2);
1254
1255 const bool c_ssl_use_system_certs = cs_subset_bool(NeoMutt->sub, "ssl_use_system_certs");
1256 if (c_ssl_use_system_certs)
1257 {
1258 if (!SSL_CTX_set_default_verify_paths(sockdata(conn)->sctx))
1259 {
1260 mutt_debug(LL_DEBUG1, "Error setting default verify paths\n");
1261 goto free_ctx;
1262 }
1263 }
1264
1265 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
1266 if (c_certificate_file && !ssl_load_certificates(sockdata(conn)->sctx))
1267 mutt_debug(LL_DEBUG1, "Error loading trusted certificates\n");
1268
1269 ssl_get_client_cert(sockdata(conn), conn);
1270
1271 const char *const c_ssl_ciphers = cs_subset_string(NeoMutt->sub, "ssl_ciphers");
1272 if (c_ssl_ciphers)
1273 {
1274 SSL_CTX_set_cipher_list(sockdata(conn)->sctx, c_ssl_ciphers);
1275 }
1276
1277 if (!ssl_set_verify_partial(sockdata(conn)->sctx))
1278 {
1279 mutt_error(_("Warning: error enabling ssl_verify_partial_chains"));
1280 }
1281
1282 sockdata(conn)->ssl = SSL_new(sockdata(conn)->sctx);
1283 SSL_set_fd(sockdata(conn)->ssl, conn->fd);
1284
1285 if (ssl_negotiate(conn, sockdata(conn)))
1286 goto free_ssl;
1287
1288 sockdata(conn)->isopen = 1;
1289 conn->ssf = SSL_CIPHER_get_bits(SSL_get_current_cipher(sockdata(conn)->ssl), &maxbits);
1290
1291 return 0;
1292
1293free_ssl:
1294 SSL_free(sockdata(conn)->ssl);
1295 sockdata(conn)->ssl = NULL;
1296free_ctx:
1297 SSL_CTX_free(sockdata(conn)->sctx);
1298 sockdata(conn)->sctx = NULL;
1299free_ssldata:
1300 FREE(&conn->sockdata);
1301
1302 return -1;
1303}
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:291
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:51
static int ssl_negotiate(struct Connection *conn, struct SslSockData *ssldata)
Attempt to negotiate SSL over the wire.
Definition: openssl.c:1117
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:1194
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 1422 of file openssl.c.

1423{
1424 if (ssl_init())
1425 return -1;
1426
1427 int rc = ssl_setup(conn);
1428
1429 /* hmm. watch out if we're starting TLS over any method other than raw. */
1430 conn->read = ssl_socket_read;
1431 conn->write = ssl_socket_write;
1433 conn->poll = ssl_socket_poll;
1434
1435 return rc;
1436}
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:1308
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:1337
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:1366
static int ssl_setup(struct Connection *conn)
Set up SSL on the Connection.
Definition: openssl.c:1205
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 1444 of file openssl.c.

1445{
1446 if (ssl_init() < 0)
1447 {
1448 conn->open = ssl_socket_open_err;
1449 return -1;
1450 }
1451
1452 conn->open = ssl_socket_open;
1453 conn->read = ssl_socket_read;
1454 conn->write = ssl_socket_write;
1455 conn->poll = ssl_socket_poll;
1456 conn->close = ssl_socket_close;
1457
1458 return 0;
1459}
static int ssl_socket_close(struct Connection *conn)
Close an SSL connection - Implements Connection::close() -.
Definition: openssl.c:1397
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:1322
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.