NeoMutt  2024-03-23-142-g2b2e76
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 (char *s, int l, 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 ( 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 387 of file openssl.c.

388{
389 unsigned char md[EVP_MAX_MD_SIZE];
390 unsigned int n;
391
392 if (X509_digest(cert, hashfunc(), md, &n) == 0) // Failure
393 {
394 snprintf(s, l, "%s", _("[unable to calculate]"));
395 }
396 else
397 {
398 for (unsigned int i = 0; i < n; i++)
399 {
400 char ch[8] = { 0 };
401 snprintf(ch, sizeof(ch), "%02X%s", md[i], ((i % 2) ? " " : ""));
402 mutt_str_cat(s, l, ch);
403 }
404 }
405}
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:268
+ 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:630
+ 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:474
+ 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:775
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:545
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:852
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 char buf[256] = { 0 };
905 struct CertArray carr = ARRAY_HEAD_INITIALIZER;
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 buf[0] = '\0';
921 x509_fingerprint(buf, sizeof(buf), cert, EVP_sha1);
922 mutt_str_asprintf(&line, _("SHA1 Fingerprint: %s"), buf);
923 ARRAY_ADD(&carr, line);
924 buf[0] = '\0';
925 buf[40] = '\0'; /* Ensure the second printed line is null terminated */
926 x509_fingerprint(buf, sizeof(buf), cert, EVP_sha256);
927 buf[39] = '\0'; /* Divide into two lines of output */
928 mutt_str_asprintf(&line, "%s%s", _("SHA256 Fingerprint: "), buf);
929 ARRAY_ADD(&carr, line);
930 mutt_str_asprintf(&line, "%*s%s",
931 (int) mutt_str_len(_("SHA256 Fingerprint: ")), "", buf + 40);
932 ARRAY_ADD(&carr, line);
933
934 bool allow_skip = false;
935/* The leaf/host certificate can't be skipped. */
936#ifdef HAVE_SSL_PARTIAL_CHAIN
937 const bool c_ssl_verify_partial_chains = cs_subset_bool(NeoMutt->sub, "ssl_verify_partial_chains");
938 if ((idx != 0) && c_ssl_verify_partial_chains)
939 allow_skip = true;
940#endif
941
942 char title[256] = { 0 };
943 snprintf(title, sizeof(title),
944 _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
945
946 /* Inside ssl_verify_callback(), this function is guarded by a call to
947 * check_certificate_by_digest(). This means if check_certificate_expiration() is
948 * true, then check_certificate_file() must be false. Therefore we don't need
949 * to also scan the certificate file here. */
950 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
951 allow_always = allow_always && c_certificate_file &&
953
954 int rc = dlg_certificate(title, &carr, allow_always, allow_skip);
955 if ((rc == 3) && !allow_always)
956 rc = 4;
957
958 switch (rc)
959 {
960 case 1: // Reject
961 break;
962 case 2: // Once
963 SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
965 break;
966 case 3: // Always
967 {
968 bool saved = false;
969 FILE *fp = mutt_file_fopen(c_certificate_file, "a");
970 if (fp)
971 {
972 if (PEM_write_X509(fp, cert))
973 saved = true;
974 mutt_file_fclose(&fp);
975 }
976
977 if (saved)
978 mutt_message(_("Certificate saved"));
979 else
980 mutt_error(_("Warning: Couldn't save certificate"));
981
982 SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
984 break;
985 }
986 case 4: // Skip
987 SSL_set_ex_data(ssl, SkipModeExDataIndex, &SkipModeExDataIndex);
988 break;
989 }
990
991 cert_array_clear(&carr);
992 return (rc > 1);
993}
#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 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 void x509_fingerprint(char *s, int l, X509 *cert, const EVP_MD *(*hashfunc)(void))
Generate a fingerprint for an X509 certificate.
Definition: openssl.c:387
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 1006 of file openssl.c.

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

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

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

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

1428{
1429 if (ssl_init())
1430 return -1;
1431
1432 int rc = ssl_setup(conn);
1433
1434 /* hmm. watch out if we're starting TLS over any method other than raw. */
1435 conn->read = ssl_socket_read;
1436 conn->write = ssl_socket_write;
1438 conn->poll = ssl_socket_poll;
1439
1440 return rc;
1441}
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:1313
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:1342
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:1371
static int ssl_setup(struct Connection *conn)
Set up SSL on the Connection.
Definition: openssl.c:1210
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 1449 of file openssl.c.

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