NeoMutt  2024-04-16-36-g75b6fb
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:169
#define mutt_file_fclose(FP)
Definition: file.h:147
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:146
#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:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
+ 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:48
+ 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:90
#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:131
int mutt_account_getuser(struct ConnAccount *cac)
Retrieve username into ConnAccount, if necessary.
Definition: connaccount.c:52
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:203
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:394
+ 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:575
+ 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:419
+ 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:160
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:720
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
617 /* if we are using a client cert, SASL may expect an external auth name */
618 if (mutt_account_getuser(&conn->account) < 0)
619 mutt_debug(LL_DEBUG1, "Couldn't get user info\n");
620}
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 641 of file openssl.c.

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

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

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

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

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

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

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

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

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

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

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

1427{
1428 if (ssl_init())
1429 return -1;
1430
1431 int rc = ssl_setup(conn);
1432
1433 /* hmm. watch out if we're starting TLS over any method other than raw. */
1434 conn->read = ssl_socket_read;
1435 conn->write = ssl_socket_write;
1437 conn->poll = ssl_socket_poll;
1438
1439 return rc;
1440}
static int ssl_socket_close_and_restore(struct Connection *conn)
Close an SSL Connection and restore Connection callbacks - Implements Connection::close() -.
Definition: openssl.c:625
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:1312
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:1341
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:1370
static int ssl_setup(struct Connection *conn)
Set up SSL on the Connection.
Definition: openssl.c:1209
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 1448 of file openssl.c.

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