NeoMutt  2025-01-09-41-g086358
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
gnutls.c File Reference

Handling of GnuTLS encryption. More...

#include "config.h"
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include "private.h"
#include "mutt/lib.h"
#include "config/lib.h"
#include "core/lib.h"
#include "lib.h"
#include "connaccount.h"
#include "connection.h"
#include "globals.h"
#include "muttlib.h"
#include "ssl.h"
+ Include dependency graph for gnutls.c:

Go to the source code of this file.

Data Structures

struct  TlsSockData
 TLS socket data -. More...
 

Macros

#define CERTERR_VALID   0
 
#define CERTERR_EXPIRED   (1 << 0)
 
#define CERTERR_NOTYETVALID   (1 << 1)
 
#define CERTERR_REVOKED   (1 << 2)
 
#define CERTERR_NOTTRUSTED   (1 << 3)
 
#define CERTERR_HOSTNAME   (1 << 4)
 
#define CERTERR_SIGNERNOTCA   (1 << 5)
 
#define CERTERR_INSECUREALG   (1 << 6)
 
#define CERTERR_OTHER   (1 << 7)
 
#define CERT_SEP   "-----BEGIN"
 

Functions

int gnutls_protocol_set_priority (gnutls_session_t session, const int *list)
 
static int tls_init (void)
 Set up Gnu TLS.
 
static int tls_verify_peers (gnutls_session_t tlsstate, gnutls_certificate_status_t *certstat)
 Wrapper for gnutls_certificate_verify_peers()
 
static void tls_fingerprint (gnutls_digest_algorithm_t algo, struct Buffer *buf, const gnutls_datum_t *data)
 Create a fingerprint of a TLS Certificate.
 
static bool tls_check_stored_hostname (const gnutls_datum_t *cert, const char *hostname)
 Does the hostname match a stored certificate?
 
static int tls_compare_certificates (const gnutls_datum_t *peercert)
 Compare certificates against $certificate_file
 
static int tls_check_preauth (const gnutls_datum_t *certdata, gnutls_certificate_status_t certstat, const char *hostname, int chainidx, int *certerr, int *savedcert)
 Prepare a certificate for authentication.
 
static void add_cert (const char *title, gnutls_x509_crt_t cert, bool issuer, struct CertArray *carr)
 Look up certificate info and save it to a list.
 
static int tls_check_one_certificate (const gnutls_datum_t *certdata, gnutls_certificate_status_t certstat, const char *hostname, int idx, size_t len)
 Check a GnuTLS certificate.
 
static int tls_check_certificate (struct Connection *conn)
 Check a connection's certificate.
 
static void tls_get_client_cert (struct Connection *conn)
 Get the client certificate for a TLS connection.
 
static int tls_set_priority (struct TlsSockData *data)
 Set the priority of various protocols.
 
static int tls_negotiate (struct Connection *conn)
 Negotiate TLS connection.
 
static int tls_socket_poll (struct Connection *conn, time_t wait_secs)
 Check if any data is waiting on a socket - Implements Connection::poll() -.
 
static int tls_socket_close (struct Connection *conn)
 Close a TLS socket - Implements Connection::close() -.
 
static int tls_socket_open (struct Connection *conn)
 Open a TLS socket - Implements Connection::open() -.
 
static int tls_socket_read (struct Connection *conn, char *buf, size_t count)
 Read data from a TLS socket - Implements Connection::read() -.
 
static int tls_socket_write (struct Connection *conn, const char *buf, size_t count)
 Write data to a TLS socket - Implements Connection::write() -.
 
static int tls_starttls_close (struct Connection *conn)
 Close a TLS connection - Implements Connection::close() -.
 
int mutt_ssl_socket_setup (struct Connection *conn)
 Set up SSL socket mulitplexor.
 
int mutt_ssl_starttls (struct Connection *conn)
 Negotiate TLS over an already opened connection.
 

Variables

static int ProtocolPriority [] = { GNUTLS_TLS1_2, GNUTLS_TLS1_1, GNUTLS_TLS1, GNUTLS_SSL3, 0 }
 This array needs to be large enough to hold all the possible values support by NeoMutt.
 

Detailed Description

Handling of GnuTLS encryption.

Authors
  • Richard Russon
  • Pietro Cerutti

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 gnutls.c.

Macro Definition Documentation

◆ CERTERR_VALID

#define CERTERR_VALID   0

Definition at line 53 of file gnutls.c.

◆ CERTERR_EXPIRED

#define CERTERR_EXPIRED   (1 << 0)

Definition at line 54 of file gnutls.c.

◆ CERTERR_NOTYETVALID

#define CERTERR_NOTYETVALID   (1 << 1)

Definition at line 55 of file gnutls.c.

◆ CERTERR_REVOKED

#define CERTERR_REVOKED   (1 << 2)

Definition at line 56 of file gnutls.c.

◆ CERTERR_NOTTRUSTED

#define CERTERR_NOTTRUSTED   (1 << 3)

Definition at line 57 of file gnutls.c.

◆ CERTERR_HOSTNAME

#define CERTERR_HOSTNAME   (1 << 4)

Definition at line 58 of file gnutls.c.

◆ CERTERR_SIGNERNOTCA

#define CERTERR_SIGNERNOTCA   (1 << 5)

Definition at line 59 of file gnutls.c.

◆ CERTERR_INSECUREALG

#define CERTERR_INSECUREALG   (1 << 6)

Definition at line 60 of file gnutls.c.

◆ CERTERR_OTHER

#define CERTERR_OTHER   (1 << 7)

Definition at line 61 of file gnutls.c.

◆ CERT_SEP

