NeoMutt  2022-04-29-215-gc12b98
Teaching an old dog new tricks
DOXYGEN
openssl.c File Reference

Handling of OpenSSL encryption. More...

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

Go to the source code of this file.

Functions

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

Variables

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

Detailed Description

Handling of OpenSSL encryption.

Authors
  • Tommi Komulainen

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

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

Definition in file openssl.c.

Function Documentation

◆ STACK_OF()

static STACK_OF ( X509  )
static

SSL socket data -.

Definition at line 89 of file openssl.c.

97{
98 SSL_CTX *sctx;
99 SSL *ssl;
100 unsigned char isopen;
101};
+ 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 115 of file openssl.c.

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

◆ ssl_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 164 of file openssl.c.

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

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

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

◆ ssl_dprint_err_stack()

static void ssl_dprint_err_stack ( void  )
static

Dump the SSL error stack.

Definition at line 304 of file openssl.c.

305{
306 BIO *bio = BIO_new(BIO_s_mem());
307 if (!bio)
308 return;
309 ERR_print_errors(bio);
310
311 char *buf = NULL;
312 long buflen = BIO_get_mem_data(bio, &buf);
313 if (buflen > 0)
314 {
315 char *output = mutt_mem_malloc(buflen + 1);
316 memcpy(output, buf, buflen);
317 output[buflen] = '\0';
318 mutt_debug(LL_DEBUG1, "SSL error stack: %s\n", output);
319 FREE(&output);
320 }
321 BIO_free(bio);
322}
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define FREE(x)
Definition: memory.h:43
+ 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 333 of file openssl.c.

334{
335 struct ConnAccount *cac = userdata;
336
337 if (mutt_account_getuser(cac) < 0)
338 return 0;
339
340 mutt_debug(LL_DEBUG2, "getting password for %s@%s:%u\n", cac->user, cac->host, cac->port);
341
342 if (mutt_account_getpass(cac) < 0)
343 return 0;
344
345 return snprintf(buf, buflen, "%s", cac->pass);
346}
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:50
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 366 of file openssl.c.

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

◆ x509_fingerprint()

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

Generate a fingerprint for an X509 certificate.

Parameters
sBuffer for fingerprint
lLength of buffer
certCertificate
hashfuncHashing function

Definition at line 383 of file openssl.c.

384{
385 unsigned char md[EVP_MAX_MD_SIZE];
386 unsigned int n;
387
388 if (X509_digest(cert, hashfunc(), md, &n) == 0) // Failure
389 {
390 snprintf(s, l, "%s", _("[unable to calculate]"));
391 }
392 else
393 {
394 for (unsigned int i = 0; i < n; i++)
395 {
396 char ch[8] = { 0 };
397 snprintf(ch, sizeof(ch), "%02X%s", md[i], ((i % 2) ? " " : ""));
398 mutt_str_cat(s, l, ch);
399 }
400 }
401}
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:265
+ 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 410 of file openssl.c.

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

◆ compare_certificates()

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

Compare two X509 certificated.

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

Definition at line 437 of file openssl.c.

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

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

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

549{
550 static bool init_complete = false;
551
552 if (init_complete)
553 return 0;
554
555 if (RAND_status() != 1)
556 {
557 /* load entropy from files */
558 struct Buffer *path = mutt_buffer_pool_get();
559 const char *const c_entropy_file = cs_subset_path(NeoMutt->sub, "entropy_file");
560 add_entropy(c_entropy_file);
561 add_entropy(RAND_file_name(path->data, path->dsize));
562
563/* load entropy from egd sockets */
564#ifdef HAVE_RAND_EGD
565 add_entropy(mutt_str_getenv("EGDSOCKET"));
566 mutt_buffer_printf(path, "%s/.entropy", NONULL(HomeDir));
568 add_entropy(TMPDIR "/entropy");
569#endif
570
571 /* shuffle $RANDFILE (or ~/.rnd if unset) */
572 RAND_write_file(RAND_file_name(path->data, path->dsize));
574
576 if (RAND_status() != 1)
577 {
578 mutt_error(_("Failed to find enough entropy on your system"));
579 return -1;
580 }
581 }
582
583/* OpenSSL performs automatic initialization as of 1.1.
584 * However LibreSSL does not (as of 2.8.3). */
585#if (defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x10100000L)) || \
586 (defined(LIBRESSL_VERSION_NUMBER))
587 /* I don't think you can do this just before reading the error. The call
588 * itself might clobber the last SSL error. */
589 SSL_load_error_strings();
590 SSL_library_init();
591#endif
592 init_complete = true;
593 return 0;
594}
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:168
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
char * HomeDir
User's home directory.
Definition: mutt_globals.h:49
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:927
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:200
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define NONULL(x)
Definition: string2.h:37
String manipulation buffer.
Definition: buffer.h:34
size_t dsize
Length of data.
Definition: buffer.h:37
char * data
Pointer to data.
Definition: buffer.h:35
+ 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 601 of file openssl.c.