#define CERT_SEP   "-----BEGIN"

Definition at line 64 of file gnutls.c.

Function Documentation

◆ gnutls_protocol_set_priority()

int gnutls_protocol_set_priority ( gnutls_session_t  session,
const int *  list 
)
+ Here is the caller graph for this function:

◆ tls_init()

static int tls_init ( void  )
static

Set up Gnu TLS.

Return values
0Success
-1Error

Definition at line 92 of file gnutls.c.

93{
94 static bool init_complete = false;
95 int err;
96
97 if (init_complete)
98 return 0;
99
100 err = gnutls_global_init();
101 if (err < 0)
102 {
103 mutt_error("gnutls_global_init: %s", gnutls_strerror(err));
104 return -1;
105 }
106
107 init_complete = true;
108 return 0;
109}
#define mutt_error(...)
Definition: logging2.h:92
+ Here is the caller graph for this function:

◆ tls_verify_peers()

static int tls_verify_peers ( gnutls_session_t  tlsstate,
gnutls_certificate_status_t *  certstat 
)
static

Wrapper for gnutls_certificate_verify_peers()

Parameters
tlsstateTLS state
certstatCertificate state, e.g. GNUTLS_CERT_INVALID
Return values
0Success If certstat was set. note: this does not mean success
>0Error

Wrapper with sanity-checking.

certstat is technically a bitwise-or of gnutls_certificate_status_t values.

Definition at line 122 of file gnutls.c.

123{
124 /* gnutls_certificate_verify_peers2() chains to
125 * gnutls_x509_trust_list_verify_crt2(). That function's documentation says:
126 *
127 * When a certificate chain of cert_list_size with more than one
128 * certificates is provided, the verification status will apply to
129 * the first certificate in the chain that failed
130 * verification. The verification process starts from the end of
131 * the chain(from CA to end certificate). The first certificate
132 * in the chain must be the end-certificate while the rest of the
133 * members may be sorted or not.
134 *
135 * This is why tls_check_certificate() loops from CA to host in that order,
136 * calling the menu, and recalling tls_verify_peers() for each approved
137 * cert in the chain.
138 */
139 int rc = gnutls_certificate_verify_peers2(tlsstate, certstat);
140
141 /* certstat was set */
142 if (rc == 0)
143 return 0;
144
145 if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND)
146 mutt_error(_("Unable to get certificate from peer"));
147 else
148 mutt_error(_("Certificate verification error (%s)"), gnutls_strerror(rc));
149
150 return rc;
151}
#define _(a)
Definition: message.h:28
+ Here is the caller graph for this function:

◆ tls_fingerprint()

static void tls_fingerprint ( gnutls_digest_algorithm_t  algo,
struct Buffer buf,
const gnutls_datum_t *  data 
)
static

Create a fingerprint of a TLS Certificate.

Parameters
algoFingerprint algorithm, e.g. GNUTLS_MAC_SHA256
bufBuffer for the fingerprint
dataCertificate

Definition at line 159 of file gnutls.c.

161{
162 unsigned char md[128] = { 0 };
163 size_t n = 64;
164
165 if (gnutls_fingerprint(algo, data, (char *) md, &n) < 0)
166 {
167 buf_strcpy(buf, _("[unable to calculate]"));
168 return;
169 }
170
171 for (size_t i = 0; i < n; i++)
172 {
173 buf_add_printf(buf, "%02X", md[i]);
174
175 // Put a space after a pair of bytes (except for the last one)
176 if (((i % 2) == 1) && (i < (n - 1)))
177 buf_addch(buf, ' ');
178 }
179}
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:204
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:241
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:395
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ tls_check_stored_hostname()

static bool tls_check_stored_hostname ( const gnutls_datum_t *  cert,
const char *  hostname 
)
static

Does the hostname match a stored certificate?

Parameters
certCertificate
hostnameHostname
Return values
trueHostname match found
falseError, or no match

Definition at line 188 of file gnutls.c.