602{
603 const char *const c_ssl_client_cert = cs_subset_path(NeoMutt->sub, "ssl_client_cert");
604 if (!c_ssl_client_cert)
605 return;
606
607 mutt_debug(LL_DEBUG2, "Using client certificate %s\n", c_ssl_client_cert);
608 SSL_CTX_set_default_passwd_cb_userdata(ssldata->sctx, &conn->account);
609 SSL_CTX_set_default_passwd_cb(ssldata->sctx, ssl_passwd_cb);
610 SSL_CTX_use_certificate_file(ssldata->sctx, c_ssl_client_cert, SSL_FILETYPE_PEM);
611 SSL_CTX_use_PrivateKey_file(ssldata->sctx, c_ssl_client_cert, SSL_FILETYPE_PEM);
612
613 /* if we are using a client cert, SASL may expect an external auth name */
614 if (mutt_account_getuser(&conn->account) < 0)
615 mutt_debug(LL_DEBUG1, "Couldn't get user info\n");
616}
static int ssl_passwd_cb(char *buf, int buflen, int rwflag, void *userdata)
Callback to get a password.
Definition: openssl.c:333
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
+ 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 (compare_certificates(cert, peercert, peermd, peermdlen))
652 {
653 return true;
654 }
655 }
656
657 return false;
658}
static bool compare_certificates(X509 *cert, X509 *peercert, unsigned char *peermd, unsigned int peermdlen)
Compare two X509 certificated.
Definition: openssl.c:437
+ 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 (compare_certificates(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:467
+ 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)
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
static STACK_OF(X509)
SSL socket data -.
Definition: openssl.c:89
static bool hostname_match(const char *hostname, const char *certname)
Does the hostname match the certificate.
Definition: openssl.c:502
+ 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:155
#define mutt_array_size(x)
Definition: memory.h:36
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1031
static char * x509_get_part(X509_NAME *name, int nid)
Retrieve from X509 data.
Definition: openssl.c:366
+ 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 char buf[256] = { 0 };
894 struct CertArray carr = ARRAY_HEAD_INITIALIZER;
895
896 add_cert(_("This certificate belongs to:"), cert, false, &carr);
897 ARRAY_ADD(&carr, NULL);
898 add_cert(_("This certificate was issued by:"), cert, true, &carr);
899
900 char *line = NULL;
901 ARRAY_ADD(&carr, NULL);
902 ARRAY_ADD(&carr, mutt_str_dup(_("This certificate is valid")));
903 mutt_str_asprintf(&line, _(" from %s"), asn1time_to_string(X509_getm_notBefore(cert)));
904 ARRAY_ADD(&carr, line);
905 mutt_str_asprintf(&line, _(" to %s"), asn1time_to_string(X509_getm_notAfter(cert)));
906 ARRAY_ADD(&carr, line);
907
908 ARRAY_ADD(&carr, NULL);
909 buf[0] = '\0';
910 x509_fingerprint(buf, sizeof(buf), cert, EVP_sha1);
911 mutt_str_asprintf(&line, _("SHA1 Fingerprint: %s"), buf);
912 ARRAY_ADD(&carr, line);
913 buf[0] = '\0';
914 buf[40] = '\0'; /* Ensure the second printed line is null terminated */
915 x509_fingerprint(buf, sizeof(buf), cert, EVP_sha256);
916 buf[39] = '\0'; /* Divide into two lines of output */
917 mutt_str_asprintf(&line, "%s%s", _("SHA256 Fingerprint: "), buf);
918 ARRAY_ADD(&carr, line);
919 mutt_str_asprintf(&line, "%*s%s",
920 (int) mutt_str_len(_("SHA256 Fingerprint: ")), "", buf + 40);
921 ARRAY_ADD(&carr, line);
922
923 bool allow_skip = false;
924/* The leaf/host certificate can't be skipped. */
925#ifdef HAVE_SSL_PARTIAL_CHAIN
926 const bool c_ssl_verify_partial_chains = cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
927 if ((idx != 0) && c_ssl_verify_partial_chains)
928 allow_skip = true;
929#endif
930
931 char title[256] = { 0 };
932 snprintf(title, sizeof(title),
933 _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
934
935 /* Inside ssl_verify_callback(), this function is guarded by a call to
936 * check_certificate_by_digest(). This means if check_certificate_expiration() is
937 * true, then check_certificate_file() must be false. Therefore we don't need
938 * to also scan the certificate file here. */
939 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
940 allow_always = allow_always && c_certificate_file &&
942
943 int rc = dlg_verify_certificate(title, &carr, allow_always, allow_skip);
944 if ((rc == 3) && !allow_always)
945 rc = 4;
946
947 switch (rc)
948 {
949 case 1: // Reject
950 break;
951 case 2: // Once
952 SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
954 break;
955 case 3: // Always
956 {
957 bool saved = false;
958 FILE *fp = mutt_file_fopen(c_certificate_file, "a");
959 if (fp)
960 {
961 if (PEM_write_X509(fp, cert))
962 saved = true;
963 mutt_file_fclose(&fp);
964 }
965
966 if (saved)
967 mutt_message(_("Certificate saved"));
968 else
969 mutt_error(_("Warning: Couldn't save certificate"));
970
971 SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
973 break;
974 }
975 case 4: // Skip
976 SSL_set_ex_data(ssl, SkipModeExDataIndex, &SkipModeExDataIndex);
977 break;
978 }
979
980 cert_array_clear(&carr);
981 return (rc > 1);
982}
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:57
void cert_array_clear(struct CertArray *carr)
Free all memory of a CertArray.
int dlg_verify_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:410
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 void x509_fingerprint(char *s, int l, X509 *cert, const EVP_MD *(*hashfunc)(void))
Generate a fingerprint for an X509 certificate.
Definition: openssl.c:383
static int SkipModeExDataIndex
Definition: openssl.c:85
+ 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 995 of file openssl.c.

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

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

1184{
1185 return conn->sockdata;
1186}
void * sockdata
Backend-specific socket data.
Definition: connection.h:56
+ 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 1194 of file openssl.c.

1195{
1196 int maxbits = 0;
1197
1198 conn->sockdata = mutt_mem_calloc(1, sizeof(struct SslSockData));
1199
1200 sockdata(conn)->sctx = SSL_CTX_new(SSLv23_client_method());
1201 if (!sockdata(conn)->sctx)
1202 {
1203 /* L10N: an SSL context is a data structure returned by the OpenSSL
1204 function SSL_CTX_new(). In this case it returned NULL: an
1205 error condition. */
1206 mutt_error(_("Unable to create SSL context"));
1208 goto free_ssldata;
1209 }
1210
1211 /* disable SSL protocols as needed */
1212#ifdef SSL_OP_NO_TLSv1_3
1213 const bool c_ssl_use_tlsv1_3 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_3");
1214 if (!c_ssl_use_tlsv1_3)
1215 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_3);
1216#endif
1217
1218#ifdef SSL_OP_NO_TLSv1_2
1219 const bool c_ssl_use_tlsv1_2 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_2");
1220 if (!c_ssl_use_tlsv1_2)
1221 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_2);
1222#endif
1223
1224#ifdef SSL_OP_NO_TLSv1_1
1225 const bool c_ssl_use_tlsv1_1 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_1");
1226 if (!c_ssl_use_tlsv1_1)
1227 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1_1);
1228#endif
1229
1230#ifdef SSL_OP_NO_TLSv1
1231 const bool c_ssl_use_tlsv1 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1");
1232 if (!c_ssl_use_tlsv1)
1233 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_TLSv1);
1234#endif
1235
1236 const bool c_ssl_use_sslv3 = cs_subset_bool(NeoMutt->sub, "ssl_use_sslv3");
1237 if (!c_ssl_use_sslv3)
1238 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_SSLv3);
1239
1240 const bool c_ssl_use_sslv2 = cs_subset_bool(NeoMutt->sub, "ssl_use_sslv2");
1241 if (!c_ssl_use_sslv2)
1242 SSL_CTX_set_options(sockdata(conn)->sctx, SSL_OP_NO_SSLv2);
1243
1244 const bool c_ssl_use_system_certs = cs_subset_bool(NeoMutt->sub, "ssl_use_system_certs");
1245 if (c_ssl_use_system_certs)
1246 {
1247 if (!SSL_CTX_set_default_verify_paths(sockdata(conn)->sctx))
1248 {
1249 mutt_debug(LL_DEBUG1, "Error setting default verify paths\n");
1250 goto free_ctx;
1251 }
1252 }
1253
1254 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
1255 if (c_certificate_file && !ssl_load_certificates(sockdata(conn)->sctx))
1256 mutt_debug(LL_DEBUG1, "Error loading trusted certificates\n");
1257
1258 ssl_get_client_cert(sockdata(conn), conn);
1259
1260 const char *const c_ssl_ciphers = cs_subset_string(NeoMutt->sub, "ssl_ciphers");
1261 if (c_ssl_ciphers)
1262 {
1263 SSL_CTX_set_cipher_list(sockdata(conn)->sctx, c_ssl_ciphers);
1264 }
1265
1266 if (!ssl_set_verify_partial(sockdata(conn)->sctx))
1267 {
1268 mutt_error(_("Warning: error enabling ssl_verify_partial_chains"));
1269 }
1270
1271 sockdata(conn)->ssl = SSL_new(sockdata(conn)->sctx);
1272 SSL_set_fd(sockdata(conn)->ssl, conn->fd);
1273
1274 if (ssl_negotiate(conn, sockdata(conn)))
1275 goto free_ssl;
1276
1277 sockdata(conn)->isopen = 1;
1278 conn->ssf = SSL_CIPHER_get_bits(SSL_get_current_cipher(sockdata(conn)->ssl), &maxbits);
1279
1280 return 0;
1281
1282free_ssl:
1283 SSL_free(sockdata(conn)->ssl);
1284 sockdata(conn)->ssl = NULL;
1285free_ctx:
1286 SSL_CTX_free(sockdata(conn)->sctx);
1287 sockdata(conn)->sctx = NULL;
1288free_ssldata:
1289 FREE(&conn->sockdata);
1290
1291 return -1;
1292}
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
static int ssl_negotiate(struct Connection *conn, struct SslSockData *ssldata)
Attempt to negotiate SSL over the wire.
Definition: openssl.c:1111
static bool ssl_load_certificates(SSL_CTX *ctx)
Load certificates and filter out the expired ones.
Definition: openssl.c:115
static void ssl_dprint_err_stack(void)
Dump the SSL error stack.
Definition: openssl.c:304
static struct SslSockData * sockdata(struct Connection *conn)
Get a Connection's socket data.
Definition: openssl.c:1183
static bool ssl_set_verify_partial(SSL_CTX *ctx)
Allow verification using partial chains (with no root)
Definition: openssl.c:164
static void ssl_get_client_cert(struct SslSockData *ssldata, struct Connection *conn)
Get the client certificate for an SSL connection.
Definition: openssl.c:601
unsigned int ssf
Security strength factor, in bits (see notes)
Definition: connection.h:51
int fd
Socket file descriptor.
Definition: connection.h:54
+ 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 1394 of file openssl.c.

1395{
1396 if (ssl_init())
1397 return -1;
1398
1399 int rc = ssl_setup(conn);
1400
1401 /* hmm. watch out if we're starting TLS over any method other than raw. */
1402 conn->read = ssl_socket_read;
1403 conn->write = ssl_socket_write;
1405 conn->poll = ssl_socket_poll;
1406
1407 return rc;
1408}
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 whether a socket read would block - Implements Connection::poll() -.
Definition: openssl.c:1297
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:1326
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:1348
static int ssl_setup(struct Connection *conn)
Set up SSL on the Connection.
Definition: openssl.c:1194
static int ssl_init(void)
Initialise the SSL library.
Definition: openssl.c:548
int(* poll)(struct Connection *conn, time_t wait_secs)
Definition: connection.h:106
int(* write)(struct Connection *conn, const char *buf, size_t count)
Definition: connection.h:93
int(* close)(struct Connection *conn)
Definition: connection.h:117
int(* read)(struct Connection *conn, char *buf, size_t count)
Definition: connection.h:80
+ 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 1416 of file openssl.c.

1417{
1418 if (ssl_init() < 0)
1419 {
1420 conn->open = ssl_socket_open_err;
1421 return -1;
1422 }
1423
1424 conn->open = ssl_socket_open;
1425 conn->read = ssl_socket_read;
1426 conn->write = ssl_socket_write;
1427 conn->poll = ssl_socket_poll;
1428 conn->close = ssl_socket_close;
1429
1430 return 0;
1431}
static int ssl_socket_close(struct Connection *conn)
Close an SSL connection - Implements Connection::close() -.
Definition: openssl.c:1369
static int ssl_socket_open_err(struct Connection *conn)
Error callback for opening an SSL connection - Implements Connection::open() -.
Definition: openssl.c:352
static int ssl_socket_open(struct Connection *conn)
Open an SSL socket - Implements Connection::open() -.
Definition: openssl.c:1311
int(* open)(struct Connection *conn)
Definition: connection.h:67
+ Here is the call graph for this function:

Variable Documentation

◆ HostExDataIndex

int HostExDataIndex = -1
static

Definition at line 80 of file openssl.c.

◆ SkipModeExDataIndex

int SkipModeExDataIndex = -1
static

Definition at line 85 of file openssl.c.