189{
190 char *linestr = NULL;
191 size_t linestrsize = 0;
192
193 /* try checking against names stored in stored certs file */
194 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
195 FILE *fp = mutt_file_fopen(c_certificate_file, "r");
196 if (!fp)
197 return false;
198
199 struct Buffer *buf = buf_pool_get();
200
201 tls_fingerprint(GNUTLS_DIG_MD5, buf, cert);
202 while ((linestr = mutt_file_read_line(linestr, &linestrsize, fp, NULL, MUTT_RL_NO_FLAGS)))
203 {
204 regmatch_t *match = mutt_prex_capture(PREX_GNUTLS_CERT_HOST_HASH, linestr);
205 if (match)
206 {
207 regmatch_t *mhost = &match[PREX_GNUTLS_CERT_HOST_HASH_MATCH_HOST];
208 regmatch_t *mhash = &match[PREX_GNUTLS_CERT_HOST_HASH_MATCH_HASH];
209 linestr[mutt_regmatch_end(mhost)] = '\0';
210 linestr[mutt_regmatch_end(mhash)] = '\0';
211 if ((mutt_str_equal(linestr + mutt_regmatch_start(mhost), hostname)) &&
212 (mutt_str_equal(linestr + mutt_regmatch_start(mhash), buf_string(buf))))
213 {
214 FREE(&linestr);
215 mutt_file_fclose(&fp);
216 buf_pool_release(&buf);
217 return true;
218 }
219 }
220 }
221
222 mutt_file_fclose(&fp);
223 buf_pool_release(&buf);
224
225 /* not found a matching name */
226 return false;
227}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:168
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
Read a line from a file.
Definition: file.c:685
#define mutt_file_fclose(FP)
Definition: file.h:139
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:138
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:40
static void tls_fingerprint(gnutls_digest_algorithm_t algo, struct Buffer *buf, const gnutls_datum_t *data)
Create a fingerprint of a TLS Certificate.
Definition: gnutls.c:159
#define FREE(x)
Definition: memory.h:55
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:660
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
regmatch_t * mutt_prex_capture(enum Prex which, const char *str)
Match a precompiled regex against a string.
Definition: prex.c:298
@ PREX_GNUTLS_CERT_HOST_HASH
[#H foo.com A76D 954B EB79 1F49 5B3A 0A0E 0681 65B1]
Definition: prex.h:37
@ PREX_GNUTLS_CERT_HOST_HASH_MATCH_HASH
#H foo.com [A76D ... 65B1]
Definition: prex.h:112
@ PREX_GNUTLS_CERT_HOST_HASH_MATCH_HOST
#H [foo.com] A76D ... 65B1
Definition: prex.h:111
static regoff_t mutt_regmatch_end(const regmatch_t *match)
Return the end of a match.
Definition: regex3.h:67
static regoff_t mutt_regmatch_start(const regmatch_t *match)
Return the start of a match.
Definition: regex3.h:57
String manipulation buffer.
Definition: buffer.h:36
Container for Accounts, Notifications.
Definition: neomutt.h:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ tls_compare_certificates()

static int tls_compare_certificates ( const gnutls_datum_t *  peercert)
static

Compare certificates against $certificate_file

Parameters
peercertCertificate
Return values
1Certificate matches file
0Error, or no match

Definition at line 235 of file gnutls.c.

236{
237 gnutls_datum_t cert = { 0 };
238 unsigned char *ptr = NULL;
239 gnutls_datum_t b64_data = { 0 };
240 unsigned char *b64_data_data = NULL;
241 struct stat st = { 0 };
242
243 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
244 if (stat(c_certificate_file, &st) == -1)
245 return 0;
246
247 b64_data.size = st.st_size;
248 b64_data_data = MUTT_MEM_CALLOC(b64_data.size + 1, unsigned char);
249 b64_data.data = b64_data_data;
250
251 FILE *fp = mutt_file_fopen(c_certificate_file, "r");
252 if (!fp)
253 return 0;
254
255 b64_data.size = fread(b64_data.data, 1, b64_data.size, fp);
256 b64_data.data[b64_data.size] = '\0';
257 mutt_file_fclose(&fp);
258
259 do
260 {
261 const int rc = gnutls_pem_base64_decode_alloc(NULL, &b64_data, &cert);
262 if (rc != 0)
263 {
264 FREE(&b64_data_data);
265 return 0;
266 }
267
268 /* find start of cert, skipping junk */
269 ptr = (unsigned char *) strstr((char *) b64_data.data, CERT_SEP);
270 if (!ptr)
271 {
272 gnutls_free(cert.data);
273 FREE(&b64_data_data);
274 return 0;
275 }
276 /* find start of next cert */
277 ptr = (unsigned char *) strstr((char *) ptr + 1, CERT_SEP);
278
279 b64_data.size = b64_data.size - (ptr - b64_data.data);
280 b64_data.data = ptr;
281
282 if (cert.size == peercert->size)
283 {
284 if (memcmp(cert.data, peercert->data, cert.size) == 0)
285 {
286 /* match found */
287 gnutls_free(cert.data);
288 FREE(&b64_data_data);
289 return 1;
290 }
291 }
292
293 gnutls_free(cert.data);
294 } while (ptr);
295
296 /* no match found */
297 FREE(&b64_data_data);
298 return 0;
299}
#define CERT_SEP
Definition: gnutls.c:64
#define MUTT_MEM_CALLOC(n, type)
Definition: memory.h:40
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ tls_check_preauth()

static int tls_check_preauth ( const gnutls_datum_t *  certdata,
gnutls_certificate_status_t  certstat,
const char *  hostname,
int  chainidx,
int *  certerr,
int *  savedcert 
)
static

Prepare a certificate for authentication.

Parameters
[in]certdataList of GnuTLS certificates
[in]certstatGnuTLS certificate status
[in]hostnameHostname
[in]chainidxIndex in the certificate chain
[out]certerrResult, e.g. CERTERR_VALID
[out]savedcert1 if certificate has been saved
Return values
0Success
-1Error

Definition at line 312 of file gnutls.c.

315{
316 gnutls_x509_crt_t cert;
317
318 *certerr = CERTERR_VALID;
319 *savedcert = 0;
320
321 if (gnutls_x509_crt_init(&cert) < 0)
322 {
323 mutt_error(_("Error initialising gnutls certificate data"));
324 return -1;
325 }
326
327 if (gnutls_x509_crt_import(cert, certdata, GNUTLS_X509_FMT_DER) < 0)
328 {
329 mutt_error(_("Error processing certificate data"));
330 gnutls_x509_crt_deinit(cert);
331 return -1;
332 }
333
334 /* Note: tls_negotiate() contains a call to
335 * gnutls_certificate_set_verify_flags() with a flag disabling
336 * GnuTLS checking of the dates. So certstat shouldn't have the
337 * GNUTLS_CERT_EXPIRED and GNUTLS_CERT_NOT_ACTIVATED bits set. */
338 const bool c_ssl_verify_dates = cs_subset_bool(NeoMutt->sub, "ssl_verify_dates");
339 if (c_ssl_verify_dates != MUTT_NO)
340 {
341 if (gnutls_x509_crt_get_expiration_time(cert) < mutt_date_now())
342 *certerr |= CERTERR_EXPIRED;
343 if (gnutls_x509_crt_get_activation_time(cert) > mutt_date_now())
344 *certerr |= CERTERR_NOTYETVALID;
345 }
346
347 const bool c_ssl_verify_host = cs_subset_bool(NeoMutt->sub, "ssl_verify_host");
348 if ((chainidx == 0) && (c_ssl_verify_host != MUTT_NO) &&
349 !gnutls_x509_crt_check_hostname(cert, hostname) &&
350 !tls_check_stored_hostname(certdata, hostname))
351 {
352 *certerr |= CERTERR_HOSTNAME;
353 }
354
355 if (certstat & GNUTLS_CERT_REVOKED)
356 {
357 *certerr |= CERTERR_REVOKED;
358 certstat ^= GNUTLS_CERT_REVOKED;
359 }
360
361 /* see whether certificate is in our cache (certificates file) */
362 if (tls_compare_certificates(certdata))
363 {
364 *savedcert = 1;
365
366 /* We check above for certs with bad dates or that are revoked.
367 * These must be accepted manually each time. Otherwise, we
368 * accept saved certificates as valid. */
369 if (*certerr == CERTERR_VALID)
370 {
371 gnutls_x509_crt_deinit(cert);
372 return 0;
373 }
374 }
375
376 if (certstat & GNUTLS_CERT_INVALID)
377 {
378 *certerr |= CERTERR_NOTTRUSTED;
379 certstat ^= GNUTLS_CERT_INVALID;
380 }
381
382 if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)
383 {
384 /* NB: already cleared if cert in cache */
385 *certerr |= CERTERR_NOTTRUSTED;
386 certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
387 }
388
389 if (certstat & GNUTLS_CERT_SIGNER_NOT_CA)
390 {
391 /* NB: already cleared if cert in cache */
392 *certerr |= CERTERR_SIGNERNOTCA;
393 certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
394 }
395
396 if (certstat & GNUTLS_CERT_INSECURE_ALGORITHM)
397 {
398 /* NB: already cleared if cert in cache */
399 *certerr |= CERTERR_INSECUREALG;
400 certstat ^= GNUTLS_CERT_INSECURE_ALGORITHM;
401 }
402
403 /* we've been zeroing the interesting bits in certstat -
404 * don't return OK if there are any unhandled bits we don't
405 * understand */
406 if (certstat != 0)
407 *certerr |= CERTERR_OTHER;
408
409 gnutls_x509_crt_deinit(cert);
410
411 if (*certerr == CERTERR_VALID)
412 return 0;
413
414 return -1;
415}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:47
#define CERTERR_INSECUREALG
Definition: gnutls.c:60
#define CERTERR_VALID
Definition: gnutls.c:53
#define CERTERR_HOSTNAME
Definition: gnutls.c:58
#define CERTERR_EXPIRED
Definition: gnutls.c:54
static bool tls_check_stored_hostname(const gnutls_datum_t *cert, const char *hostname)
Does the hostname match a stored certificate?
Definition: gnutls.c:188
#define CERTERR_NOTTRUSTED
Definition: gnutls.c:57
#define CERTERR_NOTYETVALID
Definition: gnutls.c:55
static int tls_compare_certificates(const gnutls_datum_t *peercert)
Compare certificates against $certificate_file
Definition: gnutls.c:235
#define CERTERR_OTHER
Definition: gnutls.c:61
#define CERTERR_SIGNERNOTCA
Definition: gnutls.c:59
#define CERTERR_REVOKED
Definition: gnutls.c:56
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:456
@ 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:

◆ add_cert()

static void add_cert ( const char *  title,
gnutls_x509_crt_t  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 424 of file gnutls.c.

426{
427 static const char *part[] = {
428 GNUTLS_OID_X520_COMMON_NAME, // CN
429 GNUTLS_OID_PKCS9_EMAIL, // Email
430 GNUTLS_OID_X520_ORGANIZATION_NAME, // O
431 GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, // OU
432 GNUTLS_OID_X520_LOCALITY_NAME, // L
433 GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, // ST
434 GNUTLS_OID_X520_COUNTRY_NAME, // C
435 };
436
437 char buf[128] = { 0 };
438 int rc;
439
440 // Allocate formatted strings and let the array take ownership
441 ARRAY_ADD(carr, mutt_str_dup(title));
442
443 for (size_t i = 0; i < mutt_array_size(part); i++)
444 {
445 size_t buflen = sizeof(buf);
446 if (issuer)
447 rc = gnutls_x509_crt_get_issuer_dn_by_oid(cert, part[i], 0, 0, buf, &buflen);
448 else
449 rc = gnutls_x509_crt_get_dn_by_oid(cert, part[i], 0, 0, buf, &buflen);
450 if (rc != 0)
451 continue;
452
453 char *line = NULL;
454 mutt_str_asprintf(&line, " %s", buf);
455 ARRAY_ADD(carr, line);
456 }
457}
#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
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:803
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ tls_check_one_certificate()

static int tls_check_one_certificate ( const gnutls_datum_t *  certdata,
gnutls_certificate_status_t  certstat,
const char *  hostname,
int  idx,
size_t  len 
)
static

Check a GnuTLS certificate.

Parameters
certdataList of GnuTLS certificates
certstatGnuTLS certificate status
hostnameHostname
idxIndex into certificate list
lenLength of certificate list
Return values
1Success
0Failure

Definition at line 469 of file gnutls.c.

472{
473 struct CertArray carr = ARRAY_HEAD_INITIALIZER;
474 int certerr, savedcert;
475 gnutls_x509_crt_t cert;
476 struct Buffer *fpbuf = NULL;
477 time_t t;
478 char datestr[30] = { 0 };
479 char title[256] = { 0 };
480 gnutls_datum_t pemdata = { 0 };
481
482 if (tls_check_preauth(certdata, certstat, hostname, idx, &certerr, &savedcert) == 0)
483 return 1;
484
485 if (OptNoCurses)
486 {
487 mutt_debug(LL_DEBUG1, "unable to prompt for certificate in batch mode\n");
488 mutt_error(_("Untrusted server certificate"));
489 return 0;
490 }
491
492 /* interactive check from user */
493 if (gnutls_x509_crt_init(&cert) < 0)
494 {
495 mutt_error(_("Error initialising gnutls certificate data"));
496 return 0;
497 }
498
499 if (gnutls_x509_crt_import(cert, certdata, GNUTLS_X509_FMT_DER) < 0)
500 {
501 mutt_error(_("Error processing certificate data"));
502 gnutls_x509_crt_deinit(cert);
503 return 0;
504 }
505
506 add_cert(_("This certificate belongs to:"), cert, false, &carr);
507 ARRAY_ADD(&carr, NULL);
508 add_cert(_("This certificate was issued by:"), cert, true, &carr);
509
510 ARRAY_ADD(&carr, NULL);
511 ARRAY_ADD(&carr, mutt_str_dup(_("This certificate is valid")));
512
513 char *line = NULL;
514 t = gnutls_x509_crt_get_activation_time(cert);
515 mutt_date_make_tls(datestr, sizeof(datestr), t);
516 mutt_str_asprintf(&line, _(" from %s"), datestr);
517 ARRAY_ADD(&carr, line);
518
519 t = gnutls_x509_crt_get_expiration_time(cert);
520 mutt_date_make_tls(datestr, sizeof(datestr), t);
521 mutt_str_asprintf(&line, _(" to %s"), datestr);
522 ARRAY_ADD(&carr, line);
523 ARRAY_ADD(&carr, NULL);
524
525 fpbuf = buf_pool_get();
526 tls_fingerprint(GNUTLS_DIG_SHA, fpbuf, certdata);
527 mutt_str_asprintf(&line, _("SHA1 Fingerprint: %s"), buf_string(fpbuf));
528 ARRAY_ADD(&carr, line);
529
530 buf_reset(fpbuf);
531 tls_fingerprint(GNUTLS_DIG_SHA256, fpbuf, certdata);
532 fpbuf->data[39] = '\0'; // Divide into two lines of output
533 mutt_str_asprintf(&line, "%s%s", _("SHA256 Fingerprint: "), buf_string(fpbuf));
534 ARRAY_ADD(&carr, line);
535 mutt_str_asprintf(&line, "%*s%s", (int) mutt_str_len(_("SHA256 Fingerprint: ")),
536 "", fpbuf->data + 40);
537 ARRAY_ADD(&carr, line);
538
539 if (certerr)
540 ARRAY_ADD(&carr, NULL);
541
542 if (certerr & CERTERR_NOTYETVALID)
543 {
544 ARRAY_ADD(&carr, mutt_str_dup(_("WARNING: Server certificate is not yet valid")));
545 }
546 if (certerr & CERTERR_EXPIRED)
547 {
548 ARRAY_ADD(&carr, mutt_str_dup(_("WARNING: Server certificate has expired")));
549 }
550 if (certerr & CERTERR_REVOKED)
551 {
552 ARRAY_ADD(&carr, mutt_str_dup(_("WARNING: Server certificate has been revoked")));
553 }
554 if (certerr & CERTERR_HOSTNAME)
555 {
556 ARRAY_ADD(&carr, mutt_str_dup(_("WARNING: Server hostname does not match certificate")));
557 }
558 if (certerr & CERTERR_SIGNERNOTCA)
559 {
560 ARRAY_ADD(&carr, mutt_str_dup(_("WARNING: Signer of server certificate is not a CA")));
561 }
562 if (certerr & CERTERR_INSECUREALG)
563 {
564 ARRAY_ADD(&carr, mutt_str_dup(_("Warning: Server certificate was signed using an insecure algorithm")));
565 }
566
567 snprintf(title, sizeof(title),
568 _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
569
570 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
571 const bool allow_always = (c_certificate_file && !savedcert &&
573 int rc = dlg_certificate(title, &carr, allow_always, false);
574 if (rc == 3) // Accept always
575 {
576 bool saved = false;
577 FILE *fp = mutt_file_fopen(c_certificate_file, "a");
578 if (fp)
579 {
580 if (certerr & CERTERR_HOSTNAME) // Save hostname if necessary
581 {
582 buf_reset(fpbuf);
583 tls_fingerprint(GNUTLS_DIG_MD5, fpbuf, certdata);
584 fprintf(fp, "#H %s %s\n", hostname, buf_string(fpbuf));
585 saved = true;
586 }
587 if (certerr ^ CERTERR_HOSTNAME) // Save the cert for all other errors
588 {
589 int rc2 = gnutls_pem_base64_encode_alloc("CERTIFICATE", certdata, &pemdata);
590 if (rc2 == 0)
591 {
592 if (fwrite(pemdata.data, pemdata.size, 1, fp) == 1)
593 {
594 saved = true;
595 }
596 gnutls_free(pemdata.data);
597 }
598 }
599 mutt_file_fclose(&fp);
600 }
601 if (saved)
602 mutt_message(_("Certificate saved"));
603 else
604 mutt_error(_("Warning: Couldn't save certificate"));
605 }
606
607 buf_pool_release(&fpbuf);
608 cert_array_clear(&carr);
609 ARRAY_FREE(&carr);
610 gnutls_x509_crt_deinit(cert);
611 return (rc > 1);
612}
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:204
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:58
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:76
void cert_array_clear(struct CertArray *carr)
Free all memory of a CertArray.
bool OptNoCurses
(pseudo) when sending in batch mode
Definition: globals.c:69
static int tls_check_preauth(const gnutls_datum_t *certdata, gnutls_certificate_status_t certstat, const char *hostname, int chainidx, int *certerr, int *savedcert)
Prepare a certificate for authentication.
Definition: gnutls.c:312
static void add_cert(const char *title, gnutls_x509_crt_t cert, bool issuer, struct CertArray *carr)
Look up certificate info and save it to a list.
Definition: gnutls.c:424
int dlg_certificate(const char *title, struct CertArray *carr, bool allow_always, bool allow_skip)
Ask the user to validate the certificate -.
#define mutt_message(...)
Definition: logging2.h:91
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
int mutt_date_make_tls(char *buf, size_t buflen, time_t timestamp)
Format date in TLS certificate verification style.
Definition: date.c:837
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:496
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:

◆ tls_check_certificate()

static int tls_check_certificate ( struct Connection conn)
static

Check a connection's certificate.

Parameters
connConnection to a server
Return values
1Certificate is valid
0Error, or certificate is invalid

Definition at line 620 of file gnutls.c.

621{
622 struct TlsSockData *data = conn->sockdata;
623 gnutls_session_t session = data->session;
624 const gnutls_datum_t *cert_list = NULL;
625 unsigned int cert_list_size = 0;
626 gnutls_certificate_status_t certstat;
627 int certerr, savedcert, rc = 0;
628 int max_preauth_pass = -1;
629
630 /* tls_verify_peers() calls gnutls_certificate_verify_peers2(),
631 * which verifies the auth_type is GNUTLS_CRD_CERTIFICATE
632 * and that get_certificate_type() for the server is GNUTLS_CRT_X509.
633 * If it returns 0, certstat will be set with failure codes for the first
634 * cert in the chain(from CA to host) with an error.
635 */
636 if (tls_verify_peers(session, &certstat) != 0)
637 return 0;
638
639 cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
640 if (!cert_list || (cert_list_size == 0))
641 {
642 mutt_error(_("Unable to get certificate from peer"));
643 return 0;
644 }
645
646 /* tls_verify_peers doesn't check hostname or expiration, so walk
647 * from most specific to least checking these. If we see a saved certificate,
648 * its status short-circuits the remaining checks. */
649 int preauthrc = 0;
650 for (int i = 0; i < cert_list_size; i++)
651 {
652 rc = tls_check_preauth(&cert_list[i], certstat, conn->account.host, i,
653 &certerr, &savedcert);
654 preauthrc += rc;
655 if (!preauthrc)
656 max_preauth_pass = i;
657
658 if (savedcert)
659 {
660 if (preauthrc == 0)
661 return 1;
662 break;
663 }
664 }
665
666 /* then check interactively, starting from chain root */
667 for (int i = cert_list_size - 1; i >= 0; i--)
668 {
669 rc = tls_check_one_certificate(&cert_list[i], certstat, conn->account.host,
670 i, cert_list_size);
671
672 /* Stop checking if the menu cert is aborted or rejected. */
673 if (rc == 0)
674 break;
675
676 /* add signers to trust set, then reverify */
677 if (i)
678 {
679 int rcsettrust = gnutls_certificate_set_x509_trust_mem(data->xcred, &cert_list[i],
680 GNUTLS_X509_FMT_DER);
681 if (rcsettrust != 1)
682 mutt_debug(LL_DEBUG1, "error trusting certificate %d: %d\n", i, rcsettrust);
683
684 if (tls_verify_peers(session, &certstat) != 0)
685 return 0;
686
687 /* If the cert chain now verifies, and all lower certs already
688 * passed preauth, we are done. */
689 if (!certstat && (max_preauth_pass >= (i - 1)))
690 return 1;
691 }
692 }
693
694 return rc;
695}
static int tls_verify_peers(gnutls_session_t tlsstate, gnutls_certificate_status_t *certstat)
Wrapper for gnutls_certificate_verify_peers()
Definition: gnutls.c:122
static int tls_check_one_certificate(const gnutls_datum_t *certdata, gnutls_certificate_status_t certstat, const char *hostname, int idx, size_t len)
Check a GnuTLS certificate.
Definition: gnutls.c:469
char host[128]
Server to login to.
Definition: connaccount.h:54
void * sockdata
Backend-specific socket data.
Definition: connection.h:55
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:49
TLS socket data -.
Definition: gnutls.c:82
gnutls_certificate_credentials_t xcred
Definition: gnutls.c:84
gnutls_session_t session
Definition: gnutls.c:83
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ tls_get_client_cert()

static void tls_get_client_cert ( struct Connection conn)
static

Get the client certificate for a TLS connection.

Parameters
connConnection to a server
Note
This function grabs the CN out of the client cert but appears to do nothing with it.

Definition at line 704 of file gnutls.c.

705{
706 struct TlsSockData *data = conn->sockdata;
707 gnutls_x509_crt_t clientcrt;
708 char *cn = NULL;
709 size_t cnlen = 0;
710 int rc;
711
712 /* get our cert CN if we have one */
713 const gnutls_datum_t *crtdata = gnutls_certificate_get_ours(data->session);
714 if (!crtdata)
715 return;
716
717 if (gnutls_x509_crt_init(&clientcrt) < 0)
718 {
719 mutt_debug(LL_DEBUG1, "Failed to init gnutls crt\n");
720 return;
721 }
722
723 if (gnutls_x509_crt_import(clientcrt, crtdata, GNUTLS_X509_FMT_DER) < 0)
724 {
725 mutt_debug(LL_DEBUG1, "Failed to import gnutls client crt\n");
726 goto err;
727 }
728
729 /* get length of CN, then grab it. */
730 rc = gnutls_x509_crt_get_dn_by_oid(clientcrt, GNUTLS_OID_X520_COMMON_NAME, 0,
731 0, NULL, &cnlen);
732 if (((rc >= 0) || (rc == GNUTLS_E_SHORT_MEMORY_BUFFER)) && (cnlen > 0))
733 {
734 cn = MUTT_MEM_CALLOC(cnlen, char);
735 if (gnutls_x509_crt_get_dn_by_oid(clientcrt, GNUTLS_OID_X520_COMMON_NAME, 0,
736 0, cn, &cnlen) < 0)
737 {
738 goto err;
739 }
740 mutt_debug(LL_DEBUG2, "client certificate CN: %s\n", cn);
741 }
742
743err:
744 FREE(&cn);
745 gnutls_x509_crt_deinit(clientcrt);
746}
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
+ Here is the caller graph for this function:

◆ tls_set_priority()

static int tls_set_priority ( struct TlsSockData data)
static

Set the priority of various protocols.

Parameters
dataTLS socket data
Return values
0Success
-1Error

Definition at line 827 of file gnutls.c.

828{
829 size_t nproto = 0; /* number of tls/ssl protocols */
830
831 const bool c_ssl_use_tlsv1_2 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_2");
832 if (c_ssl_use_tlsv1_2)
833 ProtocolPriority[nproto++] = GNUTLS_TLS1_2;
834 const bool c_ssl_use_tlsv1_1 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1_1");
835 if (c_ssl_use_tlsv1_1)
836 ProtocolPriority[nproto++] = GNUTLS_TLS1_1;
837 const bool c_ssl_use_tlsv1 = cs_subset_bool(NeoMutt->sub, "ssl_use_tlsv1");
838 if (c_ssl_use_tlsv1)
839 ProtocolPriority[nproto++] = GNUTLS_TLS1;
840 const bool c_ssl_use_sslv3 = cs_subset_bool(NeoMutt->sub, "ssl_use_sslv3");
841 if (c_ssl_use_sslv3)
842 ProtocolPriority[nproto++] = GNUTLS_SSL3;
843 ProtocolPriority[nproto] = 0;
844
845 if (nproto == 0)
846 {
847 mutt_error(_("All available protocols for TLS/SSL connection disabled"));
848 return -1;
849 }
850
851 const char *const c_ssl_ciphers = cs_subset_string(NeoMutt->sub, "ssl_ciphers");
852 if (c_ssl_ciphers)
853 {
854 mutt_error(_("Explicit ciphersuite selection via $ssl_ciphers not supported"));
855 }
856
857 /* We use default priorities (see gnutls documentation),
858 * except for protocol version */
859 gnutls_set_default_priority(data->session);
861 return 0;
862}
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:291
static int ProtocolPriority[]
This array needs to be large enough to hold all the possible values support by NeoMutt.
Definition: gnutls.c:75
int gnutls_protocol_set_priority(gnutls_session_t session, const int *list)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ tls_negotiate()

static int tls_negotiate ( struct Connection conn)
static

Negotiate TLS connection.

Parameters
connConnection to a server
Return values
0Success
-1Error

After TLS session has been initialized, attempt to negotiate TLS over the wire, including certificate checks.

Definition at line 874 of file gnutls.c.

875{
876 struct TlsSockData *data = MUTT_MEM_CALLOC(1, struct TlsSockData);
877 conn->sockdata = data;
878 int err = gnutls_certificate_allocate_credentials(&data->xcred);
879 if (err < 0)
880 {
881 FREE(&conn->sockdata);
882 mutt_error("gnutls_certificate_allocate_credentials: %s", gnutls_strerror(err));
883 return -1;
884 }
885
886 const char *const c_certificate_file = cs_subset_path(NeoMutt->sub, "certificate_file");
887 gnutls_certificate_set_x509_trust_file(data->xcred, c_certificate_file, GNUTLS_X509_FMT_PEM);
888 /* ignore errors, maybe file doesn't exist yet */
889
890 const char *const c_ssl_ca_certificates_file = cs_subset_path(NeoMutt->sub, "ssl_ca_certificates_file");
891 if (c_ssl_ca_certificates_file)
892 {
893 gnutls_certificate_set_x509_trust_file(data->xcred, c_ssl_ca_certificates_file,
894 GNUTLS_X509_FMT_PEM);
895 }
896
897 const char *const c_ssl_client_cert = cs_subset_path(NeoMutt->sub, "ssl_client_cert");
898 if (c_ssl_client_cert)
899 {
900 mutt_debug(LL_DEBUG2, "Using client certificate %s\n", c_ssl_client_cert);
901 gnutls_certificate_set_x509_key_file(data->xcred, c_ssl_client_cert,
902 c_ssl_client_cert, GNUTLS_X509_FMT_PEM);
903 }
904
905#ifdef HAVE_DECL_GNUTLS_VERIFY_DISABLE_TIME_CHECKS
906 /* disable checking certificate activation/expiration times
907 * in gnutls, we do the checks ourselves */
908 gnutls_certificate_set_verify_flags(data->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
909#endif
910
911 err = gnutls_init(&data->session, GNUTLS_CLIENT);
912 if (err)
913 {
914 mutt_error("gnutls_init: %s", gnutls_strerror(err));
915 goto fail;
916 }
917
918 /* set socket */
919 gnutls_transport_set_ptr(data->session, (gnutls_transport_ptr_t) (long) conn->fd);
920
921 if (gnutls_server_name_set(data->session, GNUTLS_NAME_DNS, conn->account.host,
922 mutt_str_len(conn->account.host)))
923 {
924 mutt_error(_("Warning: unable to set TLS SNI host name"));
925 }
926
927 if (tls_set_priority(data) < 0)
928 {
929 goto fail;
930 }
931
932 const short c_ssl_min_dh_prime_bits = cs_subset_number(NeoMutt->sub, "ssl_min_dh_prime_bits");
933 if (c_ssl_min_dh_prime_bits > 0)
934 {
935 gnutls_dh_set_prime_bits(data->session, c_ssl_min_dh_prime_bits);
936 }
937
938 gnutls_credentials_set(data->session, GNUTLS_CRD_CERTIFICATE, data->xcred);
939
940 do
941 {
942 err = gnutls_handshake(data->session);
943 } while ((err == GNUTLS_E_AGAIN) || (err == GNUTLS_E_INTERRUPTED));
944
945 if (err < 0)
946 {
947 if (err == GNUTLS_E_FATAL_ALERT_RECEIVED)
948 {
949 mutt_error("gnutls_handshake: %s(%s)", gnutls_strerror(err),
950 gnutls_alert_get_name(gnutls_alert_get(data->session)));
951 }
952 else
953 {
954 mutt_error("gnutls_handshake: %s", gnutls_strerror(err));
955 }
956 goto fail;
957 }
958
959 if (tls_check_certificate(conn) == 0)
960 goto fail;
961
962 /* set Security Strength Factor (SSF) for SASL */
963 /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
964 conn->ssf = gnutls_cipher_get_key_size(gnutls_cipher_get(data->session)) * 8;
965
967
968 if (!OptNoCurses)
969 {
970 mutt_message(_("SSL/TLS connection using %s (%s/%s/%s)"),
971 gnutls_protocol_get_name(gnutls_protocol_get_version(data->session)),
972 gnutls_kx_get_name(gnutls_kx_get(data->session)),
973 gnutls_cipher_get_name(gnutls_cipher_get(data->session)),
974 gnutls_mac_get_name(gnutls_mac_get(data->session)));
975 mutt_sleep(0);
976 }
977
978 return 0;
979
980fail:
981 gnutls_certificate_free_credentials(data->xcred);
982 gnutls_deinit(data->session);
983 FREE(&conn->sockdata);
984 return -1;
985}
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:143
static int tls_check_certificate(struct Connection *conn)
Check a connection's certificate.
Definition: gnutls.c:620
static void tls_get_client_cert(struct Connection *conn)
Get the client certificate for a TLS connection.
Definition: gnutls.c:704
static int tls_set_priority(struct TlsSockData *data)
Set the priority of various protocols.
Definition: gnutls.c:827
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:842
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_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 1127 of file gnutls.c.

1128{
1129 if (tls_init() < 0)
1130 return -1;
1131
1132 conn->open = tls_socket_open;
1133 conn->read = tls_socket_read;
1134 conn->write = tls_socket_write;
1135 conn->close = tls_socket_close;
1136 conn->poll = tls_socket_poll;
1137
1138 return 0;
1139}
static int tls_init(void)
Set up Gnu TLS.
Definition: gnutls.c:92
static int tls_socket_close(struct Connection *conn)
Close a TLS socket - Implements Connection::close() -.
Definition: gnutls.c:1005
static int tls_socket_open(struct Connection *conn)
Open a TLS socket - Implements Connection::open() -.
Definition: gnutls.c:1030
static int tls_socket_poll(struct Connection *conn, time_t wait_secs)
Check if any data is waiting on a socket - Implements Connection::poll() -.
Definition: gnutls.c:990
static int tls_socket_read(struct Connection *conn, char *buf, size_t count)
Read data from a TLS socket - Implements Connection::read() -.
Definition: gnutls.c:1047
static int tls_socket_write(struct Connection *conn, const char *buf, size_t count)
Write data to a TLS socket - Implements Connection::write() -.
Definition: gnutls.c:1074
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(* open)(struct Connection *conn)
Definition: connection.h:66
int(* read)(struct Connection *conn, char *buf, size_t count)
Definition: connection.h:79
+ 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 1147 of file gnutls.c.

1148{
1149 if (tls_init() < 0)
1150 return -1;
1151
1152 if (tls_negotiate(conn) < 0)
1153 return -1;
1154
1155 conn->read = tls_socket_read;
1156 conn->write = tls_socket_write;
1157 conn->close = tls_starttls_close;
1158 conn->poll = tls_socket_poll;
1159
1160 return 0;
1161}
static int tls_negotiate(struct Connection *conn)
Negotiate TLS connection.
Definition: gnutls.c:874
static int tls_starttls_close(struct Connection *conn)
Close a TLS connection - Implements Connection::close() -.
Definition: gnutls.c:1108
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ ProtocolPriority

int ProtocolPriority[] = { GNUTLS_TLS1_2, GNUTLS_TLS1_1, GNUTLS_TLS1, GNUTLS_SSL3, 0 }
static

This array needs to be large enough to hold all the possible values support by NeoMutt.

The initialized values are just placeholders–the array gets overwrriten in tls_negotiate() depending on the $ssl_use_* options.

Note: gnutls_protocol_set_priority() was removed in GnuTLS version 3.4 (2015-04). TLS 1.3 support wasn't added until version 3.6.5. Therefore, no attempt is made to support $ssl_use_tlsv1_3 in this code.

Definition at line 75 of file gnutls.c.